GlassFish REST Resource Style and Behavior Guidelines

The following document specifies the constraints that apply to all REST resources produced as part of the development effort, and acts as a set of guidelines for any external contributions. Adherence to these constraints will help ensure a consistent end-user experience.

Imperative Terms In Specifications

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document, and all other documents that comprise the specification of the GlassFish RESTful API, are to be interpreted as described in "Key words for use in RFCs to Indicate Requirement Levels" (RFC 2119).

Request Headers

In requests made to RESTful services, several specific HTTP headers are used as described in the following table:

Header Supported Values Description of Use Required
Accept Comma-delimited list of media types or media type patterns. Indicates to the server what media type(s) this client is prepared to accept. Recommended, on requests that will produce a response message body.
Accept-Language Any valid language tag (see section 14.4 of RFC 2616). Specifies the preferred language for any messages in the response body. No. Defaults to server locale.
Authorization "Basic " plus username and password (per RFC 2617). Identifies the authorized user making this request. Yes, on all requests. TODO: This will be updated in the future as we solidify supported authentication schemes.
If-Modified-Since Any date, formatted as specified in section 3.3.1 of RFC 2616 Can be used to prevent the retrieval of an item that has not been modified since the time specified. No.
If-None-Match A previously retrieved ETag value Can be used to prevent the retrieval of an item whose cached state on the client matches that on the server. No.
X-Requested-By Any non-empty value Security measure to prevent CSRF attacks Yes, on all state-changing requests (POST, DELETE, etc).
-X-Wrap-Object- True or false (default) If this header contains the value 'true' (case-insensitive), the response payload will be enclosed in a wrapper object. For collections, this wrapper will have one top-level property, items, whose value is the collection itself. For item resources, the wrapper will have one top-level property, item, whose value is the entity itself No. The system will default to non-wrapped responses.
X-Skip-Metadata True or false (default) If this header contains the value 'true' (case-insensitive), the response payload from collection resources will not include the metadata portion (see description below). No
X-GlassFish-3
Any non-empty value
If a value is provided, the response document for "integrated resources" will be that returned by GlassFish 3.x. If the header is not provided, a new document following the guidelines detailed here will be returned.
No.

Response Headers

In responses returned by , several specific HTTP headers are used as described in the following table:

Header Supported Values Description of Use Required
Content-Length Length (in bytes) of the response message body. Describes the size of the message body. Yes, on responses that contain a message body.
Content-Type Media type describing the response message body. Describes the representation and syntax of the response message body. Yes, on responses that contain a message body.
ETag A collision-resistant hash of the entities current state. Used in determining if the entity state held by the client is up-to-date with the current server-side state. No. Only required for GET and HEAD requests. Optional for OPTIONS requests.
Location Canonical URI of a newly created resource. Returns a new URI that can be used to request a representation of the newly created resource. Yes, on responses to requests that create new server side resources accessible via a URI.

FIXME - Document cache control headers and behavior somewhere? 

Standard HTTP Status Codes

REST resources will return standard HTTP response codes as described in the following table, under the conditions listed in the description.

