/**
* Copyright 2005-2014 Restlet
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or or EPL 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 Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.restlet.data.CacheDirective;
import org.restlet.data.Header;
import org.restlet.data.MediaType;
import org.restlet.data.RecipientInfo;
import org.restlet.data.Warning;
import org.restlet.engine.header.HeaderConstants;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.ClientResource;
import org.restlet.util.Series;
/**
* Generic message exchanged between components.
*
* @author Jerome Louvel
*/
public abstract class Message {
/** The modifiable attributes map. */
private volatile ConcurrentMap<String, Object> attributes;
/** The caching directives. */
private volatile List<CacheDirective> cacheDirectives;
/** The date and time at which the message was originated. */
private volatile Date date;
/** The payload of the message. */
private volatile Representation entity;
// [ifndef gwt] member
/** The optional cached text. */
private volatile String entityText;
/** Callback invoked when an error occurs when sending the message. */
private volatile Uniform onError;
/** Callback invoked after sending the message. */
private volatile Uniform onSent;
/** The intermediary recipients info. */
private volatile List<RecipientInfo> recipientsInfo;
/** The additional warnings information. */
private volatile List<Warning> warnings;
/**
* Constructor.
*/
public Message() {
this((Representation) null);
}
/**
* Constructor.
*
* @param entity
* The payload of the message.
*/
public Message(Representation entity) {
this.attributes = null;
this.cacheDirectives = null;
this.date = null;
this.entity = entity;
// [ifndef gwt] instruction
this.entityText = null;
this.onSent = null;
this.recipientsInfo = null;
this.warnings = null;
}
// [ifndef gwt] method
/**
* If the entity is transient or its size unknown in advance but available,
* then the entity is wrapped with a
* {@link org.restlet.representation.BufferingRepresentation}.<br>
* <br>
* Be careful as this method could create potentially very large byte
* buffers in memory that could impact your application performance.
*
* @see org.restlet.representation.BufferingRepresentation
* @see ClientResource#setRequestEntityBuffering(boolean)
* @see ClientResource#setResponseEntityBuffering(boolean)
*/
public void bufferEntity() {
if ((getEntity() != null)
&& (getEntity().isTransient() || (getEntity().getSize() == Representation.UNKNOWN_SIZE))
&& getEntity().isAvailable()) {
setEntity(new org.restlet.representation.BufferingRepresentation(
getEntity()));
}
}
/**
* Asks the underlying connector to immediately flush the network buffers.
*
* @throws IOException
*/
public void flushBuffers() throws IOException {
}
/**
* Returns the modifiable map of attributes that can be used by developers
* to save information relative to the message. Creates a new instance if no
* one has been set. This is an easier alternative to the creation of a
* wrapper instance around the whole message.<br>
* <br>
*
* In addition, this map is a shared space between the developer and the
* connectors. In this case, it is used to exchange information that is not
* uniform across all protocols and couldn't therefore be directly included
* in the API. For this purpose, all attribute names starting with
* "org.restlet" are reserved. Currently the following attributes are used:
* <table>
* <tr>
* <th>Attribute name</th>
* <th>Class name</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>org.restlet.http.headers</td>
* <td>org.restlet.util.Series<org.restlet.engine.header.Header></td>
* <td>Server HTTP connectors must provide all request headers and client
* HTTP connectors must provide all response headers, exactly as they were
* received. In addition, developers can also use this attribute to specify
* <b>non-standard</b> headers that should be added to the request or to the
* response.</td>
* </tr>
* <tr>
* <td>org.restlet.https.clientCertificates</td>
* <td>List<java.security.cert.Certificate></td>
* <td>For requests received via a secure connector, indicates the ordered
* list of client certificates, if they are available and accessible.</td>
* </tr>
* </table>
* <br>
* Most of the standard HTTP headers are directly supported via the Restlet
* API. Thus, adding such HTTP headers is forbidden because it could
* conflict with the connector's internal behavior, limit portability or
* prevent future optimizations. The other standard HTTP headers (that are
* not supported) can be added as attributes via the
* "org.restlet.http.headers" key.<br>
*
* @return The modifiable attributes map.
*/
public ConcurrentMap<String, Object> getAttributes() {
// Lazy initialization with double-check.
ConcurrentMap<String, Object> r = this.attributes;
if (r == null) {
synchronized (this) {
r = this.attributes;
if (r == null) {
this.attributes = r = new ConcurrentHashMap<String, Object>();
}
}
}
return this.attributes;
}
/**
* Returns the cache directives.<br>
* <br>
* Note that when used with HTTP connectors, this property maps to the
* "Cache-Control" header.
*
* @return The cache directives.
*/
public List<CacheDirective> getCacheDirectives() {
// Lazy initialization with double-check.
List<CacheDirective> r = this.cacheDirectives;
if (r == null) {
synchronized (this) {
r = this.cacheDirectives;
if (r == null) {
this.cacheDirectives = r = new CopyOnWriteArrayList<CacheDirective>();
}
}
}
return r;
}
/**
* Returns the date and time at which the message was originated.
*
* @return The date and time at which the message was originated.
*/
public Date getDate() {
return date;
}
/**
* Returns the entity representation.
*
* @return The entity representation.
*/
public Representation getEntity() {
return this.entity;
}
// [ifndef gwt] method
/**
* Returns the entity as text. This method can be called several times and
* will always return the same text. Note that if the entity is large this
* method can result in important memory consumption.
*
* @return The entity as text.
*/
public String getEntityAsText() {
if (this.entityText == null) {
try {
this.entityText = (getEntity() == null) ? null : getEntity()
.getText();
} catch (java.io.IOException e) {
Context.getCurrentLogger().log(java.util.logging.Level.FINE,
"Unable to get the entity text.", e);
}
}
return this.entityText;
}
/**
* Returns the series of lower-level HTTP headers. Please not that this
* method should rarely be used as most HTTP headers are already surfaced by
* the Restlet API. The result series can be used to deal with HTTP
* extension headers.
*
* @return The HTTP headers.
*/
@SuppressWarnings("unchecked")
public Series<Header> getHeaders() {
Series<Header> headers = (Series<Header>) getAttributes().get(
HeaderConstants.ATTRIBUTE_HEADERS);
if (headers == null) {
// [ifndef gwt] instruction
headers = new Series<Header>(Header.class);
// [ifdef gwt] instruction uncomment
// headers = new org.restlet.engine.util.HeaderSeries();
getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, headers);
}
return headers;
}
/**
* Returns the callback invoked when an error occurs when sending the
* message.
*
* @return The callback invoked when an error occurs when sending the
* message.
*/
public Uniform getOnError() {
return onError;
}
/**
* Returns the callback invoked after sending the message.
*
* @return The callback invoked after sending the message.
*/
public Uniform getOnSent() {
return onSent;
}
/**
* Returns the intermediary recipient information.<br>
* <br>
* Note that when used with HTTP connectors, this property maps to the "Via"
* headers.
*
* @return The intermediary recipient information.
*/
public List<RecipientInfo> getRecipientsInfo() {
// Lazy initialization with double-check.
List<RecipientInfo> r = this.recipientsInfo;
if (r == null) {
synchronized (this) {
r = this.recipientsInfo;
if (r == null) {
this.recipientsInfo = r = new CopyOnWriteArrayList<RecipientInfo>();
}
}
}
return r;
}
/**
* Returns the additional warnings information.<br>
* <br>
* Note that when used with HTTP connectors, this property maps to the
* "Warning" headers.
*
* @return The additional warnings information.
*/
public List<Warning> getWarnings() {
// Lazy initialization with double-check.
List<Warning> r = this.warnings;
if (r == null) {
synchronized (this) {
r = this.warnings;
if (r == null) {
this.warnings = r = new CopyOnWriteArrayList<Warning>();
}
}
}
return r;
}
/**
* Indicates if the message was or will be exchanged confidentially, for
* example via a SSL-secured connection.
*
* @return True if the message is confidential.
*/
public abstract boolean isConfidential();
/**
* Indicates if a content is available and can be sent or received. Several
* conditions must be met: the content must exists and have some available
* data.
*
* @return True if a content is available and can be sent.
*/
public boolean isEntityAvailable() {
// The declaration of the "result" variable is a workaround for the GWT
// platform. Please keep it!
boolean result = (getEntity() != null) && getEntity().isAvailable();
return result;
}
/**
* Releases the message's entity if present.
*
* @see org.restlet.representation.Representation#release()
*/
public void release() {
if (getEntity() != null) {
getEntity().release();
}
}
/**
* Sets the modifiable map of attributes. This method clears the current map
* and puts all entries in the parameter map.
*
* @param attributes
* A map of attributes
*/
public void setAttributes(Map<String, Object> attributes) {
synchronized (getAttributes()) {
if (attributes != getAttributes()) {
getAttributes().clear();
if (attributes != null) {
getAttributes().putAll(attributes);
}
}
}
}
/**
* Sets the cache directives. Note that when used with HTTP connectors, this
* property maps to the "Cache-Control" header. This method clears the
* current list and adds all entries in the parameter list.
*
* @param cacheDirectives
* The cache directives.
*/
public void setCacheDirectives(List<CacheDirective> cacheDirectives) {
synchronized (getCacheDirectives()) {
if (cacheDirectives != getCacheDirectives()) {
getCacheDirectives().clear();
if (cacheDirectives != null) {
getCacheDirectives().addAll(cacheDirectives);
}
}
}
}
/**
* Sets the date and time at which the message was originated.
*
* @param date
* The date and time at which the message was originated.
*/
public void setDate(Date date) {
this.date = date;
}
/**
* Sets the entity representation.
*
* @param entity
* The entity representation.
*/
public void setEntity(Representation entity) {
this.entity = entity;
}
/**
* Sets a textual entity.
*
* @param value
* The represented string.
* @param mediaType
* The representation's media type.
*/
public void setEntity(String value, MediaType mediaType) {
setEntity(new StringRepresentation(value, mediaType));
}
/**
* Sets the callback invoked when an error occurs when sending the message.
*
* @param onError
* The callback invoked when an error occurs when sending the
* message.
*/
public void setOnError(Uniform onError) {
this.onError = onError;
}
/**
* Sets the callback invoked after sending the message.
*
* @param onSentCallback
* The callback invoked after sending the message.
*/
public void setOnSent(Uniform onSentCallback) {
this.onSent = onSentCallback;
}
/**
* Sets the modifiable list of intermediary recipients. Note that when used
* with HTTP connectors, this property maps to the "Via" headers. This
* method clears the current list and adds all entries in the parameter
* list.
*
* @param recipientsInfo
* A list of intermediary recipients.
*/
public void setRecipientsInfo(List<RecipientInfo> recipientsInfo) {
synchronized (getRecipientsInfo()) {
if (recipientsInfo != getRecipientsInfo()) {
getRecipientsInfo().clear();
if (recipientsInfo != null) {
getRecipientsInfo().addAll(recipientsInfo);
}
}
}
}
/**
* Sets the additional warnings information. Note that when used with HTTP
* connectors, this property maps to the "Warning" headers. This method
* clears the current list and adds all entries in the parameter list.
*
* @param warnings
* The warnings.
*/
public void setWarnings(List<Warning> warnings) {
synchronized (getWarnings()) {
if (warnings != getWarnings()) {
getWarnings().clear();
if (warnings != null) {
getWarnings().addAll(warnings);
}
}
}
}
}