blob: 7db4640a69e53eb9f430946d35aa84bd904fdcf1 [file] [log] [blame]
/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the "License"). You may not use this file except
* in compliance with the License.
*
* You can obtain a copy of the license at
* http://www.opensource.org/licenses/cddl1.php
* See the License for the specific language governing
* permissions and limitations under the License.
*/
/*
* Response.java
*
* Created on April 18, 2007, 9:00 AM
*
*/
package javax.ws.rs.core;
import java.net.URI;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import javax.ws.rs.ext.RuntimeDelegate;
/**
* Defines the contract between a returned instance and the runtime when
* an application needs to provide metadata to the runtime. An application
* class can extend this class directly or can use one of the static
* methods to create an instance using a ResponseBuilder.
*
* Several methods have parameters of type URI, {@link UriBuilder} provides
* convenient methods to create such values as does
* {@link <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/URI.html#create(java.lang.String)">URI.create()</a>}.
*
* @see Response.ResponseBuilder
*/
public abstract class Response {
/**
* Protected constructor, use one of the static methods to obtain a
* {@link ResponseBuilder} instance and obtain a Response from that.
*/
protected Response() {}
/**
* Return the response entity. The response will be serialized using a
* MessageBodyWriter for either the class of the entity or, in the case of
* {@link GenericEntity}, the value of {@link GenericEntity#getRawType()}.
* @return an object instance or null if there is no entity
* @see javax.ws.rs.ext.MessageBodyWriter
*/
public abstract Object getEntity();
/**
* Get the status code associated with the response.
* @return the response status code or -1 if the status was not set.
*/
public abstract int getStatus();
/**
* Get metadata associated with the response as a map. The returned map
* may be subsequently modified by the JAX-RS runtime. Values will be
* serialized using a {@link javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate}
* if one is available via
* {@link javax.ws.rs.ext.RuntimeDelegate#createHeaderDelegate(java.lang.Class)}
* for the class of the value or using the values {@code toString} method if a
* header delegate is not available.
* @return response metadata as a map
*/
public abstract MultivaluedMap<String, Object> getMetadata();
/**
* Create a new ResponseBuilder by performing a shallow copy of an
* existing Response. The returned builder has its own metadata map but
* entries are simply references to the keys and values contained in the
* supplied Response metadata map.
* @param response a Response from which the status code, entity and metadata
* will be copied
* @return a new ReponseBuilder
*/
public static ResponseBuilder fromResponse(Response response) {
ResponseBuilder b = status(response.getStatus());
b.entity(response.getEntity());
for (String headerName: response.getMetadata().keySet()) {
List<Object> headerValues = response.getMetadata().get(headerName);
for (Object headerValue: headerValues) {
b.header(headerName, headerValue);
}
}
return b;
}
/**
* Create a new ResponseBuilder with the supplied status.
* @param status the response status
* @return a new ResponseBuilder
* @throws IllegalArgumentException if status is null
*/
public static ResponseBuilder status(StatusType status) {
ResponseBuilder b = ResponseBuilder.newInstance();
b.status(status);
return b;
}
/**
* Create a new ResponseBuilder with the supplied status.
* @param status the response status
* @return a new ResponseBuilder
* @throws IllegalArgumentException if status is null
*/
public static ResponseBuilder status(Status status) {
return status((StatusType)status);
}
/**
* Create a new ResponseBuilder with the supplied status.
* @param status the response status
* @return a new ResponseBuilder
* @throws IllegalArgumentException if status is less than 100 or greater
* than 599.
*/
public static ResponseBuilder status(int status) {
ResponseBuilder b = ResponseBuilder.newInstance();
b.status(status);
return b;
}
/**
* Create a new ResponseBuilder with an OK status.
*
* @return a new ResponseBuilder
*/
public static ResponseBuilder ok() {
ResponseBuilder b = status(Status.OK);
return b;
}
/**
* Create a new ResponseBuilder that contains a representation. It is the
* callers responsibility to wrap the actual entity with
* {@link GenericEntity} if preservation of its generic type is required.
*
* @param entity the representation entity data
* @return a new ResponseBuilder
*/
public static ResponseBuilder ok(Object entity) {
ResponseBuilder b = ok();
b.entity(entity);
return b;
}
/**
* Create a new ResponseBuilder that contains a representation. It is the
* callers responsibility to wrap the actual entity with
* {@link GenericEntity} if preservation of its generic type is required.
*
* @param entity the representation entity data
* @param type the media type of the entity
* @return a new ResponseBuilder
*/
public static ResponseBuilder ok(Object entity, MediaType type) {
ResponseBuilder b = ok();
b.entity(entity);
b.type(type);
return b;
}
/**
* Create a new ResponseBuilder that contains a representation. It is the
* callers responsibility to wrap the actual entity with
* {@link GenericEntity} if preservation of its generic type is required.
*
* @param entity the representation entity data
* @param type the media type of the entity
* @return a new ResponseBuilder
*/
public static ResponseBuilder ok(Object entity, String type) {
ResponseBuilder b = ok();
b.entity(entity);
b.type(type);
return b;
}
/**
* Create a new ResponseBuilder that contains a representation. It is the
* callers responsibility to wrap the actual entity with
* {@link GenericEntity} if preservation of its generic type is required.
*
* @param entity the representation entity data
* @param variant representation metadata
* @return a new ResponseBuilder
*/
public static ResponseBuilder ok(Object entity, Variant variant) {
ResponseBuilder b = ok();
b.entity(entity);
b.variant(variant);
return b;
}
/**
* Create a new ResponseBuilder with an server error status.
*
* @return a new ResponseBuilder
*/
public static ResponseBuilder serverError() {
ResponseBuilder b = status(Status.INTERNAL_SERVER_ERROR);
return b;
}
/**
* Create a new ResponseBuilder for a created resource, set the location
* header using the supplied value.
*
* @param location the URI of the new resource. If a relative URI is
* supplied it will be converted into an absolute URI by resolving it
* relative to the request URI (see {@link UriInfo#getRequestUri}).
* @return a new ResponseBuilder
* @throws java.lang.IllegalArgumentException if location is null
*/
public static ResponseBuilder created(URI location) {
ResponseBuilder b = status(Status.CREATED).location(location);
return b;
}
/**
* Create a new ResponseBuilder for an empty response.
*
* @return a new ResponseBuilder
*/
public static ResponseBuilder noContent() {
ResponseBuilder b = status(Status.NO_CONTENT);
return b;
}
/**
* Create a new ResponseBuilder with a not-modified status.
*
* @return a new ResponseBuilder
*/
public static ResponseBuilder notModified() {
ResponseBuilder b = status(Status.NOT_MODIFIED);
return b;
}
/**
* Create a new ResponseBuilder with a not-modified status.
*
* @param tag a tag for the unmodified entity
* @return a new ResponseBuilder
* @throws java.lang.IllegalArgumentException if tag is null
*/
public static ResponseBuilder notModified(EntityTag tag) {
ResponseBuilder b = notModified();
b.tag(tag);
return b;
}
/**
* Create a new ResponseBuilder with a not-modified status
* and a strong entity tag. This is a shortcut
* for <code>notModified(new EntityTag(<i>value</i>))</code>.
*
* @param tag the string content of a strong entity tag. The JAX-RS
* runtime will quote the supplied value when creating the header.
* @return a new ResponseBuilder
* @throws java.lang.IllegalArgumentException if tag is null
*/
public static ResponseBuilder notModified(String tag) {
ResponseBuilder b = notModified();
b.tag(tag);
return b;
}
/**
* Create a new ResponseBuilder for a redirection. Used in the
* redirect-after-POST (aka POST/redirect/GET) pattern.
*
* @param location the redirection URI. If a relative URI is
* supplied it will be converted into an absolute URI by resolving it
* relative to the base URI of the application (see
* {@link UriInfo#getBaseUri}).
* @return a new ResponseBuilder
* @throws java.lang.IllegalArgumentException if location is null
*/
public static ResponseBuilder seeOther(URI location) {
ResponseBuilder b = status(Status.SEE_OTHER).location(location);
return b;
}
/**
* Create a new ResponseBuilder for a temporary redirection.
*
* @param location the redirection URI. If a relative URI is
* supplied it will be converted into an absolute URI by resolving it
* relative to the base URI of the application (see
* {@link UriInfo#getBaseUri}).
* @return a new ResponseBuilder
* @throws java.lang.IllegalArgumentException if location is null
*/
public static ResponseBuilder temporaryRedirect(URI location) {
ResponseBuilder b = status(Status.TEMPORARY_REDIRECT).location(location);
return b;
}
/**
* Create a new ResponseBuilder for a not acceptable response.
*
* @param variants list of variants that were available, a null value is
* equivalent to an empty list.
* @return a new ResponseBuilder
*/
public static ResponseBuilder notAcceptable(List<Variant> variants) {
ResponseBuilder b = status(Status.NOT_ACCEPTABLE).variants(variants);
return b;
}
/**
* A class used to build Response instances that contain metadata instead
* of or in addition to an entity. An initial instance may be obtained via
* static methods of the Response class, instance methods provide the
* ability to set metadata. E.g. to create a response that indicates the
* creation of a new resource:
* <pre>&#64;POST
* Response addWidget(...) {
* Widget w = ...
* URI widgetId = UriBuilder.fromResource(Widget.class)...
* return Response.created(widgetId).build();
* }</pre>
*
* <p>Several methods have parameters of type URI, {@link UriBuilder} provides
* convenient methods to create such values as does <code>URI.create()</code>.</p>
*
* <p>Where multiple variants of the same method are provided, the type of
* the supplied parameter is retained in the metadata of the built
* {@code Response}.</p>
*
*/
public static abstract class ResponseBuilder {
/**
* Protected constructor, use one of the static methods of
* <code>Response</code> to obtain an instance.
*/
protected ResponseBuilder() {}
/**
* Create a new builder instance.
*
* @return a new ResponseBuilder
*/
protected static ResponseBuilder newInstance() {
ResponseBuilder b = RuntimeDelegate.getInstance().createResponseBuilder();
return b;
}
/**
* Create a Response instance from the current ResponseBuilder. The builder
* is reset to a blank state equivalent to calling the ok method.
*
* @return a Response instance
*/
public abstract Response build();
/**
* Create a copy of the ResponseBuilder preserving its state.
* @return a copy of the ResponseBuilder
*/
@Override
public abstract ResponseBuilder clone();
/**
* Set the status on the ResponseBuilder.
*
* @param status the response status
* @return the updated ResponseBuilder
* @throws IllegalArgumentException if status is less than 100 or greater
* than 599.
*/
public abstract ResponseBuilder status(int status);
/**
* Set the status on the ResponseBuilder.
*
* @param status the response status
* @return the updated ResponseBuilder
* @throws IllegalArgumentException if status is null
*/
public ResponseBuilder status(StatusType status) {
if (status == null)
throw new IllegalArgumentException();
return status(status.getStatusCode());
};
/**
* Set the status on the ResponseBuilder.
*
* @param status the response status
* @return the updated ResponseBuilder
* @throws IllegalArgumentException if status is null
*/
public ResponseBuilder status(Status status) {
return status((StatusType)status);
};
/**
* Set the entity on the ResponseBuilder. It is the
* callers responsibility to wrap the actual entity with
* {@link GenericEntity} if preservation of its generic type is required.
*
* @param entity the response entity
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder entity(Object entity);
/**
* Set the response media type on the ResponseBuilder.
*
* @param type the media type of the response entity, if null any
* existing value for type will be removed
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder type(MediaType type);
/**
* Set the response media type on the ResponseBuilder.
*
* @param type the media type of the response entity, if null any
* existing value for type will be removed
* @return the updated ResponseBuilder
* @throws IllegalArgumentException if type cannot be parsed
*/
public abstract ResponseBuilder type(String type);
/**
* Set representation metadata on the ResponseBuilder. Equivalent to
* setting the values of content type, content language, and content
* encoding separately using the values of the variant properties.
*
* @param variant metadata of the response entity, a null value is
* equivalent to a variant with all null properties.
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder variant(Variant variant);
/**
* Add a Vary header that lists the available variants.
*
* @param variants a list of available representation variants, a null
* value will remove an existing value for vary.
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder variants(List<Variant> variants);
/**
* Set the language on the ResponseBuilder.
*
* @param language the language of the response entity, if null any
* existing value for language will be removed
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder language(String language);
/**
* Set the language on the ResponseBuilder.
*
*
* @param language the language of the response entity, if null any
* existing value for type will be removed
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder language(Locale language);
/**
* Set the location on the ResponseBuilder.
*
* @param location the location. If a relative URI is
* supplied it will be converted into an absolute URI by resolving it
* relative to the base URI of the application (see
* {@link UriInfo#getBaseUri}). If null any
* existing value for location will be removed.
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder location(URI location);
/**
* Set the content location on the ResponseBuilder.
*
* @param location the content location. Relative or absolute URIs
* may be used for the value of content location. If null any
* existing value for content location will be removed.
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder contentLocation(URI location);
/**
* Set an entity tag on the ResponseBuilder.
*
* @param tag the entity tag, if null any
* existing entity tag value will be removed.
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder tag(EntityTag tag);
/**
* Set a strong entity tag on the ResponseBuilder. This is a shortcut
* for <code>tag(new EntityTag(<i>value</i>))</code>.
*
* @param tag the string content of a strong entity tag. The JAX-RS
* runtime will quote the supplied value when creating the header. If
* null any existing entity tag value will be removed.
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder tag(String tag);
/**
* Set the last modified date on the ResponseBuilder.
*
* @param lastModified the last modified date, if null any existing
* last modified value will be removed.
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder lastModified(Date lastModified);
/**
* Set the cache control data on the ResponseBuilder.
*
* @param cacheControl the cache control directives, if null removes any
* existing cache control directives.
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder cacheControl(CacheControl cacheControl);
/**
* Set the expires date on the ResponseBuilder.
*
* @param expires the expiration date, if null removes any existing
* expires value.
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder expires(Date expires);
/**
* Add a header to the ResponseBuilder.
*
* @param name the name of the header
* @param value the value of the header, the header will be serialized
* using a {@link javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate} if
* one is available via
* {@link javax.ws.rs.ext.RuntimeDelegate#createHeaderDelegate(java.lang.Class)}
* for the class of {@code value} or using its {@code toString} method if a
* header delegate is not available. If {@code value} is null then all
* current headers of the same name will be removed.
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder header(String name, Object value);
/**
* Add cookies to the ResponseBuilder.
*
* @param cookies new cookies that will accompany the response. A null
* value will remove all cookies, including those added via the
* {@link #header(java.lang.String, java.lang.Object)} method.
* @return the updated ResponseBuilder
*/
public abstract ResponseBuilder cookie(NewCookie... cookies);
}
/**
* Base interface for statuses used in responses.
*/
public interface StatusType {
/**
* Get the associated status code
* @return the status code
*/
public int getStatusCode();
/**
* Get the class of status code
* @return the class of status code
*/
public Status.Family getFamily();
/**
* Get the reason phrase
* @return the reason phrase
*/
public String getReasonPhrase();
}
/**
* Commonly used status codes defined by HTTP, see
* {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10">HTTP/1.1 documentation</a>}
* for the complete list. Additional status codes can be added by applications
* by creating an implementation of {@link StatusType}.
*/
public enum Status implements StatusType {
/**
* 200 OK, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">HTTP/1.1 documentation</a>}.
*/
OK(200, "OK"),
/**
* 201 Created, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.2">HTTP/1.1 documentation</a>}.
*/
CREATED(201, "Created"),
/**
* 202 Accepted, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.3">HTTP/1.1 documentation</a>}.
*/
ACCEPTED(202, "Accepted"),
/**
* 204 No Content, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5">HTTP/1.1 documentation</a>}.
*/
NO_CONTENT(204, "No Content"),
/**
* 303 See Other, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2">HTTP/1.1 documentation</a>}.
*/
MOVED_PERMANENTLY(301, "Moved Permanently"),
/**
* 303 See Other, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4">HTTP/1.1 documentation</a>}.
*/
SEE_OTHER(303, "See Other"),
/**
* 304 Not Modified, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5">HTTP/1.1 documentation</a>}.
*/
NOT_MODIFIED(304, "Not Modified"),
/**
* 307 Temporary Redirect, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.8">HTTP/1.1 documentation</a>}.
*/
TEMPORARY_REDIRECT(307, "Temporary Redirect"),
/**
* 400 Bad Request, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">HTTP/1.1 documentation</a>}.
*/
BAD_REQUEST(400, "Bad Request"),
/**
* 401 Unauthorized, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">HTTP/1.1 documentation</a>}.
*/
UNAUTHORIZED(401, "Unauthorized"),
/**
* 403 Forbidden, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4">HTTP/1.1 documentation</a>}.
*/
FORBIDDEN(403, "Forbidden"),
/**
* 404 Not Found, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">HTTP/1.1 documentation</a>}.
*/
NOT_FOUND(404, "Not Found"),
/**
* 406 Not Acceptable, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7">HTTP/1.1 documentation</a>}.
*/
NOT_ACCEPTABLE(406, "Not Acceptable"),
/**
* 409 Conflict, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10">HTTP/1.1 documentation</a>}.
*/
CONFLICT(409, "Conflict"),
/**
* 410 Gone, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11">HTTP/1.1 documentation</a>}.
*/
GONE(410, "Gone"),
/**
* 412 Precondition Failed, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.13">HTTP/1.1 documentation</a>}.
*/
PRECONDITION_FAILED(412, "Precondition Failed"),
/**
* 415 Unsupported Media Type, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.16">HTTP/1.1 documentation</a>}.
*/
UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
/**
* 500 Internal Server Error, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">HTTP/1.1 documentation</a>}.
*/
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
/**
* 503 Service Unavailable, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4">HTTP/1.1 documentation</a>}.
*/
SERVICE_UNAVAILABLE(503, "Service Unavailable");
private final int code;
private final String reason;
private Family family;
/**
* An enumeration representing the class of status code. Family is used
* here since class is overloaded in Java.
*/
public enum Family {INFORMATIONAL, SUCCESSFUL, REDIRECTION, CLIENT_ERROR, SERVER_ERROR, OTHER};
Status(final int statusCode, final String reasonPhrase) {
this.code = statusCode;
this.reason = reasonPhrase;
switch(code/100) {
case 1: this.family = Family.INFORMATIONAL; break;
case 2: this.family = Family.SUCCESSFUL; break;
case 3: this.family = Family.REDIRECTION; break;
case 4: this.family = Family.CLIENT_ERROR; break;
case 5: this.family = Family.SERVER_ERROR; break;
default: this.family = Family.OTHER; break;
}
}
/**
* Get the class of status code
* @return the class of status code
*/
public Family getFamily() {
return family;
}
/**
* Get the associated status code
* @return the status code
*/
public int getStatusCode() {
return code;
}
/**
* Get the reason phrase
* @return the reason phrase
*/
public String getReasonPhrase() {
return toString();
}
/**
* Get the reason phrase
* @return the reason phrase
*/
@Override
public String toString() {
return reason;
}
/**
* Convert a numerical status code into the corresponding Status
* @param statusCode the numerical status code
* @return the matching Status or null is no matching Status is defined
*/
public static Status fromStatusCode(final int statusCode) {
for (Status s : Status.values()) {
if (s.code == statusCode) {
return s;
}
}
return null;
}
}
}