org.restlet.resource.Resource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.servicemix.bundles.restlet
Show all versions of org.apache.servicemix.bundles.restlet
This OSGi bundle wraps org.restlet, and com.noelios.restlet ${pkgVersion} jar files.
The newest version!
/**
* Copyright 2005-2008 Noelios Technologies.
*
* The contents of this file are subject to the terms of the following open
* source licenses: LGPL 3.0 or LGPL 2.1 or CDDL 1.0 (the "Licenses"). You can
* select the license that you prefer but you may not use this file except in
* compliance with one of these Licenses.
*
* You can obtain a copy of the LGPL 3.0 license at
* http://www.gnu.org/licenses/lgpl-3.0.html
*
* You can obtain a copy of the LGPL 2.1 license at
* http://www.gnu.org/licenses/lgpl-2.1.html
*
* You can obtain a copy of the CDDL 1.0 license at
* http://www.sun.com/cddl/cddl.html
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royaltee free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://www.noelios.com/products/restlet-engine
*
* Restlet is a registered trademark of Noelios Technologies.
*/
package org.restlet.resource;
import java.util.ArrayList;
import java.util.List;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Handler;
import org.restlet.data.Dimension;
import org.restlet.data.Language;
import org.restlet.data.Parameter;
import org.restlet.data.ReferenceList;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.data.Status;
import org.restlet.util.Series;
/**
* Intended conceptual target of a hypertext reference. "Any information that
* can be named can be a resource: a document or image, a temporal service (e.g.
* "today's weather in Los Angeles"), a collection of other resources, a
* non-virtual object (e.g. a person), and so on. In other words, any concept
* that might be the target of an author's hypertext reference must fit within
* the definition of a resource. The only thing that is required to be static
* for a resource is the semantics of the mapping, since the semantics is what
* distinguishes one resource from another." Roy T. Fielding
*
* Another definition adapted from the URI standard (RFC 3986): a resource is
* the conceptual mapping to a representation (also known as entity) or set of
* representations, not necessarily the representation which corresponds to that
* mapping at any particular instance in time. Thus, a resource can remain
* constant even when its content (the representations to which it currently
* corresponds) changes over time, provided that the conceptual mapping is not
* changed in the process. In addition, a resource is always identified by a
* URI.
*
* This is the point where the RESTful view of your Web application can be
* integrated with your domain objects. Those domain objects can be implemented
* using any technology, relational databases, object databases, transactional
* components like EJB, etc.
*
* You just have to extend this class to override the REST methods you want to
* support like {@link #acceptRepresentation(Representation)} for POST
* processing, {@link #storeRepresentation(Representation)} for PUT processing
* or {@link #removeRepresentations()} for DELETE processing.
*
* The common GET method is supported by the modifiable "variants" list property
* and the {@link #represent(Variant)} method. This allows an easy and cheap
* declaration of the available variants, in the constructor for example. Then
* the creation of costly representations is delegated to the
* {@link #represent(Variant)} method when actually needed.
*
* Concurrency note: typically created by Routers, Resource instances are the
* final handlers of requests. Unlike the other processors in the Restlet chain,
* a Resource instance is not reused by several calls and is only invoked by one
* thread. Therefore, it doesn't have to be thread-safe.
*
* @see Source
* dissertation
* @see Tutorial
* : Reaching target Resources
* @see org.restlet.resource.Representation
* @see org.restlet.Finder
* @author Jerome Louvel
* @author Thierry Boileau
* @author Konstantin Laufer ([email protected])
*/
public class Resource extends Handler {
/** Indicates if the resource is actually available. */
private boolean available;
/**
* Indicates if the representations can be modified via the
* {@link #handlePost()}, the {@link #handlePut()} or the
* {@link #handleDelete()} methods.
*/
private boolean modifiable;
/** Indicates if the best content is automatically negotiated. */
private boolean negotiateContent;
/**
* Indicates if the representations can be read via the {@link #handleGet()}
* method.
*/
private boolean readable;
/** The modifiable list of variants. */
private volatile List variants;
/**
* Initializer block to ensure that the basic properties of the Resource are
* initialized consistently across constructors.
*/
{
this.available = true;
this.modifiable = false;
this.negotiateContent = true;
this.readable = true;
this.variants = null;
}
/**
* Special constructor used by IoC frameworks. Note that the init() method
* MUST be invoked right after the creation of the handler in order to keep
* a behavior consistent with the normal three arguments constructor.
*/
public Resource() {
}
/**
* Normal constructor. This constructor will invoke the parent constructor
* by default.
*
* @param context
* The parent context.
* @param request
* The request to handle.
* @param response
* The response to return.
*/
public Resource(Context context, Request request, Response response) {
super(context, request, response);
}
/**
* Accepts and processes a representation posted to the resource. The
* default behavior is to set the response status to
* {@link Status#SERVER_ERROR_INTERNAL}.
*
* This is the higher-level method that let you process POST requests.
*
* @param entity
* The posted entity.
*/
public void acceptRepresentation(Representation entity)
throws ResourceException {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
}
/**
* Indicates if DELETE calls are allowed by checking the "modifiable"
* property.
*
* @return True if the method is allowed.
*/
@Override
public boolean allowDelete() {
return isModifiable();
}
/**
* Indicates if GET calls are allowed by checking the "readable" property.
*
* @return True if the method is allowed.
*/
@Override
public boolean allowGet() {
return isReadable();
}
/**
* Indicates if POST calls are allowed by checking the "modifiable"
* property.
*
* @return True if the method is allowed.
*/
@Override
public boolean allowPost() {
return isModifiable();
}
/**
* Indicates if PUT calls are allowed by checking the "modifiable" property.
*
* @return True if the method is allowed.
*/
@Override
public boolean allowPut() {
return isModifiable();
}
/**
* Asks the resource to delete itself and all its representations.The
* default behavior is to invoke the {@link #removeRepresentations()}
* method.
*
* @deprecated Use the {@link #removeRepresentations()} method instead.
*/
@Deprecated
public void delete() {
try {
removeRepresentations();
} catch (ResourceException re) {
getResponse().setStatus(re.getStatus(), re);
}
}
/**
* Returns the preferred representation according to the client preferences
* specified in the request.
*
* @return The preferred representation.
* @deprecated Use the {@link #represent()} method instead.
* @see #getPreferredVariant()
*/
@Deprecated
public Representation getPreferredRepresentation() {
Representation result = null;
try {
result = represent();
} catch (ResourceException re) {
getResponse().setStatus(re.getStatus(), re);
}
return result;
}
/**
* Returns the preferred variant according to the client preferences
* specified in the request.
*
* @return The preferred variant.
*/
public Variant getPreferredVariant() {
Variant result = null;
final List variants = getVariants();
if ((variants != null) && (!variants.isEmpty())) {
Language language = null;
// Compute the preferred variant. Get the default language
// preference from the Application (if any).
final Application app = Application.getCurrent();
if (app != null) {
language = app.getMetadataService().getDefaultLanguage();
}
result = getRequest().getClientInfo().getPreferredVariant(variants,
language);
}
return result;
}
/**
* Returns a full representation for a given variant previously returned via
* the getVariants() method. The default implementation directly returns the
* variant in case the variants are already full representations. In all
* other cases, you will need to override this method in order to provide
* your own implementation.
*
*
* This method is very useful for content negotiation when it is too costly
* to initilize all the potential representations. It allows a resource to
* simply expose the available variants via the getVariants() method and to
* actually server the one selected via this method.
*
* @param variant
* The variant whose full representation must be returned.
* @return The full representation for the variant.
* @see #getVariants()
* @deprecated Use the {@link #represent(Variant)} method instead.
*/
@Deprecated
public Representation getRepresentation(Variant variant) {
Representation result = null;
try {
result = represent(variant);
} catch (ResourceException re) {
getResponse().setStatus(re.getStatus(), re);
}
return result;
}
/**
* Returns the modifiable list of variants. Creates a new instance if no one
* has been set. A variant can be a purely descriptive representation, with
* no actual content that can be served. It can also be a full
* representation in case a resource has only one variant or if the
* initialization cost is very low.
*
* Note that the order in which the variants are inserted in the list
* matters. For example, if the client has no preference defined, or if the
* acceptable variants have the same quality level for the client, the first
* acceptable variant in the list will be returned.
*
* It is recommended to not override this method and to simply use it at
* construction time to initialize the list of available variants.
* Overriding it may reconstruct the list for each call which can be
* expensive.
*
* @return The list of variants.
* @see #getRepresentation(Variant)
*/
public List getVariants() {
// Lazy initialization with double-check.
List v = this.variants;
if (v == null) {
synchronized (this) {
v = this.variants;
if (v == null) {
this.variants = v = new ArrayList();
}
}
}
return v;
}
/**
* Handles a DELETE call by invoking the {@link #removeRepresentations()}
* method. It also automatically support conditional DELETEs.
*/
@Override
public void handleDelete() {
boolean canDelete = true;
if (getRequest().getConditions().hasSome()) {
Variant preferredVariant = null;
if (isNegotiateContent()) {
preferredVariant = getPreferredVariant();
} else {
final List variants = getVariants();
if (variants.size() == 1) {
preferredVariant = variants.get(0);
} else {
getResponse().setStatus(
Status.CLIENT_ERROR_PRECONDITION_FAILED);
canDelete = false;
}
}
// The conditions have to be checked
// even if there is no preferred variant.
if (canDelete) {
final Status status = getRequest().getConditions().getStatus(
getRequest().getMethod(),
getRepresentation(preferredVariant));
if (status != null) {
getResponse().setStatus(status);
canDelete = false;
}
}
}
if (canDelete) {
delete();
}
}
/**
* Handles a GET call by automatically returning the best representation
* available. The content negotiation is automatically supported based on
* the client's preferences available in the request. This feature can be
* turned off using the "negotiateContent" property.
*
* If the resource's "available" property is set to false, the method
* immediately returns with a {@link Status#CLIENT_ERROR_NOT_FOUND} status.
*
* The negotiated representation is obtained by calling the
* {@link #getPreferredVariant()}. If a variant is sucessfully selected,
* then the {@link #represent(Variant)} method is called to get the actual
* representation corresponding to the metadata in the variant.
*
* If no variant matching the client preferences is available, the response
* status is set to {@link Status#CLIENT_ERROR_NOT_ACCEPTABLE} and the list
* of available representations is returned in the response entity as a
* textual list of URIs (only if the variants have an identifier properly
* set).
*
* If the content negotiation is turned off and only one variant is defined
* in the "variants" property, then its representation is returned by
* calling the {@link #represent(Variant)} method. If several variants are
* available, then the list of available representations is returned in the
* response entity as a textual list of URIs (only if the variants have an
* identifier properly set).
*
* If no variant is defined in the "variants" property, the response status
* is set to {@link Status#CLIENT_ERROR_NOT_FOUND}.
* If it is disabled and multiple variants are available for the target
* resource, then a 300 (Multiple Choices) status will be returned with the
* list of variants URI if available. Conditional GETs are also
* automatically supported.
*/
@Override
public void handleGet() {
if (!isAvailable()) {
// Resource not existing or not available to the current client
getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);
} else {
// The variant that may need to meet the request conditions
Representation selectedRepresentation = null;
final List variants = getVariants();
if ((variants == null) || (variants.isEmpty())) {
// Resource not found
getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);
getLogger()
.warning(
"A resource should normally have at least one variant added by calling getVariants().add() in the constructor. Check your resource \""
+ getRequest().getResourceRef() + "\".");
} else if (isNegotiateContent()) {
final Variant preferredVariant = getPreferredVariant();
if (preferredVariant == null) {
// No variant was found matching the client preferences
getResponse().setStatus(Status.CLIENT_ERROR_NOT_ACCEPTABLE);
// The list of all variants is transmitted to the client
final ReferenceList refs = new ReferenceList(variants
.size());
for (final Variant variant : variants) {
if (variant.getIdentifier() != null) {
refs.add(variant.getIdentifier());
}
}
getResponse().setEntity(refs.getTextRepresentation());
} else {
// Set the variant dimensions used for content negotiation
getResponse().getDimensions().clear();
getResponse().getDimensions().add(Dimension.CHARACTER_SET);
getResponse().getDimensions().add(Dimension.ENCODING);
getResponse().getDimensions().add(Dimension.LANGUAGE);
getResponse().getDimensions().add(Dimension.MEDIA_TYPE);
// Set the negotiated representation as response entity
getResponse()
.setEntity(getRepresentation(preferredVariant));
}
selectedRepresentation = getResponse().getEntity();
} else {
if (variants.size() == 1) {
getResponse().setEntity(getRepresentation(variants.get(0)));
selectedRepresentation = getResponse().getEntity();
} else {
final ReferenceList variantRefs = new ReferenceList();
for (final Variant variant : variants) {
if (variant.getIdentifier() != null) {
variantRefs.add(variant.getIdentifier());
} else {
getLogger()
.warning(
"A resource with multiple variants should provide an identifier for each variant when content negotiation is turned off");
}
}
if (variantRefs.size() > 0) {
// Return the list of variants
getResponse().setStatus(
Status.REDIRECTION_MULTIPLE_CHOICES);
getResponse().setEntity(
variantRefs.getTextRepresentation());
} else {
getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);
}
}
}
if (selectedRepresentation == null) {
if ((getResponse().getStatus() == null)
|| (getResponse().getStatus().isSuccess() && !Status.SUCCESS_NO_CONTENT
.equals(getResponse().getStatus()))) {
getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);
} else {
// Keep the current status as the developer might prefer a
// special status like 'method not authorized'.
}
} else {
// The given representation (even if null) must meet the request
// conditions (if any).
if (getRequest().getConditions().hasSome()) {
final Status status = getRequest().getConditions()
.getStatus(getRequest().getMethod(),
selectedRepresentation);
if (status != null) {
getResponse().setStatus(status);
getResponse().setEntity(null);
}
}
}
}
}
/**
* Handles a POST call by invoking the
* {@link #acceptRepresentation(Representation)} method. It also logs a
* trace if there is no entity posted.
*/
@Override
public void handlePost() {
if (!getRequest().isEntityAvailable()) {
getLogger()
.fine(
"POST request received without any entity. Continuing processing.");
}
post(getRequest().getEntity());
}
/**
* Handles a PUT call by invoking the
* {@link #storeRepresentation(Representation)} method. It also handles
* conditional PUTs and forbids partial PUTs as they are not supported yet.
* Finally, it prevents PUT with no entity by setting the response status to
* {@link Status#CLIENT_ERROR_BAD_REQUEST} following the HTTP
* specifications.
*/
@SuppressWarnings("unchecked")
@Override
public void handlePut() {
boolean canPut = true;
if (getRequest().getConditions().hasSome()) {
Variant preferredVariant = null;
if (isNegotiateContent()) {
preferredVariant = getPreferredVariant();
} else {
final List variants = getVariants();
if (variants.size() == 1) {
preferredVariant = variants.get(0);
} else {
getResponse().setStatus(
Status.CLIENT_ERROR_PRECONDITION_FAILED);
canPut = false;
}
}
// The conditions have to be checked
// even if there is no preferred variant.
if (canPut) {
final Status status = getRequest().getConditions().getStatus(
getRequest().getMethod(),
getRepresentation(preferredVariant));
if (status != null) {
getResponse().setStatus(status);
canPut = false;
}
}
}
if (canPut) {
// Check the Content-Range HTTP Header
// in order to prevent usage of partial PUTs
final Object oHeaders = getRequest().getAttributes().get(
"org.restlet.http.headers");
if (oHeaders != null) {
final Series headers = (Series) oHeaders;
if (headers.getFirst("Content-Range", true) != null) {
getResponse()
.setStatus(
new Status(
Status.SERVER_ERROR_NOT_IMPLEMENTED,
"The Content-Range header is not understood"));
canPut = false;
}
}
}
if (canPut) {
put(getRequest().getEntity());
// HTTP spec says that PUT may return
// the list of allowed methods
updateAllowedMethods();
}
}
/**
* Initialize the resource with its context. If you override this method,
* make sure that you don't forget to call super.init() first, otherwise
* your Resource won't behave properly.
*
* @param context
* The parent context.
* @param request
* The request to handle.
* @param response
* The response to return.
*/
@Override
public void init(Context context, Request request, Response response) {
super.init(context, request, response);
}
/**
* Indicates if the resource is actually available. By default this property
* is true but it can be set to false if the resource doesn't exist at all
* or if it isn't visible to a specific client. The {@link #handleGet()}
* method will set the response's status to
* {@link Status#CLIENT_ERROR_NOT_FOUND} if this property is false.
*
* @return True if the resource is available.
*/
public boolean isAvailable() {
return this.available;
}
/**
* Indicates if the representations can be modified via the
* {@link #handlePost()}, the {@link #handlePut()} or the
* {@link #handleDelete()} methods.
*
* @return True if representations can be modified.
*/
public boolean isModifiable() {
return this.modifiable;
}
/**
* Indicates if the best content is automatically negotiated. Default value
* is true.
*
* @return True if the best content is automatically negotiated.
*/
public boolean isNegotiateContent() {
return this.negotiateContent;
}
/**
* Indicates if the representations can be read via the {@link #handleGet()}
* method.
*
* @return True if the representations can be read.
*/
public boolean isReadable() {
return this.readable;
}
/**
* Posts a representation to the resource. The default behavior is to invoke
* the {@link #acceptRepresentation(Representation)} method.
*
* @param entity
* The representation posted.
* @deprecated Use the {@link #acceptRepresentation(Representation)} method
* instead.
*/
@Deprecated
public void post(Representation entity) {
try {
acceptRepresentation(entity);
} catch (ResourceException re) {
getResponse().setStatus(re.getStatus(), re);
}
}
/**
* Puts a representation in the resource. The default behavior is to invoke
* the {@link #storeRepresentation(Representation)} method.
*
* @param entity
* The representation put.
* @deprecated Use the {@link #storeRepresentation(Representation)} method
* instead.
*/
@Deprecated
public void put(Representation entity) {
try {
storeRepresentation(entity);
} catch (ResourceException re) {
getResponse().setStatus(re.getStatus(), re);
}
}
/**
* Removes all the representations of the resource and effectively the
* resource itself. The default behavior is to set the response status to
* {@link Status#SERVER_ERROR_INTERNAL}.
*
* This is the higher-level method that let you process DELETE requests.
*/
public void removeRepresentations() throws ResourceException {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
}
/**
* Returns the preferred representation according to the client preferences
* specified in the request. By default it calls the
* {@link #represent(Variant)} method with the preferred variant returned by
* {@link #getPreferredVariant()}.
*
* @return The preferred representation.
* @see #getPreferredVariant()
* @throws ResourceException
*/
public Representation represent() throws ResourceException {
return represent(getPreferredVariant());
}
/**
* Returns a full representation for a given variant previously returned via
* the getVariants() method. The default implementation directly returns the
* variant in case the variants are already full representations. In all
* other cases, you will need to override this method in order to provide
* your own implementation.
*
*
* This method is very useful for content negotiation when it is too costly
* to initialize all the potential representations. It allows a resource to
* simply expose the available variants via the getVariants() method and to
* actually server the one selected via this method.
*
* @param variant
* The variant whose full representation must be returned.
* @return The full representation for the variant.
* @see #getVariants()
*/
public Representation represent(Variant variant) throws ResourceException {
Representation result = null;
if (variant instanceof Representation) {
result = (Representation) variant;
}
return result;
}
/**
* Indicates if the resource is actually available. By default this property
* is true but it can be set to false if the resource doesn't exist at all
* or if it isn't visible to a specific client. The {@link #handleGet()}
* method will set the response's status to
* {@link Status#CLIENT_ERROR_NOT_FOUND} if this property is false.
*
* @param available
* True if the resource is actually available.
*/
public void setAvailable(boolean available) {
this.available = available;
}
/**
* Indicates if the representations can be modified via the
* {@link #handlePost()}, the {@link #handlePut()} or the
* {@link #handleDelete()} methods.
*
* @param modifiable
* Indicates if the representations can be modified.
*/
public void setModifiable(boolean modifiable) {
this.modifiable = modifiable;
}
/**
* Indicates if the returned representation is automatically negotiated.
* Default value is true.
*
* @param negotiateContent
* True if content negotiation is enabled.
*/
public void setNegotiateContent(boolean negotiateContent) {
this.negotiateContent = negotiateContent;
}
/**
* Indicates if the representations can be read via the {@link #handleGet()}
* method.
*
* @param readable
* Indicates if the representations can be read.
*/
public void setReadable(boolean readable) {
this.readable = readable;
}
/**
* Sets the modifiable list of variants.
*
* @param variants
* The modifiable list of variants.
*/
public void setVariants(List variants) {
this.variants = variants;
}
/**
* Stores a representation put to the resource and replaces all existing
* representations of the resource. If the resource doesn't exist yet, it
* should create it and use the entity as its initial representation. The
* default behavior is to set the response status to
* {@link Status#SERVER_ERROR_INTERNAL}.
*
* This is the higher-level method that let you process PUT requests.
*
* @param entity
*/
public void storeRepresentation(Representation entity)
throws ResourceException {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy