REST Module
The REST module compiles and validates the REST API. The order of the REST deployment module is 0, as it doesn't depend on any artifacts exported by any other module.
REST Model
We start by defining a model for the REST API. A REST API is comprised of a set of resources on which a constrained set of operations can act. Borrowing terms from english grammar, Enunciate identifies each REST resource as a noun (with an associated noun context) and the REST operations as verbs. Because the REST API is to be deployed using HTTP, Enunciate constrains the set of verbs to the set {create, read, update, delete}, mapping to the HTTP verbs {PUT, GET, POST, DELETE}, respectively.
While a REST endpoint must have a noun and a verb, it can optionally use other constructs to more clearly define itself.
Adjectives
REST adjectives are used to qualify a REST noun or a REST verb. For a REST invocation, an adjective has a name and one or more values. In terms of HTTP, adjectives are passed as HTTP parameters.
For example, if we were to invoke the verb "read" on a noun "circle", but we wanted to describe the color of the circle as "red", then "color" would be the adjective and "red" would be the adjective value. And mapped to HTTP, the HTTP request would look something like this:
GET /rest/circle?color=red
Proper Noun
A proper noun is used to identify a specific noun. In practical terms, a proper noun usually takes the form of an id, and the only difference between a proper noun and an adjective is that the proper noun is supplied directly on the URL, as opposed to being supplied as a query parameter.
For example, if we wanted to invoke the verb "read" on a noun "purchase-order", but identify the specific purchase order by the id "12345", the "12345" could be a proper noun. (Note that it could also be an adjective, the only difference is that a proper noun doesn't need a name, only a value.)
And an HTTP request might like like this:
GET /rest/purchase-order/12345
Noun Value
In REST, a noun value often needs to be supplied, such as during a "create" or an "update". For example, If we were to invoke the verb "update" on the noun "shape" to be "red circle", "red circle" would be the noun value. In terms of HTTP, the noun value is the payload of the request, and the request would look something like this:
POST /rest/shape
<circle color="red"/>
Noun Context
A noun can be qualified by a noun context. The noun context can be thought of as a "grouping" of nouns. Perhaps, as an admittedly contrived example, we were to have two separate resources for the noun "rectangle", say "wide" and "tall". The "rectangle" to which those two contexts could be applied qualifies two different "rectangle" nouns.
Noun Context Parameters
A noun context parameter (or just "context parameter" for short) is a parameter that is defined by the noun context. For example, if we wanted to identify a specific user of a specific group, we could identify the "group id" as a context parameter, the user as the noun, and the user id as the proper noun.
Content Types
Each REST noun is represented by a set of content types (i.e. MIME types). By default, Enunciate represents each noun with both the "application/xml" content type and "application/json" content type (corresponding to XML and JSON representations of the noun). You can also apply other content types to each noun (and optionally disable the default content types) using the org.codehaus.enunciate.rest.annotations.ContentType annotation. This annotation can be applied at the method level, type level, and package level (in that priority order).
Associated with each content type is (1) a "content type id" and (2) a "content type handler". By default, the content type id is the "subtype" of the content type. For example, the default id of "application/xml" is "xml", the default id of "application/json" is "json", and the default id for "application/atom+xml" is "atom+xml". The content type id is used to identify the content type for a resource on the URL. For example, the content type with content type id "xml" for a resource "circle" will be accessed at the relative URL "/xml/circle".
Content type ids can be cofigured with the enunciate configuration file. Because content type ids relate to the location of endpoints, they are considered application-level configuration (not module-level configuration). The configuration for content type ids occurs in the "services" element of the main Enunciate configuration file. Here is an example:
<enunciate>
<services>
<rest>
<content-types>
<content-type type="..." id="..."/>
<content-type type="..." id="..."/>
...
</content-types>
</rest>
</services>
</enunciate>
Content type handlers contain the logic for marshalling a Java object to/from a given content type. There must be one (and only one) content type handler for each content type. By default, Enunciate provides content type handlers for XML ("application/xml" and "text/xml") and JSON ("application/json"). You can specify a content type handler by annotating the class declaration with @org.codehaus.enunciate.rest.annotations.ContentTypeHandler, which you can use to identify the applicable content types. You can also specify a content type handler using rest module configuration (see below). If no content type handler for a content type is specified via type declaration nor by configuration, a configurable (see below) default content type handler will be used. Without configuration, the default content handler will be org.codehaus.enunciate.modules.rest.xml.JaxbXmlContentHandler (the XML content handler).
Content type handlers supported by the module must implement org.codehaus.enunciate.modules.rest.RESTRequestContentTypeHandler and have an accessible no-arg constructor.
Constraints
Enunciate uses J2EE and JAXB 2.0 to map a REST model onto HTTP. In order to do that definitively, Enunciate imposes the following constraints:
- All verbs that act on the same noun must be unique. (E.g. there can't be two "read" methods for the same noun.)
- Proper nouns must not be of a complex XML type. Only simple types are allowed (e.g. integer, string, enum, etc.).
- There can only be one proper noun for a REST operation
- Adjectives must be simple types, but there can be more than one value for a single adjective.
- The verbs "read" and "delete" cannot support a noun value.
- A noun value must be either an xml root element (not just a complex type) or javax.activation.DataHandler.
- A return type must be either a root element or javax.activation.DataHandler.
- Noun context parameters must be simple types.
Mapping Java to a REST API
Java Types
The org.codehaus.enunciate.rest.annotations.RESTEndpoint annotation is used on a Java type (i.e. class or interface) to indicate that it contains methods that will service REST endpoints. This is used simply to indicate to the engine that the methods on the annotated class or interface should be searched for their nouns and verbs. Only if a method is annotated with org.codehaus.enunciate.rest.annotations.Verb will it service a REST endpoint (see below).
The @RESTEndpoint annotation on an interface means that the annotated interface defines the REST methods for any methods on an annotated class that directly implement it. In practical terms, classes annotated with @RESTEndpoint will use the metadata on any @RESTEnpoint interface instead of the metadata in their own methods. Allowing interfaces to define the REST API allows developers to leverage the advantages of coding to interfaces (e.g. introduction of aspects, multiple implementations, etc.).
Java Methods
Each Java method that is to serve as a REST endpoint must be assigned a verb and a noun. A public method can be assigned a verb with the org.codehaus.enunciate.rest.annotations.Verb annotation. A method that is not assigned a verb will not be considered to service a REST endpoint.
A method that is assigned a verb must be assigned a noun as well. The noun is specified with the org.codehaus.enunciate.rest.annotations.Noun annotation, which can supply both the name and the context of the noun. As a convenience, The org.codehaus.enunciate.rest.annotations.NounContext annotation can be supplied along with the @RESTEndpoint annotation at the level of the interface (or class) to specify the default context for all nouns that are defined by the methods of the interface (or class).
To identify a context parameter, specify the name of the context parameter in braces ("{" and "}") in the noun context. When context parameters are defined, Enunciate will look for a method parameter that is defined to be a context parameter with the same name. If there is no context parameter defined by that name, the context parameter will be silently ignored. See below for how to define a method parameter as a context parameter.
Java Return Types
By default, the return type of the Java method defines the payload of the response. In order support the default content type handlers (see "Content Types" above), the return type of the Java method must be an XML root element. The exception to this is that Enunciate also allows the return type of a Java method to be javax.activation.DataHandler, which defines its own payload and content type. In the case where the return type is a DataHandler, consider using the @ContentType (see "Content Types" above) annotation to specify which content types the DataHandler supports and which content types are not supported.
Java Method Parameters
A parameter to a method can be a proper noun, an adjective, a context parameter, or a noun value. By default, a parameter is mapped as an adjective. By default, the name of the adjective is the name of the parameter. Parameters can be customized with the org.codehaus.enunciate.rest.annotations.Adjective, org.codehaus.enunciate.rest.annotations.NounValue, org.codehaus.enunciate.rest.annotations.ContextParameter, and org.codehaus.enunciate.rest.annotations.ProperNoun annotations.
Complex Adjectives
There may be cases where a REST request may accept a large number of adjectives (HTTP parameters). If this is the case, the corresponding Java method could start to get unwieldy because of the number of parameters on the method. To address this inconvenience, Enunciate supports the concept of a "complex adjective". An adjective is marked as complex with by setting "complex=true" on the @Adjective annotation. The type of a complex adjective is expected to be a simple bean with an accessible no-arg constructor. In the case of a complex adjective, the properties of the bean define the names and types of the adjectives.
Noun Value Types
By default, a method parameter that is identified as a noun value is deserialized from the request body from the content type handler (see "Content Types" above). This means that the noun value parameter must be an XML root element to support the default content type handlers. Like the return type, there is an exception to this rule: a noun value parameter can be of type javax.activation.DataHandler or an array/collection of javax.activation.DataHandler. In the case of javax.activation.DataHandler, the request payload is considered to be of a "custom type" and the DataHandler will become a handler to the InputStream of the request payload. The DataSource of the DataHandler will be an instance of org.codehaus.enunciate.modules.rest.RESTRequestDataSource.
In the case of an array/collection of javax.activation.DataHandler, the REST method will be considered able to handle a multipart file upload as defined in RFC 1867. However, in order for a REST request to be able to be parsed as a multipart file upload, an instance of org.codehaus.enunciate.modules.rest.MultipartRequestHandler must be supplied. The default instance, org.codehaus.enunciate.modules.rest.DefaultMultipartRequestHandler, will quietly fail (i.e. the request won't be parsed into a multipart request) unless the necessary Commons-FileUpload libraries are found on the classpath.
In some cases, you may want to provide your own org.codehaus.enunciate.modules.rest.MultipartRequestHandler. Enunciate provides another implementation, the org.codehaus.enunciate.modules.rest.StreamingMultipartRequestHandler, that uses Commons-FileUpload to provide a "streaming" approach to parsing a multipart form upload. See the JavaDocs for details.
Exceptions
By default, an exception that gets thrown during a REST invocation will return an HTTP 500 error. This can be customized with the org.codehaus.enunciate.rest.annotations.RESTError annotation on the exception that gets thrown. The body of the error response can be customized by annotating an accessor method on the exception with org.codehaus.enunciate.rest.annotations.RESTErrorBody. The object that is returns from this method will be written to the response in the same manner that the return type of the method is serialized (see "Java Return Types" above).
JSON API
By default, each resource is available as both the "application/xml" content type and "application/json" content type. The default JSON content type handler supports three different serialization methods: "hierarchical", "xmlMapped", and "badgerfish". These values can be passed as request parameters to specify which format is desired. The serialization method to use when none is specified by a request parameter can be specified by using the "defaultJsonSerialization" attribute on the REST module configuration element. Note the default value is ("xmlMapped").
When the JSON serialization is done by converting the XML result to JSON, there are two mapping conventions that can be used. By default, the mapping is done using the "mapped" convention (JSON serialization method "xmlMapped"). The Badgerfish convention is also available (method "badgerfish"). To learn more about the difference between the two convensions, see the Jettison user's guide.
The "hierarchical" serialization method serializes the result of the operation using XStream's JSON serialization (which leverages the object hierarchy). For more information, see XStream JSON Tutorial. You can also use XStream's annotations to customize the serialized JSON.
JSONP
You can tell Enunciate to enable a JSONP parameter in a JSON request with the use of the org.codehaus.enunciate.rest.annotations.JSONP annotation. When this annotation is applied at the method, class, or package level, any JSON requests can supply a JSONP parameter. The parameter name can be customized with the annotation. The default value is "callback".
Steps
generate
The generate step of the REST module sets up the known content type handlers and writes out a metadata file that contains the parameter names of each REST method.
Configuration
The REST module supports the following attributes:
- The "defaultContentTypeHandler" attribute is used to define the default content type handler when no others are found for a given content type. The default value is "org.codehaus.enunciate.modules.rest.xml.JaxbXmlContentHandler".
The "json" element
The "json" child element of the rest module configuration element supports configuration options for JSON serialization. The following attributes are supported on this element:
- The "defaultJsonSerialization" attribute is used to define the default default JSON serialization method. Default: "xmlMapped".
- The "xstreamReferenceAction" attribute is used to define the action that XStream takes with object references. Possible values are "no_references", "id_references", "relative_references", "absolute_references". Default value is "relative_references". For more information, see the XStream documentation.
The "content-type-handlers" element
The "content-type-handlers" child element of the rest module configuration element supports configuration of the content type handler for a specific content type. The "contentType" attribute identifies the content type. The "class" element is the fully-qualified classname of the content handler.
Artifacts
The REST deployment module exports no artifacts.