Again, I assume that you already have a basic familiarity with Akka, Apache Camel and the akka-camel integration module. If you are new to it, you may want to read the Akka and Camel chapter (free pdf) of the Camel in Action book or the Introduction section of the official akka-camel documentation first.
Akka producer actors can send messages to any Camel endpoint, provided that the corresponding Camel component is on the classpath. This allows Akka actors to interact with external systems or other components over a large number of protocols and APIs.
Let's start with a simple producer actor that sends all messages it receives to an external HTTP service and returns the response to the initial sender. For sending messages over HTTP we can use the Camel jetty component which features an asynchronous HTTP client.
Concrete producer actors inherit a default implementation of
Producertrait. For simple use cases, only an endpoint URI must be defined. Producer actors also require a started
CamelContextManagerfor working properly. A
CamelContextManageris started when an application starts a
CamelServiceManager.startCamelServiceor when starting the
The latter approach is recommended when an application uses only producer actors but no consumer actors. This slightly reduces the overhead when starting actors. After starting the producer actor, clients can interact with the HTTP service via the actor API.
!!is used for sending the message and waiting for a response. Alternatively, one can also use
!together with an implicit sender reference.
In this case the
senderwill receive an asynchronous reply from the producer actor. Before, the producer actor itself receives an asynchronous reply from the jetty endpoint. The asynchronous jetty endpoint doesn't block a thread waiting for a response and the producer actor doesn't do that either. This is important from a scalability perspective, especially for longer-running request-response cycles.
By default, a producer actor initiates an in-out message exchange with its Camel endpoint i.e. it expects a response from it. If a producer actor wants to initiate an in-only message exchange then it must override the
onewaymethod to return
true. The following example shows a producer actor that initiates an in-only message exchange with a JMS endpoint.
This actor adds any message it receives to the
testJMS queue. By default, producer actors that are configured with
oneway = truedon't reply. This behavior is defined in the
Producer.receiveAfterProducemethod which is implemented as follows.
receiveAfterProducemethod has the same signature as
Actor.receiveand is called with the result of the message exchange with the endpoint (please note that in-only message exchanges with Camel endpoints have a result as well). The result type for successful message exchanges is
Message, for failed message exchanges it is
Concrete producer actors can override this method. For example, the following producer actor overrides
onReceiveAfterProduceto reply with a constant
The result of the message exchange with the JMS endpoint is ignored (
Messages exchanges with a Camel endpoint can fail. In this case,
onReceiveAfterProduceis called with a Failure message containing the cause of the failure (a
Throwable). Let's extend the
HttpProducerusage example to deal with failure responses.
In addition to a failure cause, a
Failuremessage can also contain endpoint-specific headers with failure details such as the HTTP response code, for example. When using
!!, together with an implicit sender reference (as shown in the previous section), that sender will then receive the
Failuremessage asynchronously. The
JmsReplyingProducerexample can also be extended to return more meaningful responses: a
"done"message only on success and an error message on failure.
Failed message exchanges never cause the producer actor to throw an exception during execution of
Producerimplementations want to throw an exception on failure (for whatever reason) they can do so in
In this case failure handling should be done in combination with a supervisor (see below).
Let's look at another example. What if we want
to throw an exception on failure (instead of returning a
Failuremessage) but to respond with a normal
Messageon success? In this case, we need to use
onReceiveAfterProduceand complete it with an exception.
Another option to deal with message exchange results inside
onReceiveAfterProduceis to forward them to another actor. Forwarding a message also forwards the initial sender reference. This allows the receiving actor to reply to the initial sender.
With producer actors that forward message exchange results to other actors (incl. other producer actors) one can build actor-based message processing pipelines that integrate external systems. In combination with consumer actors, this could be extended towards a scalable and distributed enterprise service bus (ESB) based on Akka actors ... but this is a topic for another blog post.
The Producer trait also supports correlation identifiers. This allows clients to correlate request messages with asynchronous response messages. A correlation identifier is a message header that can be set by clients. The following example uses the correlation identifier (or message exchange identifier)
An asynchronous response (
httpProducerwill contain that correlation identifier as well.
A failed message exchange by default does not cause a producer actor to throw an exception. However, concrete producer actors may decide to throw an exception inside
onReceiveAfterProduce, for example, or there can be a system-level Camel problem that causes a runtime exception. An application that wants to handle these exceptions should supervise its producer actors.
The following example shows how to implement a producer actor that replies to the initial sender with a
Failuremessage when it is restarted or stopped by a supervisor.
To handle restart callbacks, producer actors must override the
preRestartProducermethod instead of
preRestartmethod is implemented by the
Producertrait and does additional resource de-allocation work after calling
preRestartProducer. More information about replies within
postStopcan be found in my previous blog post about consumer actors.