HTTP Status Description
200 OK The request was successfully completed. If this request created a new resource that is addressable with a URI, and a response body is returned containing a representation of the new resource, a 200 status will be returned with a Location header containing the canonical URI for the newly created resource.
201 Created A request that created a new resource was completed, and no response body containing a representation of the new resource is being returned. A Location header containing the canonical URI for the newly created resource should also be returned.
202 Accepted The request has been accepted for processing, but the processing has not been completed. Per the HTTP/1.1 specification, the returned entity (if any) SHOULD include an indication of the request's current status, and either a pointer to a status monitor or some estimate of when the user can expect the request to be fulfilled.
204 No Content The server fulfilled the request, but does not need to return a response message body.
304 Not Modified A request was made for an object the client has cached but has not been modified since it was retrieved.
400 Bad Request The request could not be processed because it contains missing or invalid information (such as validation error on an input field, a missing required value, and so on).
401 Unauthorized The authentication credentials included with this request are missing or invalid. FIXME - talk about WWW-Authenticate header in the response.
403 Forbidden The server recognized your credentials, but you do not possess authorization to perform this request.
404 Not Found The request specified a URI of a resource that does not exist.
405 Method Not Allowed The HTTP verb specified in the request (DELETE, GET, HEAD, POST, PUT) is not supported for this request URI.
406 Not Acceptable The resource identified by this request is not capable of generating a representation corresponding to one of the media types in the Accept header of the request.
409 Conflict A creation or update request could not be completed, because it would cause a conflict in the current state of the resources supported by the server (for example, an attempt to create a new resource with a unique identifier already assigned to some existing resource).
500 Internal Server Error The server encountered an unexpected condition which prevented it from fulfilling the request.
501 Not Implemented The server does not (currently) support the functionality required to fulfill the request.
503 Service Unavailable The server is currently unable to handle the request due to temporary overloading or maintenance of the server.

HTTP Verb Usage

Typically, RESTful services are implemented over HTTP, but this is not necessary to be considered RESTful. In the context of GlassFish RESTful services, however, all resources MUST be implemented over HTTP, and MUST use the HTTP verbs according to the following conventions:

HTTP Verb Usage
GET GET will be used to retrieve the current representation of a model's state. It will also used to retrieve a list of URIs for child resources. For example, one would GET the current state of a particular cluster (http://machine:port/clusters/cluster1) or retrieving a list of clusters (http://machine:port/clusters). GET requestMUST NOT change server state.
POST POST will be used to create or update a new entity in the referenced collection. For example, a client would POST a representation of a new cluster to the cluster endpoint. The system will create the new cluster and MUST return a 201 (Created) status code to the client, with the URI of the newly created entity returned as the value of the Location header.

In the case of action resources (e.g., starting or stopping an instance), the system SHOULD return a 200 (OK) status, or, for long-running, asynchronous operations, a 202 (Accepted) status, with a URI for the long-running process returned as the value of the Location header. When a long-running process has finished, the system must then return the appropriate status code, e.g. 200 (Ok), 500 (Server Error), etc.

