40.2. Messages
Overview
Message objects represent messages using the following abstract model:
- Message body
- Message headers
- Message attachments
The message body and the message headers can be of arbitrary type (they are declared as type
Object
) and the message attachments are declared to be of type javax.activation.DataHandler
, which can contain arbitrary MIME types. If you need to obtain a concrete representation of the message contents, you can convert the body and headers to another type using the type converter mechanism and, possibly, using the marshalling and unmarshalling mechanism.
One important feature of Apache Camel messages is that they support lazy creation of message bodies and headers. In some cases, this means that a message can pass through a route without needing to be parsed at all.
The Message interface
The
org.apache.camel.Message
interface defines methods to access the message body, message headers and message attachments, as shown in Example 40.2, “Message Interface”.
Example 40.2. Message Interface
// Access the message body Object getBody(); <T> T getBody(Class<T> type); void setBody(Object body); <T> void setBody(Object body, Class<T> type); // Access message headers Object getHeader(String name); <T> T getHeader(String name, Class<T> type); void setHeader(String name, Object value); Object removeHeader(String name); Map<String, Object> getHeaders(); void setHeaders(Map<String, Object> headers); // Access message attachments javax.activation.DataHandler getAttachment(String id); java.util.Map<String, javax.activation.DataHandler> getAttachments(); java.util.Set<String> getAttachmentNames(); void addAttachment(String id, javax.activation.DataHandler content) // Access the message ID String getMessageId(); void setMessageId(String messageId);
For a complete description of the methods in the
Message
interface, see Section 50.1, “The Message Interface”.
Lazy creation of bodies, headers, and attachments
Apache Camel supports lazy creation of bodies, headers, and attachments. This means that the objects that represent a message body, a message header, or a message attachment are not created until they are needed.
For example, consider the following route that accesses the
foo
message header from the In message:
from("SourceURL") .filter(header("foo") .isEqualTo("bar")) .to("TargetURL");
In this route, if we assume that the component referenced by SourceURL supports lazy creation, the In message headers are not actually parsed until the
header("foo")
call is executed. At that point, the underlying message implementation parses the headers and populates the header map. The message body is not parsed until you reach the end of the route, at the to("TargetURL")
call. At that point, the body is converted into the format required for writing it to the target endpoint, TargetURL.
By waiting until the last possible moment before populating the bodies, headers, and attachments, you can ensure that unnecessary type conversions are avoided. In some cases, you can completely avoid parsing. For example, if a route contains no explicit references to message headers, a message could traverse the route without ever parsing the headers.
Whether or not lazy creation is implemented in practice depends on the underlying component implementation. In general, lazy creation is valuable for those cases where creating a message body, a message header, or a message attachment is expensive. For details about implementing a message type that supports lazy creation, see Section 50.2, “Implementing the Message Interface”.
Lazy creation of message IDs
Apache Camel supports lazy creation of message IDs. That is, a message ID is generated only when you actually call the
getMessageId()
method. The DefaultExchange.getExchangeId()
implementation of this method delegates ID generation to the UUID generator that is registered with the CamelContext
.
Some endpoint implementations would call the
getMessageId()
method implicitly, if the endpoint implements a protocol that requires a unique message ID. In particular, JMS messages normally include a header containing unique message ID, so the JMS component automatically calls getMessageId()
to obtain the message ID (this is controlled by the messageIdEnabled
option on the JMS endpoint).
For details of how to register UUID generators with the
CamelContext
, see Section 40.4, “Built-In UUID Generators”.
Initial message format
The initial format of an In message is determined by the source endpoint, and the initial format of an Out message is determined by the target endpoint. If lazy creation is supported by the underlying component, the message remains unparsed until it is accessed explicitly by the application. Most Apache Camel components create the message body in a relatively raw form—for example, representing it using types such as
byte[]
, ByteBuffer
, InputStream
, or OutputStream
. This ensures that the overhead required for creating the initial message is minimal. Where more elaborate message formats are required components usually rely on type converters or marshalling processors.
Type converters
It does not matter what the initial format of the message is, because you can easily convert a message from one format to another using the built-in type converters (see Section 40.3, “Built-In Type Converters”). There are various methods in the Apache Camel API that expose type conversion functionality. For example, the
convertBodyTo(Class type)
method can be inserted into a route to convert the body of an In message, as follows:
from("SourceURL").convertBodyTo(String.class).to("TargetURL");
Where the body of the In message is converted to a
java.lang.String
. The following example shows how to append a string to the end of the In message body:
from("SourceURL").setBody(bodyAs(String.class).append("My Special Signature")).to("TargetURL");
Where the message body is converted to a string format before appending a string to the end. It is not necessary to convert the message body explicitly in this example. You can also use:
from("SourceURL").setBody(body().append("My Special Signature")).to("TargetURL");
Where the
append()
method automatically converts the message body to a string before appending its argument.
Type conversion methods in Message
The
org.apache.camel.Message
interface exposes some methods that perform type conversion explicitly:
getBody(Class<T> type)
—Returns the message body as type,T
.getHeader(String name, Class<T> type)
—Returns the named header value as type,T
.
For the complete list of supported conversion types, see Section 40.3, “Built-In Type Converters”.
Converting to XML
In addition to supporting conversion between simple types (such as
byte[]
, ByteBuffer
, String
, and so on), the built-in type converter also supports conversion to XML formats. For example, you can convert a message body to the org.w3c.dom.Document
type. This conversion is more expensive than the simple conversions, because it involves parsing the entire message and then creating a tree of nodes to represent the XML document structure. You can convert to the following XML document types:
org.w3c.dom.Document
javax.xml.transform.sax.SAXSource
XML type conversions have narrower applicability than the simpler conversions. Because not every message body conforms to an XML structure, you have to remember that this type conversion might fail. On the other hand, there are many scenarios where a router deals exclusively with XML message types.
Marshalling and unmarshalling
Marshalling involves converting a high-level format to a low-level format, and unmarshalling involves converting a low-level format to a high-level format. The following two processors are used to perform marshalling or unmarshalling in a route:
marshal()
unmarshal()
For example, to read a serialized Java object from a file and unmarshal it into a Java object, you could use the route definition shown in Example 40.3, “Unmarshalling a Java Object”.
Example 40.3. Unmarshalling a Java Object
from("file://tmp/appfiles/serialized") .unmarshal() .serialization() .<FurtherProcessing> .to("TargetURL");
Final message format
When an In message reaches the end of a route, the target endpoint must be able to convert the message body into a format that can be written to the physical endpoint. The same rule applies to Out messages that arrive back at the source endpoint. This conversion is usually performed implicitly, using the Apache Camel type converter. Typically, this involves converting from a low-level format to another low-level format, such as converting from a
byte[]
array to an InputStream
type.