POST will also be used to update the state of an existing entity. If the entity does not exist, the system SHOULD return a 404 (Not Found) error. The creation of new entities SHOULD be achieved by POSTing to a collection URI (http://machine:port/clusters/).
PUT PUT is typically used to create a new entity when the client is allowed to specify they URI of the newly created entity (e.g., PUTting to  http://machine:port/clusters/cluster2 would create a new cluster at that URI). URI creation SHOULD be handled by the system, so the use of PUT is discouraged, and, thus SHOULD NOT be used.
DELETE DELETE is used to delete a resource. If the entity is successfully deleted, the system SHOULD return 200 (Ok). If the entity is not found, the system SHOULD return a 404 (Not Found). If the delete fails for any reason (such as a validation failure), the system SHOULD return the appropriate status code and an explanatory message as the response body.
OPTIONS OPTIONS can be used to interrogate a resource. The response MAY include such things as methods supported, action resource links, etc.
HEAD HEAD can be used to interrogate a resource with respect to caching. For example, a HEAD response MAY return various headers such as ETag to help a client determine if a locally-cached representation is out of date.

Response Bodies for Successful Requests

Successful requests MUST return an HTTP status code of 200 (OK), 201 (Created), or 204 (No Content), to indicate that the requested action has been successfully performed, or 202 (Accepted) for long-running, asynchronous actions. In addition, response MAY include a response message body (with an appropriate media type) containing a representation of the requested information.


The system supports three basic types of resources:

  • collections (a list of entities)
  • entities (the state of a single, specific entity)
  • actions (resource defined for the purpose of exposing workflow-related operations on parent resources).

Each of these resources will return bodies tailored to the purpose of the resource.

Collection Resources

The response of a collection resource will a list of the entities owned by the parent. For example, an /accounts/ resource would return a list of Account objects. To maintain some flexibility in the structure and content of the response body, this list (or JSON array) will be enclosed in a generic object. Such a response might look like this:

> Link: http://localhost:4848/management/accounts/id/account1; rel="account"; title="account1"
> Link: http://localhost:4848/management/accounts/id/account2; rel="account"; title="account2"
> Link: http://localhost:4848/management/accounts/id/account3; rel="account"; title="account3"

{
    items: [
        {'name':'account1', description="Account 1"},
        {'name':'account2', description="Account 2"},
        {'name':'account3', description="Account 3"},
    ]
}

In this example, the list returned is the full Account object. The objects in the list, though, do not specify how to address those objects directly. The client has two options: Using the name of the account, the appropriate URI can be calculated on the client and used in subsequent requests. This requires, though, that the client have a detailed knowledge of the server and how parent/child relationships are built and expressed. This results in a very tight coupling, making the client fragile, and server-side changes difficult, and/or expensive.

Another option, though, is to use the metadata provided in the response headers. Link headers SHOULD be provided for each item in the list. Each header's rel attribute should specify the type of data represented by the link, and the title attribute should reflect the unique identifier used by the system for each entity.

For example above we see three Link headers (each line prefixed with "> " as one might see returned from the curl command-line client) were returned, each one holding the URI for a given account. The rel attribute for each link was set to "account", as that is the type of the entity in question, and the title attribute was set to the account's name, as that is the primary key by which the system identifies a given account. Given this information, the client should easily be able to find the URI for a given Account in the items list.

Entity Resources

When a client accesses an entity resource, it expects the current state of the entity. There is, however, more to the entity than its state. For example, an entity might have child or action resources about which the client needs to be informed. As with collection resources, this type of information will be presented to the client via Link headers. There exists the possibility, though, that extra data, such as error messages, etc. may also need to be sent to the client, so the entity will be enclosed in an outer, generic object, under the key "item":

> Link: http://localhost:4848/management/accounts/id/account1; rel="self"
> Link: http://localhost:4848/management/accounts/id/account1/services; rel="services"

{
    "item": {
        "name": "account1",
        "description": "Account 1"
    }
}

In this example, we see that an account can have services associated with it. The client, then, already aware of this through domain knowledge acquired during requirements gathering and system design, knows to look for a link with a rel value of "services" to find the URI to fetch this type of information. The client is, of course, free to compute the URI itself, but the fragility and coupling issues discussed above apply here as well.

In this example, another Link header was provided, this one with the rel value of "self". Many REST users are accustomed to seeing this type of self-referential data in the response, so resources MAY return this if desired.

Action Resources

An action resource, as noted in the Jersey documentation, is a concession to the real world fact that some things can't easily be encapsulated as a piece of state. The Jersey documentation uses the workflow of an online ordering system as an example: the client doesn't want or need to know the details. It just wants to pay for the order, ship it, etc. These types of interactions, then are exposed as "action resources", and they are children of the resource on which they act. In the example above, we may need to lock or unlock an account. Rather than exposing a "locked" property and requiring that the user discover the property then somehow learn how to set it, we can model this actions. These are discovered via another set of Link headers:

> Link: http://localhost:4848/management/accounts/id/account1; rel="self"
> Link: http://localhost:4848/management/accounts/id/account1/services; rel="services"
> Link: http://localhost:4848/management/accounts/id/account1/lock; rel="action"; title="lock";op=POST
> Link: http://localhost:4848/management/accounts/id/account1/unlock; rel="action"; title="lock";op=POST

{
    "item": {
        "name": "account1",
        "description": "Account 1"
    }
}

This is the same entity as above, but we've added two more Link headers. The rel value of "action" identifies this as an action resource URL, the title says what it is, and op tells the client the HTTP method to use. The response from invoking such an action MUST include the appropriate status code (see table above) and MAY include a message object in the body of the response.

Interaction Modes

REST resources are typically consumed in a synchronous fashion: the client makes the request, then waits for the response. Modern REST resources, though, may offer more interesting means of client/server interaction including asynchronous calls and server-sent events. The GlassFish RESTful Administration framework provides support for the following "modes":

  • Synchronous
  • Asynchronous
  • Server-sent events
  • Detached

Synchronous requests

This is the standard/normal means of interacting with a REST resource and need not be elaborated upon here.

Asynchronous requests

Technically, asynchronous requests (in JAX-RS terms) are a purely client-side concern. There is no on-the-wire difference for these, but, for the sake of completeness, an example is included here:

public void asyncRestClient() throws JSONException, InterruptedException {
        getClient()
                .target(restUrl)
                .request(MediaType.APPLICATION_JSON)
                .async() // #1
                .post(Entity.entity("Here is some text", MediaType.TEXT_PLAIN),
                        new InvocationCallback<Response>() { // #2
            @Override
            public void completed(Response response) {
                processResponse(response.readEntity(JSONObject.class));
            }

            @Override
            public void failed(ClientException ce) {
                // Do something
            }
        });
    }

This simple example shows how to use the JAX-RS Client API to make an asynchronous request. First, a call to async() is added to the call chain (#1), and, second, an instance of InvocationCallback to is passed to post() (#2). The Client creates a background thread to handle the request. This thread sends the request, then blocks, waiting for the response. Once the response is received, it calls completed() on the InvocationCallback object. The REST client code can then read the entity off the Response, and pass it along to a business method for processing. If an error occurs, the Client will call failure(), at which point the client should handle the error in a manner appropriate for its context.

Server-sent events

GlassFish resources can, if written to do so, push ProgressStatus events to the client, allowing the client to display the status of a long-running request. The ProgressStatus object will look like this (formatted for readability):

event: AdminCommandInstance/stateChanged
data: {
    "state":"RUNNING",
    "id":"1",
    "empty-payload":true
}

GlassFish Currently provides a JAX-RS MessageBodyReader to read this entity off the request and return a ProgressStatus object which the client can consume. In a typical SSE-based requests, three different event types will be seen.

AdminCommandInstance/stateChanged
This event will be the first and the last event type received. It will notify the client the request has been received and started, and, finally, that it has completed. The data elements of this event will look like this, respecitvely:

RUNNING:

{
    "state":"RUNNING",
    "id":"1",
    "empty-payload":true
}

COMPLETED:

{
    "state":"COMPLETED",
    "id":"1",
    "empty-payload":true,
    "action-report": {
        "message":"Slow command completed.",
        "command":"slow-command AdminCommand",
        "exit_code":"SUCCESS",
        "extraProperties": {
            "response":{
                "messages":[{"message":"It works!","severity":"SUCCESS"}]
            }
        }
    }
}

ProgressStatus/state
This event will send the initial state of the ProgessStatus object:

{
    "progress-status":{
        "name":"slow-command",
        "id":"1",
        "total-step-count":-1,
        "current-step-count":0,
        "complete":false
     }
}

ProgressStatus/change
This event is sent anytime a change happens on the server. The number and frequency of these events is dependent on the REST resource implementation. No assumptions should be made regarding either quantity or frequency.

{
    "progress-status-event":{
        "changed":["SPINNER","STEPS"],
        "message":"Finished step #1",
        "progress-status":{
            "name":"slow-command",
            "id":"1",
            "total-step-count":5,
            "current-step-count":1,
            "complete":false
        }
    }
}

The last event of this type should have its complete flag set to true. A client of an SSE-based resource is considerably more complex than a normal client, so an example is included here.

Click here to see the example source
import com.sun.enterprise.admin.remote.reader.AdminCommandStateJsonReader;
import com.sun.enterprise.admin.remote.reader.ProgressStatusDTOJsonReader;
import com.sun.enterprise.admin.remote.reader.ProgressStatusEventJsonReader;
import com.sun.enterprise.admin.remote.sse.GfSseEventReceiver;
import com.sun.enterprise.admin.remote.sse.GfSseEventReceiverReader;
import com.sun.enterprise.admin.remote.sse.GfSseInboundEvent;
// ...

public class SseClient {
    public static void main(String... args) throws IOException, JSONException {
        Client client = JerseyClientFactory.newClient();
        client.configuration()
                .register(new AdminCommandStateJsonReader())
                .register(new ProgressStatusDTOJsonReader())
                .register(new ProgressStatusEventJsonReader())
                .register(GfSseEventReceiverReader.class);

        GfSseEventReceiver eventReceiver =
            client.target("http://localhost:4848/management/slow").
                request(EventChannel.SERVER_SENT_EVENTS).
                get(GfSseEventReceiver.class);
        boolean closeSse = false;
        GfSseInboundEvent event;
        ActionReport ar = null;
        String message = null;
        do {
            event = eventReceiver.readEvent();
            if (event != null) {
                final String eventName = event.getName();
                if (AdminCommandState.EVENT_STATE_CHANGED.equals(eventName)) {
                    AdminCommandState acs = event.getData(AdminCommandState.class,
                        MediaType.APPLICATION_JSON_TYPE);
                    if (acs.getState() == AdminCommandState.State.COMPLETED
                            || acs.getState() == AdminCommandState.State.RECORDED) {
                        if (acs.getActionReport() != null) {
                            ar = acs.getActionReport();
                            final JSONObject responseBody =
                                new JSONObject((Map)ar.getExtraProperties().get("response"));
                            JSONArray messages = responseBody.getJSONArray("messages");
                            message = ((JSONObject)messages.get(0)).getString("message");
                        }
                        closeSse = true;
                    }
                }
            }
        } while (event != null && !eventReceiver.isClosed() && !closeSse);
        if (closeSse) {
            try {
                eventReceiver.close();
            } catch (Exception exc) {
            }
        }

        System.out.println(message);

    }
}

Detached requests

This interaction mode is a GlassFish-specific mode and, like server-sent events, may not supported by every resource. In a nutshell, requests of this type will return a URI to a job ID, rather than, say, an entity. To request a detached execution, the client should send the query parameter __detached with an arbitrary value (only the presence of the parameter is important). Resources which support this MUST return 201 (Accepted), and SHOULD return two headers: Location, which is the URI by which clients can check the status of the detached job, and, optionally, X-Location which gives the URI of the entity should the detached execution ultimately succeed. (NOTE: X-Location, both it's presence and its name, may change for the final release of the product).

Client Models

To help with authoring client applications, as well as helping automate the serialization and deserialization of the entity state, each REST resource SHOULD expose on or more model Objects which hold the state of the resource. The system will then encode this resource as JSON (or any other supported encoding). If using such a model is not desired, the module providing the resource MUST provide a means to encode and decode the entity state. This means SHOULD integrate with REST interface architecture, which is described in another document.

These models, if used, SHOULD adhere to the JavaBeans specification as much as possible to ensure consistent on-the-wire encoding (see next section).

Encoded Client Structure

The client state, when encoded, should be done in as consistent a manner as possible. To that end, the following rules SHOULD be followed when encoding values:

  • All on-the-wire state encoding SHOULD be done using JSON, using the "natural" notation (see, for example, section 5.2.2.2 of the Jersey documentation)
  • All element and attribute names should be camelCased. Dashes and underscores SHOULD be avoided (e.g., preferred: someLongPropertyName; discouraged: some_long_property_name and some-long-property-name).
  • Collections SHOULD be encoded in a manner as natural as possible in the target encoding. For example, in JSON, lists/arrays will be designated by using brackets ([ and ]), even for single item lists:
"items":["item1", "item2", "itemN"]

while maps will be designated using braces ({ and }), even for single item collections:

"someObject" : {"property1": "string value", "property2" : 2 }

Error Response Message Bodies

It is, of course, possible for a number of things to go wrong. The various underlying causes are described (as discussed above) by various HTTP status codes in the range 400-499 (for client side errors) or 500-599 (for server side problems). The description of each request type MAY list the possible status codes returned by that request type.

If a response is returned with an error status code (400-499 or 500-599), the server SHOULD also return a message body containing an error object containing the message(s) which describe what went wrong. The values of such messages might be used, for example, to communicate with a human user of the client side application. Sensitive information, such as stack traces, SHOULD NOT be reflected to the user. For example:

{
    "validationErrors" : [
        {
            "field" : "emailAddress",
            "message" :"The value of emailAddress is not a valid email address"
        },
        {
            "field" : "birthDate",
            "message" : "The value of birthDate can not be a future date"
        }
    ]
}

Design Patterns

This section will detail a list of design patterns and behaviors that SHOULD be implemented in all resources (where appropriate).

Entity Creation

After an entity is created on the server, the response to the client SHOULD include the URI to new entity via the Location header, as well as the entity itself in the body of the response.

Validation Failure

If an entity is being updated and a validation error occurs, the resource SHOULD attempt to validate as much of the submitted entity as possible, then it SHOULD return a 400 (Bad Request) with the validation failures returned as an object with a property, validationErrors, whose value is a list of strings detailing the reasons for the failure:

{
    "validationErrors" : [
        {"field" : "field1" , "message" : "Reason 1"},
        {"field" : "field2" , "message" : "Reason 2"},
        {"field" : "field3" , "message" : "Reason n"}
    ]
}

Collection Resources

When making a GET request against a collection resource, the collection SHOULD be returned as an object with a property, children, whose value is a list of the URIs of the child resources:

{
	"children":  [
		{"uri" : "http://machine:port/uri/for/child1"},
		{"uri" : "http://machine:port/uri/for/childN"}
	]
}

In some scenarios, it may be desired to return the child entities with the request. In these situations, the resource can identify such a request from the client via the presence of the _full ( TODO: discuss this name ) query parameter. If present, the resource MAY honor the request. If the request will result in a significant use of system resources (memory, bandwidth, etc), the resource MAY ignore the request to return the child entity states.

URI Addressability

Generated URIs SHOULD be addressable by the client; i.e., The machine, port, etc SHOULD reflect that specified by client, meaning that the presence of any NAT/firewall in the network SHOULD NOT result in the generation of unreachable URIs.

Authentication and Authorization

If security is enabled (as it may not be in a development environment) and a request is made to a resource by an unauthenticated user, the resource MUST return 401 (Unauthorized)

If security is enabled and a request is made to a resource to which the client does not have permissions, the system MUST return 403 (Forbidden).

If security is enabled and a request is made to a resource to which the client has permission, but the entity contains data to which the client does not have permission, the resource MAY return only the portion to which the client has permission, or the resource MAY reject the entire request with a 403 (Forbidden) response. In the latter case, the system SHOULD include an explanatory error message in the body.

Multiple Delete

Deleting multiple entities at one time is often desired for performance reasons. Resources that support this operation MUST maintain fidelity with the spirit of REST and implement the method using DELETE. The IDs of the entities to be delete SHOULD be specified by repeated id=value query parameters.

Open Questions

  1. What content types must I support in a new resource, and what should I expect from any GlassFish resource?
  2. If I have an artifact like a Data Source that supports a number of operations - shrink, suspend, resume, start, stop - how would I represent that in REST? As one resource? As many?
  3. If I am asking for a collection of results and the user does not have read privs for all of the artifacts, how do I convey that in a response?
  4. Must resources encode values to prevent cross site scripting? Under what circumstances?
  5. Must resource implementations be sensitive to CSRF, or will that be addressed in infrastructure?
  6. If a resource is to implement a "Clone" feature, what would that request look like? How is the source object conveyed?
  7. How is metadata for a request provided (options, other)?
  8. How are related resource URLs made available in a response?
  9. How should resources be named? Should parent-child relationships be conveyed in a URL? What is the best pattern for describing specific 'facets' or perspectives into an artifact?