Remove akka-camel (#26768)

* Remove akka-camel

This module has been deprecated since 2.5.0 and will now be removed in 2.6.

If there is interest it can be moved to a separate community-maintained repo.

* Add note to migration guide

* Remove from allModules as well
This commit is contained in:
Arnout Engelen 2019-04-25 15:55:25 +02:00 committed by Patrik Nordwall
parent 39b344c508
commit 96c8ef4257
80 changed files with 12 additions and 6538 deletions

View file

@ -870,7 +870,6 @@ private[akka] class ActorSystemImpl(
"akka-actor-testkit-typed",
"akka-actor-typed",
"akka-agent",
"akka-camel",
"akka-cluster",
"akka-cluster-metrics",
"akka-cluster-sharding",

View file

@ -1,50 +0,0 @@
####################################
# Akka Camel Reference Config File #
####################################
# This is the reference config file that contains all the default settings.
# Make your edits/overrides in your application.conf.
akka {
camel {
# FQCN of the ContextProvider to be used to create or locate a CamelContext
# it must implement akka.camel.ContextProvider and have a no-arg constructor
# the built-in default create a fresh DefaultCamelContext
context-provider = akka.camel.DefaultContextProvider
# Whether JMX should be enabled or disabled for the Camel Context
jmx = off
# enable/disable streaming cache on the Camel Context
streamingCache = on
consumer {
# Configured setting which determines whether one-way communications
# between an endpoint and this consumer actor
# should be auto-acknowledged or application-acknowledged.
# This flag has only effect when exchange is in-only.
auto-ack = on
# When endpoint is out-capable (can produce responses) reply-timeout is the
# maximum time the endpoint can take to send the response before the message
# exchange fails. This setting is used for out-capable, in-only,
# manually acknowledged communication.
reply-timeout = 1m
# The duration of time to await activation of an endpoint.
activation-timeout = 10s
}
producer {
# The id of the dispatcher to use for producer child actors, i.e. the actor that
# interacts with the Camel endpoint. Some endpoints may be blocking and then it
# can be good to define a dedicated dispatcher.
# If not defined the producer child actor is using the same dispatcher as the
# parent producer actor.
use-dispatcher = ""
}
#Scheme to FQCN mappings for CamelMessage body conversions
conversions {
"file" = "java.io.InputStream"
}
}
}

View file

@ -1,34 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import akka.util.Timeout
import akka.actor.{ ActorRef }
import scala.concurrent.{ ExecutionContext, Future }
/**
* Activation trait that can be used to wait on activation or de-activation of Camel endpoints.
* The Camel endpoints are activated asynchronously. This trait can signal when an endpoint is activated or de-activated.
*/
trait Activation {
/**
* Produces a Future with the specified endpoint that will be completed when the endpoint has been activated,
* or if it times out, which will happen after the specified Timeout.
*
* @param endpoint the endpoint to be activated
* @param timeout the timeout for the Future
*/
def activationFutureFor(endpoint: ActorRef)(implicit timeout: Timeout, executor: ExecutionContext): Future[ActorRef]
/**
* Produces a Future which will be completed when the given endpoint has been deactivated or
* or if it times out, which will happen after the specified Timeout.
*
* @param endpoint the endpoint to be deactivated
* @param timeout the timeout of the Future
*/
def deactivationFutureFor(endpoint: ActorRef)(implicit timeout: Timeout, executor: ExecutionContext): Future[ActorRef]
}

View file

@ -1,14 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
/**
* Thrown to indicate that the actor referenced by an endpoint URI cannot be
* found in the actor system.
*
*/
class ActorNotRegisteredException(uri: String) extends RuntimeException {
override def getMessage: String = "Actor [%s] doesn't exist".format(uri)
}

View file

@ -1,49 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import akka.actor.ActorRef
import akka.camel.internal.component.CamelPath
import org.apache.camel.model.ProcessorDefinition
import scala.concurrent.duration.Duration
/**
* Wraps a [[org.apache.camel.model.ProcessorDefinition]].
* There is an implicit conversion in the `akka.camel` package object that converts a `ProcessorDefinition` into `this` type.
* Because of this conversion, it is possible to use an [[akka.actor.ActorRef]] as a `to` parameter in building a route:
* {{{
* class TestRoute(system: ActorSystem) extends RouteBuilder {
* val responder = system.actorOf(Props[TestResponder], name = "TestResponder")
*
* def configure {
* from("direct:producer").to(responder)
* }
* }
* }}}
* @param definition the processor definition
*/
class ActorRouteDefinition[T <: ProcessorDefinition[T]](definition: ProcessorDefinition[T]) {
/**
* Sends the message to an ActorRef endpoint.
* @param actorRef the actorRef to the actor.
* @return the path to the actor, as a camel uri String
*/
def to(actorRef: ActorRef): T = definition.to(CamelPath.toUri(actorRef))
/**
* Sends the message to an ActorRef endpoint
* @param actorRef the consumer
* @param autoAck Determines whether one-way communications between an endpoint and this consumer actor
* should be auto-acknowledged or application-acknowledged.
* This flag has only effect when exchange is in-only.
* @param replyTimeout When endpoint is out-capable (can produce responses) replyTimeout is the maximum time
* the endpoint can take to send the response before the message exchange fails. It defaults to 1 minute.
* This setting is used for out-capable, in-only, manually acknowledged communication.
* @return the path to the actor, as a camel uri String
*/
def to(actorRef: ActorRef, autoAck: Boolean, replyTimeout: Duration): T =
definition.to(CamelPath.toUri(actorRef, autoAck, replyTimeout))
}

View file

@ -1,155 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import akka.camel.internal._
import akka.actor._
import akka.ConfigurationException
import org.apache.camel.ProducerTemplate
import org.apache.camel.impl.DefaultCamelContext
import org.apache.camel.model.RouteDefinition
import com.typesafe.config.Config
import scala.concurrent.duration.FiniteDuration
import scala.collection.immutable
/**
* Camel trait encapsulates the underlying camel machinery.
* '''Note:''' `CamelContext` and `ProducerTemplate` are stopped when the associated actor system is shut down.
* This trait can be obtained through the [[akka.camel.CamelExtension]] object.
*/
trait Camel extends Extension with Activation {
/**
* Underlying camel context.
*
* It can be used to configure camel manually, i.e. when the user wants to add new routes or endpoints,
* i.e. {{{camel.context.addRoutes(...)}}}
*
* @see [[org.apache.camel.impl.DefaultCamelContext]]
*/
def context: DefaultCamelContext
/**
* The Camel ProducerTemplate.
* @see [[org.apache.camel.ProducerTemplate]]
*/
def template: ProducerTemplate
/**
* The settings for the CamelExtension
*/
def settings: CamelSettings
/**
* INTERNAL API
* Returns the camel supervisor actor.
*/
private[camel] def supervisor: ActorRef
/**
* INTERNAL API
* Returns the associated ActorSystem.
*/
private[camel] def system: ActorSystem
}
/**
* Settings for the Camel Extension
* @param config the config
*/
class CamelSettings private[camel] (config: Config, dynamicAccess: DynamicAccess) {
import akka.util.Helpers.ConfigOps
/**
* Configured setting for how long the actor should wait for activation before it fails.
*/
final val ActivationTimeout: FiniteDuration = config.getMillisDuration("akka.camel.consumer.activation-timeout")
/**
* Configured setting, when endpoint is out-capable (can produce responses) replyTimeout is the maximum time
* the endpoint can take to send the response before the message exchange fails.
* This setting is used for out-capable, in-only, manually acknowledged communication.
*/
final val ReplyTimeout: FiniteDuration = config.getMillisDuration("akka.camel.consumer.reply-timeout")
/**
* Configured setting which determines whether one-way communications between an endpoint and this consumer actor
* should be auto-acknowledged or application-acknowledged.
* This flag has only effect when exchange is in-only.
*/
final val AutoAck: Boolean = config.getBoolean("akka.camel.consumer.auto-ack")
final val JmxStatistics: Boolean = config.getBoolean("akka.camel.jmx")
/**
* enables or disables streamingCache on the Camel Context
*/
final val StreamingCache: Boolean = config.getBoolean("akka.camel.streamingCache")
final val ProducerChildDispatcher: String = config.getString("akka.camel.producer.use-dispatcher")
final val Conversions: (String, RouteDefinition) => RouteDefinition = {
val specifiedConversions = {
import scala.collection.JavaConverters.asScalaSetConverter
val section = config.getConfig("akka.camel.conversions")
section.entrySet.asScala.map(e => (e.getKey, section.getString(e.getKey)))
}
val conversions = specifiedConversions.foldLeft(Map[String, Class[_ <: AnyRef]]()) {
case (m, (key, fqcn)) =>
m.updated(
key,
dynamicAccess
.getClassFor[AnyRef](fqcn)
.recover {
case e =>
throw new ConfigurationException("Could not find/load Camel Converter class [" + fqcn + "]", e)
}
.get)
}
(s: String, r: RouteDefinition) => conversions.get(s).fold(r)(r.convertBodyTo)
}
/**
* Configured setting, determine the class used to load/retrieve the instance of the Camel Context
*/
final val ContextProvider: ContextProvider = {
val fqcn = config.getString("akka.camel.context-provider")
dynamicAccess
.createInstanceFor[ContextProvider](fqcn, immutable.Seq.empty)
.recover {
case e => throw new ConfigurationException("Could not find/load Context Provider class [" + fqcn + "]", e)
}
.get
}
}
/**
* This class can be used to get hold of an instance of the Camel class bound to the actor system.
* <p>For example:
* {{{
* val system = ActorSystem("some system")
* val camel = CamelExtension(system)
* camel.context.addRoutes(...)
* }}}
*
* @see akka.actor.ExtensionId
* @see akka.actor.ExtensionIdProvider
*/
object CamelExtension extends ExtensionId[Camel] with ExtensionIdProvider {
/**
* Creates a new instance of Camel and makes sure it gets stopped when the actor system is shutdown.
*/
override def createExtension(system: ExtendedActorSystem): Camel = {
val camel = new DefaultCamel(system).start()
system.registerOnTermination(camel.shutdown())
camel
}
override def lookup(): ExtensionId[Camel] = CamelExtension
override def get(system: ActorSystem): Camel = super.get(system)
}

View file

@ -1,325 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import java.util.{ Map => JMap, Set => JSet }
import javax.activation.DataHandler
import org.apache.camel.{ CamelContext, Message => JCamelMessage, StreamCache }
import akka.AkkaException
import scala.reflect.ClassTag
import scala.runtime.ScalaRunTime
import scala.util.Try
import akka.dispatch.Mapper
import scala.collection.JavaConverters._
/**
* An immutable representation of a Camel message.
*/
@deprecated(
"Akka Camel is deprecated in favour of 'Alpakka', the Akka Streams based collection of integrations to various endpoints (including Camel).",
since = "2.5.0")
class CamelMessage(val body: Any, val headers: Map[String, Any], val attachments: Map[String, DataHandler])
extends Serializable
with Product {
def this(body: Any, headers: JMap[String, Any]) =
this(body, headers.asScala.toMap, Map.empty[String, DataHandler]) //Java
def this(body: Any, headers: JMap[String, Any], attachments: JMap[String, DataHandler]) =
this(body, headers.asScala.toMap, attachments.asScala.toMap) //Java
def this(body: Any, headers: Map[String, Any]) = this(body, headers.toMap, Map.empty[String, DataHandler])
def copy(body: Any = this.body, headers: Map[String, Any] = this.headers): CamelMessage =
CamelMessage(body, headers, this.attachments)
override def toString: String = "CamelMessage(%s, %s, %s)".format(body, headers, attachments)
/**
* Returns those headers from this message whose name is contained in <code>names</code>.
*/
def headers(names: Set[String]): Map[String, Any] = headers.filterKeys(names).toMap
/**
* Java API: Returns those headers from this message whose name is contained in <code>names</code>.
* The returned headers map is backed up by an immutable headers map. Any attempt to modify
* the returned map will throw an exception.
*/
def getHeaders(names: JSet[String]): JMap[String, Any] = headers(names.asScala.toSet).asJava
/**
* Java API: Returns all headers from this message. The returned headers map is backed up by this
* message's immutable headers map. Any attempt to modify the returned map will throw an
* exception.
*/
def getHeaders: JMap[String, Any] = headers.asJava
/**
* Java API: Creates a new CamelMessage with given <code>headers</code>. A copy of the headers map is made.
*/
def withHeaders[A](headers: JMap[String, A]): CamelMessage = copy(this.body, headers.asScala.toMap)
/**
* Returns the header by given <code>name</code> parameter in a [[scala.util.Try]]. The header is converted to type <code>T</code>, which is returned
* in a [[scala.util.Success]]. If an exception occurs during the conversion to the type <code>T</code> or when the header cannot be found,
* the exception is returned in a [[scala.util.Failure]].
*
* The CamelContext is accessible in a [[akka.camel.javaapi.UntypedConsumerActor]] and [[akka.camel.javaapi.UntypedProducerActor]]
* using the `getCamelContext` method, and is available on the [[akka.camel.CamelExtension]].
*
*/
def headerAs[T](name: String)(implicit t: ClassTag[T], camelContext: CamelContext): Try[T] =
Try(
headers
.get(name)
.map(camelContext.getTypeConverter.mandatoryConvertTo[T](t.runtimeClass.asInstanceOf[Class[T]], _))
.getOrElse(throw new NoSuchElementException(name)))
/**
* Java API: Returns the header by given <code>name</code> parameter. The header is converted to type <code>T</code> as defined by the <code>clazz</code> parameter.
* An exception is thrown when the conversion to the type <code>T</code> fails or when the header cannot be found.
*
* The CamelContext is accessible in a [[akka.camel.javaapi.UntypedConsumerActor]] and [[akka.camel.javaapi.UntypedProducerActor]]
* using the `getCamelContext` method, and is available on the [[akka.camel.CamelExtension]].
*/
def getHeaderAs[T](name: String, clazz: Class[T], camelContext: CamelContext): T =
headerAs[T](name)(ClassTag(clazz), camelContext).get
/**
* Returns a new CamelMessage with a transformed body using a <code>transformer</code> function.
* This method will throw a [[java.lang.ClassCastException]] if the body cannot be mapped to type A.
*/
def mapBody[A, B](transformer: A => B): CamelMessage = copy(body = transformer(body.asInstanceOf[A]))
/**
* Java API: Returns a new CamelMessage with a transformed body using a <code>transformer</code> function.
* This method will throw a [[java.lang.ClassCastException]] if the body cannot be mapped to type A.
*/
def mapBody[A, B](transformer: Mapper[A, B]): CamelMessage = copy(body = transformer(body.asInstanceOf[A]))
/**
* Returns the body of the message converted to the type <code>T</code>. Conversion is done
* using Camel's type converter. The type converter is obtained from the CamelContext that is passed in.
* The CamelContext is accessible in a [[akka.camel.javaapi.UntypedConsumerActor]] and [[akka.camel.javaapi.UntypedProducerActor]]
* using the `getCamelContext` method, and is available on the [[akka.camel.CamelExtension]].
*/
def bodyAs[T](implicit t: ClassTag[T], camelContext: CamelContext): T =
getBodyAs(t.runtimeClass.asInstanceOf[Class[T]], camelContext)
/**
* Java API: Returns the body of the message converted to the type as given by the <code>clazz</code>
* parameter. Conversion is done using Camel's type converter. The type converter is obtained
* from the CamelContext that is passed in.
* <p>
* The CamelContext is accessible in a [[akka.camel.javaapi.UntypedConsumerActor]] and [[akka.camel.javaapi.UntypedProducerActor]]
* using the `getCamelContext` method, and is available on the [[akka.camel.CamelExtension]].
*/
def getBodyAs[T](clazz: Class[T], camelContext: CamelContext): T = {
val result = camelContext.getTypeConverter.mandatoryConvertTo[T](clazz, body)
// to be able to re-read a StreamCache we must "undo" the side effect by resetting the StreamCache
resetStreamCache()
result
}
/**
* Reset StreamCache body. Nothing is done if the body is not a StreamCache.
* See http://camel.apache.org/stream-caching.html
*/
def resetStreamCache(): Unit = body match {
case stream: StreamCache => stream.reset
case _ =>
}
/**
* Java API: Returns a new CamelMessage with a new body, while keeping the same headers.
*/
def withBody[T](body: T): CamelMessage = copy(body = body)
/**
* Creates a CamelMessage with current <code>body</code> converted to type <code>T</code>.
* The CamelContext is accessible in a [[akka.camel.javaapi.UntypedConsumerActor]] and [[akka.camel.javaapi.UntypedProducerActor]]
* using the `getCamelContext` method, and is available on the [[akka.camel.CamelExtension]].
*/
def withBodyAs[T](implicit t: ClassTag[T], camelContext: CamelContext): CamelMessage =
withBodyAs(t.runtimeClass.asInstanceOf[Class[T]])
/**
* Java API: Creates a CamelMessage with current <code>body</code> converted to type <code>clazz</code>.
* <p>
* The CamelContext is accessible in a [[akka.camel.javaapi.UntypedConsumerActor]] and [[akka.camel.javaapi.UntypedProducerActor]]
* using the `getCamelContext` method, and is available on the [[akka.camel.CamelExtension]].
*/
def withBodyAs[T](clazz: Class[T])(implicit camelContext: CamelContext): CamelMessage =
copy(body = getBodyAs(clazz, camelContext))
/**
* Returns those attachments from this message whose name is contained in <code>names</code>.
*/
def attachments(names: Set[String]): Map[String, DataHandler] = attachments.filterKeys(names).toMap
/**
* Java API: Returns those attachments from this message whose name is contained in <code>names</code>.
* The returned headers map is backed up by an immutable headers map. Any attempt to modify
* the returned map will throw an exception.
*/
def getAttachments(names: JSet[String]): JMap[String, DataHandler] = attachments(names.asScala.toSet).asJava
/**
* Java API: Returns all attachments from this message. The returned attachments map is backed up by this
* message's immutable headers map. Any attempt to modify the returned map will throw an
* exception.
*/
def getAttachments: JMap[String, DataHandler] = attachments.asJava
/**
* Java API: Creates a new CamelMessage with given <code>attachments</code>. A copy of the attachments map is made.
*/
def withAttachments(attachments: JMap[String, DataHandler]): CamelMessage =
CamelMessage(this.body, this.headers, attachments.asScala.toMap)
/**
* SCALA API: Creates a new CamelMessage with given <code>attachments</code>.
*/
def withAttachments(attachments: Map[String, DataHandler]): CamelMessage =
CamelMessage(this.body, this.headers, attachments)
/**
* Indicates whether some other object is "equal to" this one.
*/
override def equals(that: Any): Boolean =
that match {
case that: CamelMessage if canEqual(that) =>
this.body == that.body &&
this.headers == that.headers &&
this.attachments == that.attachments
case _ => false
}
/**
* Returns a hash code value for the object.
*/
override def hashCode(): Int = ScalaRunTime._hashCode(this)
/**
* Returns the n-th element of this product, 0-based.
*/
override def productElement(n: Int): Any = n match {
case 0 => body
case 1 => headers
case 2 => attachments
}
/**
* Returns the size of this product.
*/
override def productArity: Int = 3
/**
* Indicates if some other object can be compared (based on type).
* This method should be called from every well-designed equals method that is open to be overridden in a subclass.
*/
override def canEqual(that: Any): Boolean = that match {
case _: CamelMessage => true
case _ => false
}
}
/**
* Companion object of CamelMessage class.
*/
object CamelMessage extends ((Any, Map[String, Any]) => CamelMessage) {
/**
* Returns a new CamelMessage based on the <code>body</code> and <code>headers</code>.
*/
def apply(body: Any, headers: Map[String, Any]): CamelMessage =
new CamelMessage(body, headers, Map.empty[String, DataHandler])
/**
* Returns a new CamelMessage based on the <code>body</code>, <code>headers</code> and <code>attachments</code>.
*/
def apply(body: Any, headers: Map[String, Any], attachments: Map[String, DataHandler]): CamelMessage =
new CamelMessage(body, headers, attachments)
/**
* Returns <code>Some(body, headers)</code>.
*/
def unapply(camelMessage: CamelMessage): Option[(Any, Map[String, Any])] =
Some((camelMessage.body, camelMessage.headers))
/**
* CamelMessage header to correlate request with response messages. Applications that send
* messages to a Producer actor may want to set this header on the request message
* so that it can be correlated with an asynchronous response. Messages send to Consumer
* actors have this header already set.
*/
val MessageExchangeId = "MessageExchangeId" //Deliberately without type ascription to make it a constant
/**
* Creates a canonical form of the given message <code>msg</code>. If <code>msg</code> of type
* CamelMessage then <code>msg</code> is returned, otherwise <code>msg</code> is set as body of a
* newly created CamelMessage object.
*/
private[camel] def canonicalize(msg: Any) = msg match {
case mobj: CamelMessage => mobj
case body => CamelMessage(body, Map.empty[String, Any])
}
/**
* Creates a new CamelMessage object from the Camel message.
*
* @param headers additional headers to set on the created CamelMessage in addition to those
* in the Camel message.
*/
private[camel] def from(camelMessage: JCamelMessage, headers: Map[String, Any]): CamelMessage =
CamelMessage(
camelMessage.getBody,
headers ++ camelMessage.getHeaders.asScala,
camelMessage.getAttachments.asScala.toMap)
/**
* Creates a new CamelMessageWithAttachments object from the Camel message.
*
* @param headers additional headers to set on the created CamelMessageWithAttachments in addition to those
* in the Camel message.
* @param attachments additional attachments to set on the created CamelMessageWithAttachments in addition to those
* in the Camel message.
*/
private[camel] def from(
camelMessage: JCamelMessage,
headers: Map[String, Any],
attachments: Map[String, DataHandler]): CamelMessage =
CamelMessage(
camelMessage.getBody,
headers ++ camelMessage.getHeaders.asScala,
attachments ++ camelMessage.getAttachments.asScala)
/**
* INTERNAL API
* copies the content of this CamelMessageWithAttachments to an Apache Camel Message.
*/
private[camel] def copyContent(from: CamelMessage, to: JCamelMessage): Unit = {
to.setBody(from.body)
for ((name, value) <- from.headers) to.getHeaders.put(name, value.asInstanceOf[AnyRef])
to.getAttachments.putAll(from.getAttachments)
}
}
/**
* Positive acknowledgement message (used for application-acknowledged message receipts).
* When `autoAck` is set to false in the [[akka.camel.Consumer]], you can send an `Ack` to the sender of the CamelMessage.
*/
case object Ack {
/** Java API to get the Ack singleton */
def getInstance = this
}
/**
* An exception indicating that the exchange to the camel endpoint failed.
* It contains the failure cause obtained from Exchange.getException and the headers from either the Exchange.getIn
* message or Exchange.getOut message, depending on the exchange pattern.
*/
class AkkaCamelException private[akka] (cause: Throwable, val headers: Map[String, Any])
extends AkkaException(cause.getMessage, cause) {
def this(cause: Throwable) = this(cause, Map.empty)
}

View file

@ -1,23 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import akka.actor.Actor
private[camel] trait CamelSupport { this: Actor =>
/**
* INTERNAL API
* Returns a [[akka.camel.Camel]] trait which provides access to the CamelExtension.
*/
protected val camel = CamelExtension(context.system)
/**
* Returns the CamelContext.
* The camelContext is defined implicit for simplifying the use of CamelMessage from the Scala API.
*/
protected implicit def camelContext = camel.context
}

View file

@ -1,117 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import akka.camel.internal.CamelSupervisor.Register
import org.apache.camel.model.{ ProcessorDefinition, RouteDefinition }
import akka.actor._
import scala.concurrent.duration._
import akka.dispatch.Mapper
/**
* Mixed in by Actor implementations that consume message from Camel endpoints.
*/
@deprecated(
"Akka Camel is deprecated in favour of 'Alpakka', the Akka Streams based collection of integrations to various endpoints (including Camel).",
since = "2.5.0")
trait Consumer extends Actor with CamelSupport {
import Consumer._
/**
* Must return the Camel endpoint URI that the consumer wants to consume messages from.
*/
def endpointUri: String
/**
* Registers the consumer endpoint. Note: when overriding this method, be sure to
* call 'super.preRestart', otherwise the consumer endpoint will not be registered.
*/
override def preStart(): Unit = {
super.preStart()
// Possible FIXME. registering the endpoint here because of problems
// with order of execution of trait body in the Java version (UntypedConsumerActor)
// where getEndpointUri is called before its constructor (where a uri is set to return from getEndpointUri)
// and remains null. CustomRouteTest provides a test to verify this.
register()
}
private[this] def register(): Unit = {
camel.supervisor ! Register(
self,
endpointUri,
Some(ConsumerConfig(activationTimeout, replyTimeout, autoAck, onRouteDefinition)))
}
/**
* How long the actor should wait for activation before it fails.
*/
def activationTimeout: FiniteDuration = camel.settings.ActivationTimeout
/**
* When endpoint is out-capable (can produce responses) replyTimeout is the maximum time
* the endpoint can take to send the response before the message exchange fails. It defaults to 1 minute.
* This setting is used for out-capable, in-only, manually acknowledged communication.
*/
def replyTimeout: FiniteDuration = camel.settings.ReplyTimeout
/**
* Determines whether one-way communications between an endpoint and this consumer actor
* should be auto-acknowledged or application-acknowledged.
* This flag has only effect when exchange is in-only.
*/
def autoAck: Boolean = camel.settings.AutoAck
/**
* Returns the route definition handler for creating a custom route to this consumer.
* By default it returns an identity function, override this method to
* return a custom route definition handler. The returned function is not allowed to close over 'this', meaning it is
* not allowed to refer to the actor instance itself, since that can easily cause concurrent shared state issues.
*/
def onRouteDefinition: RouteDefinition => ProcessorDefinition[_] = {
val mapper = getRouteDefinitionHandler
if (mapper != identityRouteMapper) mapper.apply _
else identityRouteMapper
}
/**
* Java API: Returns the [[akka.dispatch.Mapper]] function that will be used as a route definition handler
* for creating custom route to this consumer. By default it returns an identity function, override this method to
* return a custom route definition handler. The [[akka.dispatch.Mapper]] is not allowed to close over 'this', meaning it is
* not allowed to refer to the actor instance itself, since that can easily cause concurrent shared state issues.
*/
def getRouteDefinitionHandler: Mapper[RouteDefinition, ProcessorDefinition[_]] = identityRouteMapper
}
/**
* Internal use only.
*/
private[camel] object Consumer {
val identityRouteMapper = new Mapper[RouteDefinition, ProcessorDefinition[_]]() {
override def checkedApply(rd: RouteDefinition): ProcessorDefinition[_] = rd
}
}
/**
* INTERNAL API
* Captures the configuration of the Consumer.
*
* Was a case class but has been split up as a workaround for SI-8283
*/
private[camel] class ConsumerConfig(
val activationTimeout: FiniteDuration,
val replyTimeout: FiniteDuration,
val autoAck: Boolean,
val onRouteDefinition: RouteDefinition => ProcessorDefinition[_])
extends NoSerializationVerificationNeeded
with scala.Serializable
private[camel] object ConsumerConfig {
def apply(
activationTimeout: FiniteDuration,
replyTimeout: FiniteDuration,
autoAck: Boolean,
onRouteDefinition: RouteDefinition => ProcessorDefinition[_]): ConsumerConfig =
new ConsumerConfig(activationTimeout, replyTimeout, autoAck, onRouteDefinition)
}

View file

@ -1,29 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import akka.actor.ExtendedActorSystem
import org.apache.camel.impl.DefaultCamelContext
/**
* Implement this interface in order to inject a specific CamelContext in a system
* An instance of this class must be instantiable using a no-arg constructor.
*/
trait ContextProvider {
/**
* Retrieve or create a Camel Context for the given actor system
* Called once per actor system
*/
def getContext(system: ExtendedActorSystem): DefaultCamelContext
}
/**
* Default implementation of [[akka.camel.ContextProvider]]
* Provides a new DefaultCamelContext per actor system
*/
final class DefaultContextProvider extends ContextProvider {
override def getContext(system: ExtendedActorSystem) = new DefaultCamelContext
}

View file

@ -1,189 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import akka.actor.{ Actor, ActorRef, NoSerializationVerificationNeeded, Props }
import internal.CamelSupervisor.{ CamelProducerObjects, Register }
import internal.CamelExchangeAdapter
import akka.actor.Status.Failure
import org.apache.camel.{ AsyncCallback, Endpoint, ExchangePattern }
import org.apache.camel.processor.SendProcessor
/**
* Support trait for producing messages to Camel endpoints.
*/
trait ProducerSupport extends Actor with CamelSupport {
private[this] var messages = Vector.empty[(ActorRef, Any)]
private[this] var producerChild: Option[ActorRef] = None
override def preStart(): Unit = {
super.preStart()
register()
}
private[this] def register(): Unit = { camel.supervisor ! Register(self, endpointUri) }
/**
* CamelMessage headers to copy by default from request message to response-message.
*/
private val headersToCopyDefault: Set[String] = Set(CamelMessage.MessageExchangeId)
/**
* If set to false (default), this producer expects a response message from the Camel endpoint.
* If set to true, this producer initiates an in-only message exchange with the Camel endpoint
* (fire and forget).
*/
def oneway: Boolean = false
/**
* Returns the Camel endpoint URI to produce messages to.
*/
def endpointUri: String
/**
* Returns the names of message headers to copy from a request message to a response message.
* By default only the CamelMessage.MessageExchangeId is copied. Applications may override this to
* define an application-specific set of message headers to copy.
*/
def headersToCopy: Set[String] = headersToCopyDefault
/**
* Produces <code>msg</code> to the endpoint specified by <code>endpointUri</code>. Before the message is
* actually sent it is pre-processed by calling <code>transformOutgoingMessage</code>. If <code>oneway</code>
* is <code>true</code>, an in-only message exchange is initiated, otherwise an in-out message exchange.
*
* @see Producer#produce
*/
protected def produce: Receive = {
case CamelProducerObjects(endpoint, processor) =>
if (producerChild.isEmpty) {
val disp = camel.settings.ProducerChildDispatcher match {
case "" => context.props.dispatcher
case d => d
}
producerChild = Some(context.actorOf(Props(new ProducerChild(endpoint, processor)).withDispatcher(disp)))
messages = {
for (child <- producerChild;
(snd, msg) <- messages) child.tell(transformOutgoingMessage(msg), snd)
Vector.empty
}
}
case res: MessageResult => routeResponse(res.message)
case res: FailureResult =>
val e = new AkkaCamelException(res.cause, res.headers)
routeResponse(Failure(e))
throw e
case msg =>
producerChild match {
case Some(child) => child.forward(transformOutgoingMessage(msg))
case None => messages :+= ((sender(), msg))
}
}
/**
* Called before the message is sent to the endpoint specified by <code>endpointUri</code>. The original
* message is passed as argument. By default, this method simply returns the argument but may be overridden
* by subtraits or subclasses.
*/
protected def transformOutgoingMessage(msg: Any): Any = msg
/**
* Called before the response message is sent to the original sender. The original
* message is passed as argument. By default, this method simply returns the argument but may be overridden
* by subtraits or subclasses.
*/
protected def transformResponse(msg: Any): Any = msg
/**
* Called after a response was received from the endpoint specified by <code>endpointUri</code>. The
* response is passed as argument. By default, this method sends the response back to the original sender
* if <code>oneway</code> is <code>false</code>. If <code>oneway</code> is <code>true</code>, nothing is
* done. This method may be overridden by subtraits or subclasses (e.g. to forward responses to another
* actor).
*/
protected def routeResponse(msg: Any): Unit = if (!oneway) sender() ! transformResponse(msg)
private class ProducerChild(endpoint: Endpoint, processor: SendProcessor) extends Actor {
def receive = {
case msg @ (_: FailureResult | _: MessageResult) => context.parent.forward(msg)
case msg => produce(endpoint, processor, msg, if (oneway) ExchangePattern.InOnly else ExchangePattern.InOut)
}
/**
* Initiates a message exchange of given <code>pattern</code> with the endpoint specified by
* <code>endpointUri</code>. The in-message of the initiated exchange is the canonical form
* of <code>msg</code>. After sending the in-message, the processing result (response) is passed
* as argument to <code>receiveAfterProduce</code>. If the response is received synchronously from
* the endpoint then <code>receiveAfterProduce</code> is called synchronously as well. If the
* response is received asynchronously, the <code>receiveAfterProduce</code> is called
* asynchronously. The original sender is preserved.
*
* @see CamelMessage#canonicalize
* @param endpoint the endpoint
* @param processor the processor
* @param msg message to produce
* @param pattern exchange pattern
*/
protected def produce(endpoint: Endpoint, processor: SendProcessor, msg: Any, pattern: ExchangePattern): Unit = {
// Need copies of sender reference here since the callback could be done
// later by another thread.
val producer = context.parent
val originalSender = sender()
val xchg = new CamelExchangeAdapter(endpoint.createExchange(pattern))
val cmsg = CamelMessage.canonicalize(msg)
xchg.setRequest(cmsg)
processor.process(
xchg.exchange,
new AsyncCallback {
// Ignoring doneSync, sending back async uniformly.
def done(doneSync: Boolean): Unit =
producer.tell(
if (xchg.exchange.isFailed) xchg.toFailureResult(cmsg.headers(headersToCopy))
else MessageResult(xchg.toResponseMessage(cmsg.headers(headersToCopy))),
originalSender)
})
}
}
}
/**
* Mixed in by Actor implementations to produce messages to Camel endpoints.
*/
@deprecated(
"Akka Camel is deprecated in favour of 'Alpakka', the Akka Streams based collection of integrations to various endpoints (including Camel).",
since = "2.5.0")
trait Producer extends ProducerSupport { this: Actor =>
/**
* Implementation of Actor.receive. Any messages received by this actor
* will be produced to the endpoint specified by <code>endpointUri</code>.
*/
final def receive: Actor.Receive = produce
}
/**
* INTERNAL API
*/
private final case class MessageResult(message: CamelMessage) extends NoSerializationVerificationNeeded
/**
* INTERNAL API
*/
private final case class FailureResult(cause: Throwable, headers: Map[String, Any] = Map.empty)
extends NoSerializationVerificationNeeded
/**
* A one-way producer.
*
*
*/
@deprecated(
"Akka Camel is deprecated in favour of 'Alpakka', the Akka Streams based collection of integrations to various endpoints (including Camel).",
since = "2.5.0")
trait Oneway extends Producer { this: Actor =>
override def oneway: Boolean = true
}

View file

@ -1,70 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal
import akka.actor.ActorRef
private[camel] object ActivationProtocol {
/**
* Super class of all activation messages. Registration of the Camel [[akka.camel.Consumer]]s and [[akka.camel.Producer]]s
* is done asynchronously. Activation messages are sent in the Camel extension when endpoints are
* activated, de-activated, failed to activate and failed to de-activate.
* You can use the [[akka.camel.Activation]] trait which is available on [[akka.camel.Camel]]
* to await activation or de-activation of endpoints.
*/
@SerialVersionUID(1L)
private[camel] abstract class ActivationMessage(val actor: ActorRef) extends Serializable
/**
* INTERNAL API
* companion object of <code>ActivationMessage</code>
*/
private[camel] object ActivationMessage {
def unapply(msg: ActivationMessage): Option[ActorRef] = Option(msg.actor)
}
/**
* INTERNAL API
* Event message indicating that a single endpoint has been activated.
* You can use the [[akka.camel.Activation]] trait which is available on [[akka.camel.Camel]]
* to await activation or de-activation of endpoints.
* @param actorRef the endpoint that was activated
*/
@SerialVersionUID(1L)
final case class EndpointActivated(actorRef: ActorRef) extends ActivationMessage(actorRef)
/**
* INTERNAL API
* Event message indicating that a single endpoint failed to activate.
* You can use the [[akka.camel.Activation]] trait which is available on [[akka.camel.Camel]]
* to await activation or de-activation of endpoints.
* @param actorRef the endpoint that failed to activate
* @param cause the cause for failure
*/
@SerialVersionUID(1L)
final case class EndpointFailedToActivate(actorRef: ActorRef, cause: Throwable) extends ActivationMessage(actorRef)
/**
* INTERNAL API
* Event message indicating that a single endpoint was de-activated.
* You can use the [[akka.camel.Activation]] trait which is available on [[akka.camel.Camel]]
* to await activation or de-activation of endpoints.
* @param actorRef the endpoint that was de-activated
*/
@SerialVersionUID(1L)
final case class EndpointDeActivated(actorRef: ActorRef) extends ActivationMessage(actorRef)
/**
* INTERNAL API
* Event message indicating that a single endpoint failed to de-activate.
* You can use the [[akka.camel.Activation]] trait which is available on [[akka.camel.Camel]]
* to await activation or de-activation of endpoints.
* @param actorRef the endpoint that failed to de-activate
* @param cause the cause for failure
*/
@SerialVersionUID(1L)
final case class EndpointFailedToDeActivate(actorRef: ActorRef, cause: Throwable) extends ActivationMessage(actorRef)
}

View file

@ -1,126 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal
import akka.actor._
import collection.mutable.WeakHashMap
import akka.camel.internal.ActivationProtocol._
/**
* INTERNAL API
* An actor that tracks activation and de-activation of endpoints.
*/
private[camel] class ActivationTracker extends Actor with ActorLogging {
val activations = new WeakHashMap[ActorRef, ActivationStateMachine]
/**
* A state machine that keeps track of the endpoint activation status of an actor.
*/
class ActivationStateMachine {
type State = PartialFunction[ActivationMessage, Unit]
var receive: State = notActivated()
/**
* Not activated state
* @return a partial function that handles messages in the 'not activated' state
*/
def notActivated(): State = {
var awaitingActivation = List[ActorRef]()
var awaitingDeActivation = List[ActorRef]()
{
case AwaitActivation(ref) => awaitingActivation ::= sender()
case AwaitDeActivation(ref) => awaitingDeActivation ::= sender()
case msg @ EndpointActivated(ref) =>
awaitingActivation.foreach(_ ! msg)
receive = activated(awaitingDeActivation)
case EndpointFailedToActivate(ref, cause) =>
awaitingActivation.foreach(_ ! EndpointFailedToActivate(ref, cause))
receive = failedToActivate(cause)
}
}
/**
* Activated state.
* @param currentAwaitingDeActivation the current <code>ActorRef</code>s awaiting de-activation
* @return a partial function that handles messages in the 'activated' state
*/
def activated(currentAwaitingDeActivation: List[ActorRef]): State = {
var awaitingDeActivation = currentAwaitingDeActivation
{
case AwaitActivation(ref) => sender() ! EndpointActivated(ref)
case AwaitDeActivation(ref) => awaitingDeActivation ::= sender()
case msg @ EndpointDeActivated(ref) =>
awaitingDeActivation.foreach(_ ! msg)
receive = deactivated
case msg @ EndpointFailedToDeActivate(ref, cause) =>
awaitingDeActivation.foreach(_ ! msg)
receive = failedToDeActivate(cause)
}
}
/**
* De-activated state
* @return a partial function that handles messages in the 'de-activated' state
*/
def deactivated: State = {
// deactivated means it was activated at some point, so tell sender() it was activated
case AwaitActivation(ref) => sender() ! EndpointActivated(ref)
case AwaitDeActivation(ref) => sender() ! EndpointDeActivated(ref)
//resurrected at restart.
case msg @ EndpointActivated(ref) =>
receive = activated(Nil)
}
/**
* Failed to activate state
* @param cause the cause for the failure
* @return a partial function that handles messages in 'failed to activate' state
*/
def failedToActivate(cause: Throwable): State = {
case AwaitActivation(ref) => sender() ! EndpointFailedToActivate(ref, cause)
case AwaitDeActivation(ref) => sender() ! EndpointFailedToActivate(ref, cause)
case EndpointDeActivated(_) => // the de-register at termination always sends a de-activated when the cleanup is done. ignoring.
}
/**
* Failed to de-activate state
* @param cause the cause for the failure
* @return a partial function that handles messages in 'failed to de-activate' state
*/
def failedToDeActivate(cause: Throwable): State = {
case AwaitActivation(ref) => sender() ! EndpointActivated(ref)
case AwaitDeActivation(ref) => sender() ! EndpointFailedToDeActivate(ref, cause)
case EndpointDeActivated(_) => // the de-register at termination always sends a de-activated when the cleanup is done. ignoring.
}
}
override def receive = {
case msg @ ActivationMessage(ref) =>
activations.getOrElseUpdate(ref, new ActivationStateMachine).receive.orElse(logStateWarning(ref))(msg)
}
private[this] def logStateWarning(actorRef: ActorRef): Receive = {
case msg => log.warning("Message [{}] not expected in current state of actor [{}]", msg, actorRef)
}
}
/**
* INTERNAL API
* A request message to the ActivationTracker for the status of activation.
* @param ref the actorRef
*/
private[camel] final case class AwaitActivation(ref: ActorRef) extends ActivationMessage(ref)
/**
* INTERNAL API
* A request message to the ActivationTracker for the status of de-activation.
* @param ref the actorRef
*/
private[camel] final case class AwaitDeActivation(ref: ActorRef) extends ActivationMessage(ref)

View file

@ -1,124 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal
import org.apache.camel.util.ExchangeHelper
import org.apache.camel.{ Exchange, Message => JCamelMessage }
import akka.camel.{ AkkaCamelException, CamelMessage, FailureResult }
/**
* INTERNAL API
* Adapter for converting an [[org.apache.camel.Exchange]] to and from [[akka.camel.CamelMessage]] and [[akka.camel.FailureResult]] objects.
* The org.apache.camel.Message is mutable and not suitable to be used directly as messages between Actors.
* This adapter is used to convert to immutable messages to be used with Actors, and convert the immutable messages back
* to org.apache.camel.Message when using Camel.
*/
private[camel] class CamelExchangeAdapter(val exchange: Exchange) {
/**
* Returns the exchange id
*/
def getExchangeId: String = exchange.getExchangeId
/**
* Returns if the exchange is out capable.
*/
def isOutCapable: Boolean = exchange.getPattern.isOutCapable
/**
* Sets Exchange.getIn from the given CamelMessage object.
*/
def setRequest(msg: CamelMessage): Unit = CamelMessage.copyContent(msg, request)
/**
* Depending on the exchange pattern, sets Exchange.getIn or Exchange.getOut from the given
* CamelMessage object. If the exchange is out-capable then the Exchange.getOut is set, otherwise
* Exchange.getIn.
*/
def setResponse(msg: CamelMessage): Unit = CamelMessage.copyContent(msg, response)
/**
* Sets Exchange.getException from the given FailureResult message. Headers of the FailureResult message
* are ignored.
*/
def setFailure(msg: FailureResult): Unit = exchange.setException(msg.cause)
/**
* Creates an immutable CamelMessage object from Exchange.getIn so it can be used with Actors.
*/
def toRequestMessage: CamelMessage = toRequestMessage(Map.empty)
/**
* Depending on the exchange pattern, creates an immutable CamelMessage object from Exchange.getIn or Exchange.getOut so it can be used with Actors.
* If the exchange is out-capable then the Exchange.getOut is set, otherwise Exchange.getIn.
*/
def toResponseMessage: CamelMessage = toResponseMessage(Map.empty)
/**
* Creates an AkkaCamelException object from the adapted Exchange.
* The cause of the AkkaCamelException is set to the exception on the adapted Exchange.
*
* Depending on the exchange pattern, puts the headers from Exchange.getIn or Exchange.getOut
* on the AkkaCamelException.
*
* If the exchange is out-capable then the headers of Exchange.getOut are used, otherwise the headers of Exchange.getIn are used.
*/
def toAkkaCamelException: AkkaCamelException = toAkkaCamelException(Map.empty)
/**
* Creates an AkkaCamelException object from the adapted Exchange.
* The cause of the AkkaCamelException is set to the exception on the adapted Exchange.
*
* Depending on the exchange pattern, adds the supplied headers and the headers from Exchange.getIn or Exchange.getOut
* together and passes these to the AkkaCamelException.
*
* If the exchange is out-capable then the headers of Exchange.getOut are used, otherwise the headers of Exchange.getIn are used.
*
* @param headers additional headers to set on the exception in addition to those
* in the exchange.
*/
def toAkkaCamelException(headers: Map[String, Any]): AkkaCamelException = {
import scala.collection.JavaConverters._
new AkkaCamelException(exchange.getException, headers ++ response.getHeaders.asScala)
}
/**
* Creates an immutable Failure object from the adapted Exchange so it can be used internally between Actors.
*/
def toFailureMessage: FailureResult = toFailureResult(Map.empty)
/**
* Creates an immutable FailureResult object from the adapted Exchange so it can be used internally between Actors.
*
* @param headers additional headers to set on the created CamelMessage in addition to those
* in the Camel message.
*/
def toFailureResult(headers: Map[String, Any]): FailureResult = {
import scala.collection.JavaConverters._
FailureResult(exchange.getException, headers ++ response.getHeaders.asScala)
}
/**
* Creates an immutable CamelMessage object from Exchange.getIn so it can be used with Actors.
*
* @param headers additional headers to set on the created CamelMessage in addition to those
* in the Camel message.
*/
def toRequestMessage(headers: Map[String, Any]): CamelMessage = CamelMessage.from(request, headers)
/**
* Depending on the exchange pattern, creates an immutable CamelMessage object from Exchange.getIn or Exchange.getOut so it can be used with Actors.
* If the exchange is out-capable then the Exchange.getOut is set, otherwise Exchange.getIn.
*
* @param headers additional headers to set on the created CamelMessage in addition to those
* in the Camel message.
*/
def toResponseMessage(headers: Map[String, Any]): CamelMessage = CamelMessage.from(response, headers)
private def request: JCamelMessage = exchange.getIn
private def response: JCamelMessage = ExchangeHelper.getResultMessage(exchange)
}

View file

@ -1,227 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal
import akka.actor._
import akka.camel.{ CamelSupport, ConsumerConfig }
import org.apache.camel.Endpoint
import org.apache.camel.processor.SendProcessor
import scala.util.control.NonFatal
import akka.actor.Terminated
import akka.actor.SupervisorStrategy.Resume
import akka.camel.internal.CamelSupervisor._
import akka.AkkaException
import akka.camel.internal.ActivationProtocol._
import akka.event.Logging
/**
* INTERNAL API
* Top level supervisor for internal Camel actors
*/
private[camel] class CamelSupervisor extends Actor with CamelSupport {
private val activationTracker = context.actorOf(Props[ActivationTracker], "activationTracker")
private val registry: ActorRef = context.actorOf(Props(classOf[Registry], activationTracker), "registry")
override val supervisorStrategy = OneForOneStrategy() {
case NonFatal(e) =>
Resume
}
def receive = {
case AddWatch(actorRef) => context.watch(actorRef)
case Terminated(actorRef) => registry ! DeRegister(actorRef)
case msg: ActivationMessage => activationTracker.forward(msg)
case msg => registry.forward(msg)
}
}
/**
* INTERNAL API
* Messages for the camel supervisor, registrations and de-registrations.
*/
private[camel] object CamelSupervisor {
@SerialVersionUID(1L)
sealed trait CamelSupervisorMessage extends Serializable
/**
* INTERNAL API
* Registers a consumer or a producer.
*/
final case class Register(actorRef: ActorRef, endpointUri: String, config: Option[ConsumerConfig] = None)
extends NoSerializationVerificationNeeded
/**
* INTERNAL API
* De-registers a producer or a consumer.
*/
@SerialVersionUID(1L)
final case class DeRegister(actorRef: ActorRef) extends CamelSupervisorMessage
/**
* INTERNAL API
* Adds a watch for the actor
*/
@SerialVersionUID(1L)
final case class AddWatch(actorRef: ActorRef) extends CamelSupervisorMessage
/**
* INTERNAL API
* Provides a Producer with the required camel objects to function.
*/
final case class CamelProducerObjects(endpoint: Endpoint, processor: SendProcessor)
extends NoSerializationVerificationNeeded
}
/**
* INTERNAL API
* Thrown by registrars to indicate that the actor could not be de-activated.
*/
private[camel] class ActorDeActivationException(val actorRef: ActorRef, cause: Throwable)
extends AkkaException(s"$actorRef failed to de-activate", cause)
/**
* INTERNAL API
* Thrown by the registrars to indicate that the actor could not be activated.
*/
private[camel] class ActorActivationException(val actorRef: ActorRef, cause: Throwable)
extends AkkaException(s"$actorRef failed to activate", cause)
/**
* INTERNAL API
* Registry for Camel Consumers and Producers. Supervises the registrars.
*/
private[camel] class Registry(activationTracker: ActorRef) extends Actor with CamelSupport {
import context.{ parent, stop }
private val producerRegistrar =
context.actorOf(Props(classOf[ProducerRegistrar], activationTracker), "producerRegistrar")
private val consumerRegistrar =
context.actorOf(Props(classOf[ConsumerRegistrar], activationTracker), "consumerRegistrar")
private var producers = Set[ActorRef]()
private var consumers = Set[ActorRef]()
class RegistryLogStrategy()(_decider: SupervisorStrategy.Decider) extends OneForOneStrategy()(_decider) {
override def logFailure(
context: ActorContext,
child: ActorRef,
cause: Throwable,
decision: SupervisorStrategy.Directive): Unit =
cause match {
case _: ActorActivationException | _: ActorDeActivationException =>
try context.system.eventStream.publish {
Logging.Error(cause.getCause, child.path.toString, getClass, cause.getMessage)
} catch { case NonFatal(_) => }
case _ => super.logFailure(context, child, cause, decision)
}
}
override val supervisorStrategy = new RegistryLogStrategy()({
case e: ActorActivationException =>
activationTracker ! EndpointFailedToActivate(e.actorRef, e.getCause)
stop(e.actorRef)
Resume
case e: ActorDeActivationException =>
activationTracker ! EndpointFailedToDeActivate(e.actorRef, e.getCause)
stop(e.actorRef)
Resume
case NonFatal(e) =>
Resume
})
def receive = {
case msg @ Register(consumer, _, Some(_)) =>
if (!consumers(consumer)) {
consumers += consumer
consumerRegistrar.forward(msg)
parent ! AddWatch(consumer)
}
case msg @ Register(producer, _, None) =>
if (!producers(producer)) {
producers += producer
parent ! AddWatch(producer)
}
producerRegistrar.forward(msg)
case DeRegister(actorRef) =>
producers.find(_ == actorRef).foreach { p =>
deRegisterProducer(p)
producers -= p
}
consumers.find(_ == actorRef).foreach { c =>
deRegisterConsumer(c)
consumers -= c
}
}
private def deRegisterConsumer(actorRef: ActorRef): Unit = { consumerRegistrar ! DeRegister(actorRef) }
private def deRegisterProducer(actorRef: ActorRef): Unit = { producerRegistrar ! DeRegister(actorRef) }
}
/**
* INTERNAL API
* Registers Producers.
*/
private[camel] class ProducerRegistrar(activationTracker: ActorRef) extends Actor with CamelSupport {
private var camelObjects = Map[ActorRef, (Endpoint, SendProcessor)]()
def receive = {
case Register(producer, endpointUri, _) =>
if (!camelObjects.contains(producer)) {
try {
val endpoint = camelContext.getEndpoint(endpointUri)
val processor = new SendProcessor(endpoint)
camelObjects = camelObjects.updated(producer, endpoint -> processor)
// if this throws, the supervisor stops the producer and de-registers it on termination
processor.start()
producer ! CamelProducerObjects(endpoint, processor)
activationTracker ! EndpointActivated(producer)
} catch {
case NonFatal(e) => throw new ActorActivationException(producer, e)
}
} else {
camelObjects.get(producer).foreach {
case (endpoint, processor) => producer ! CamelProducerObjects(endpoint, processor)
}
}
case DeRegister(producer) =>
camelObjects.get(producer).foreach {
case (_, processor) =>
try {
camelObjects.get(producer).foreach(_._2.stop())
camelObjects -= producer
activationTracker ! EndpointDeActivated(producer)
} catch {
case NonFatal(e) => throw new ActorDeActivationException(producer, e)
}
}
}
}
/**
* INTERNAL API
* Registers Consumers.
*/
private[camel] class ConsumerRegistrar(activationTracker: ActorRef) extends Actor with CamelSupport {
def receive = {
case Register(consumer, endpointUri, Some(consumerConfig)) =>
try {
// if this throws, the supervisor stops the consumer and de-registers it on termination
camelContext.addRoutes(new ConsumerActorRouteBuilder(endpointUri, consumer, consumerConfig, camel.settings))
activationTracker ! EndpointActivated(consumer)
} catch {
case NonFatal(e) => throw new ActorActivationException(consumer, e)
}
case DeRegister(consumer) =>
try {
val route = consumer.path.toString
camelContext.stopRoute(route)
camelContext.removeRoute(route)
activationTracker ! EndpointDeActivated(consumer)
} catch {
case NonFatal(e) => throw new ActorDeActivationException(consumer, e)
}
}
}

View file

@ -1,39 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal
import akka.actor._
import akka.camel._
import akka.camel.internal.component.CamelPath
import org.apache.camel.builder.RouteBuilder
import org.apache.camel.model.RouteDefinition
import scala.language.existentials
/**
* INTERNAL API
* Builder of a route to a target which can be an actor.
*
* @param endpointUri endpoint URI of the consumer actor.
*
*
*/
private[camel] class ConsumerActorRouteBuilder(
endpointUri: String,
consumer: ActorRef,
config: ConsumerConfig,
settings: CamelSettings)
extends RouteBuilder {
protected def targetActorUri = CamelPath.toUri(consumer, config.autoAck, config.replyTimeout)
def configure(): Unit =
applyUserRouteCustomization(
settings.Conversions.apply(
endpointUri.take(endpointUri.indexOf(":")), // e.g. "http" from "http://whatever/..."
from(endpointUri).routeId(consumer.path.toString))).to(targetActorUri)
def applyUserRouteCustomization(rd: RouteDefinition) = config.onRouteDefinition(rd)
}

View file

@ -1,109 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal
import akka.camel.internal.component.{ ActorComponent, DurationTypeConverter }
import org.apache.camel.impl.DefaultCamelContext
import scala.Predef._
import akka.event.Logging
import akka.camel.{ Camel, CamelSettings }
import akka.camel.internal.ActivationProtocol._
import scala.util.control.NonFatal
import scala.concurrent.duration._
import org.apache.camel.ProducerTemplate
import scala.concurrent.{ ExecutionContext, Future }
import akka.util.Timeout
import akka.pattern.ask
import akka.actor.{ ActorRef, ExtendedActorSystem, Props }
/**
* INTERNAL API
* Creates an instance of the Camel subsystem.
*
* @param system is used to create internal actors needed by camel instance.
* Camel doesn't maintain the lifecycle of this actor system. The actor system has to be shut down by the user.
* In the typical scenario, when camel is used with akka extension, it is natural that camel reuses the actor system it extends.
* Also by not creating extra internal actor system we are conserving resources.
*/
private[camel] class DefaultCamel(val system: ExtendedActorSystem) extends Camel {
val supervisor = system.actorOf(Props[CamelSupervisor], "camel-supervisor")
private[camel] implicit val log = Logging(system, getClass.getName)
lazy val context: DefaultCamelContext = {
val ctx = settings.ContextProvider.getContext(system)
if (!settings.JmxStatistics) ctx.disableJMX()
ctx.setName(system.name)
ctx.setStreamCaching(settings.StreamingCache)
ctx.addComponent("akka", new ActorComponent(this, system))
ctx.getTypeConverterRegistry.addTypeConverter(classOf[FiniteDuration], classOf[String], DurationTypeConverter)
ctx
}
val settings = new CamelSettings(system.settings.config, system.dynamicAccess)
lazy val template: ProducerTemplate = context.createProducerTemplate()
/**
* Starts camel and underlying camel context and template.
* Only the creator of Camel should start and stop it.
* @see akka.camel.internal.DefaultCamel#shutdown
*/
def start(): this.type = {
context.start()
try template.start()
catch { case NonFatal(e) => context.stop(); throw e }
log.debug("Started CamelContext[{}] for ActorSystem[{}]", context.getName, system.name)
this
}
/**
* Stops camel and underlying camel context and template.
* Only the creator of Camel should shut it down.
* There is no need to stop Camel instance, which you get from the CamelExtension, as its lifecycle is bound to the actor system.
*
* @see akka.camel.internal.DefaultCamel#start
*/
def shutdown(): Unit = {
try context.stop()
finally {
try template.stop()
catch {
case NonFatal(e) => log.debug("Swallowing non-fatal exception [{}] on stopping Camel producer template", e)
}
}
log.debug("Stopped CamelContext[{}] for ActorSystem[{}]", context.getName, system.name)
}
/**
* Produces a Future with the specified endpoint that will be completed when the endpoint has been activated,
* or if it times out, which will happen after the specified Timeout.
*
* @param endpoint the endpoint to be activated
* @param timeout the timeout for the Future
*/
def activationFutureFor(endpoint: ActorRef)(implicit timeout: Timeout, executor: ExecutionContext): Future[ActorRef] =
(supervisor
.ask(AwaitActivation(endpoint))(timeout))
.map[ActorRef]({
case EndpointActivated(`endpoint`) => endpoint
case EndpointFailedToActivate(`endpoint`, cause) => throw cause
})
/**
* Produces a Future which will be completed when the given endpoint has been deactivated or
* or if it times out, which will happen after the specified Timeout.
*
* @param endpoint the endpoint to be deactivated
* @param timeout the timeout of the Future
*/
def deactivationFutureFor(
endpoint: ActorRef)(implicit timeout: Timeout, executor: ExecutionContext): Future[ActorRef] =
(supervisor
.ask(AwaitDeActivation(endpoint))(timeout))
.map[ActorRef]({
case EndpointDeActivated(`endpoint`) => endpoint
case EndpointFailedToDeActivate(`endpoint`, cause) => throw cause
})
}

View file

@ -1,286 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal.component
import java.util.{ Map => JMap }
import org.apache.camel._
import org.apache.camel.impl.{ DefaultComponent, DefaultEndpoint, DefaultProducer }
import akka.actor._
import akka.pattern._
import scala.beans.BeanProperty
import scala.concurrent.duration._
import scala.concurrent.{ Future }
import scala.util.control.NonFatal
import java.util.concurrent.{ CountDownLatch, TimeoutException }
import akka.util.Timeout
import akka.camel.internal.CamelExchangeAdapter
import akka.camel.{ Ack, ActorNotRegisteredException, Camel, CamelMessage, FailureResult }
import support.TypeConverterSupport
import scala.util.{ Failure, Success, Try }
/**
* INTERNAL API
* Creates Camel [[org.apache.camel.Endpoint]]s that send messages to [[akka.camel.Consumer]] actors through an [[akka.camel.internal.component.ActorProducer]].
* The `ActorComponent` is a Camel [[org.apache.camel.Component]].
*
* Camel integrates with Akka through this component. The [[akka.camel.internal.DefaultCamel]] module adds the
* `ActorComponent` to the [[org.apache.camel.CamelContext]] under the 'actor' component name.
* Messages are sent to [[akka.camel.Consumer]] actors through a [[akka.camel.internal.component.ActorEndpoint]] that
* this component provides.
*/
private[camel] class ActorComponent(camel: Camel, system: ActorSystem) extends DefaultComponent {
/**
* @see org.apache.camel.Component
*/
def createEndpoint(uri: String, remaining: String, parameters: JMap[String, Object]): ActorEndpoint =
new ActorEndpoint(uri, this, ActorEndpointPath.fromCamelPath(uri), camel)
}
/**
* INTERNAL API
* Does what an endpoint does, creates consumers and producers for the component. The `ActorEndpoint` is a Camel [[org.apache.camel.Endpoint]] that is used to
* receive messages from Camel. Sending messages from the `ActorComponent` is not supported, a [[akka.camel.Producer]] actor should be used instead.
*
* The `ActorEndpoint`s are created by the [[akka.camel.internal.component.ActorComponent]].
*
* Actors are referenced using actor endpoint URIs of the following format:
* <code>[actorPath]?[options]%s</code>,
* where <code>[actorPath]</code> refers to the actor path to the actor.
*/
private[camel] class ActorEndpoint(uri: String, comp: ActorComponent, val path: ActorEndpointPath, val camel: Camel)
extends DefaultEndpoint(uri, comp)
with ActorEndpointConfig {
/**
* The ActorEndpoint only supports receiving messages from Camel.
* The createProducer method (not to be confused with a producer actor) is used to send messages into the endpoint.
* The ActorComponent is only there to send to actors registered through an actor endpoint URI.
* You can use an actor as an endpoint to send to in a camel route (as in, a Camel Consumer Actor). so from(someuri) to (actoruri), but not 'the other way around'.
* Supporting createConsumer would mean that messages are consumed from an Actor endpoint in a route, and an Actor is not necessarily a producer of messages.
* [[akka.camel.Producer]] Actors can be used for sending messages to some other uri/ component type registered in Camel.
* @throws UnsupportedOperationException this method is not supported
*/
def createConsumer(processor: Processor): org.apache.camel.Consumer =
throw new UnsupportedOperationException("actor consumer not supported yet")
/**
* Creates a new producer which is used send messages into the endpoint.
*
* @return a newly created producer
*/
def createProducer: ActorProducer = new ActorProducer(this, camel)
/**
* Returns true.
*/
def isSingleton: Boolean = true
}
/**
* INTERNAL API
* Configures the `ActorEndpoint`. This needs to be a `bean` for Camel purposes.
*/
private[camel] trait ActorEndpointConfig {
def path: ActorEndpointPath
def camel: Camel
@BeanProperty var replyTimeout: FiniteDuration = camel.settings.ReplyTimeout
@BeanProperty var autoAck: Boolean = camel.settings.AutoAck
}
/**
* Sends the in-message of an exchange to an untyped actor, identified by an [[akka.camel.internal.component.ActorEndpoint]]
*
* @see akka.camel.internal.component.ActorComponent
* @see akka.camel.internal.component.ActorEndpoint
*/
private[camel] class ActorProducer(val endpoint: ActorEndpoint, camel: Camel)
extends DefaultProducer(endpoint)
with AsyncProcessor {
/**
* Processes the exchange.
* Calls the synchronous version of the method and waits for the result (blocking).
* @param exchange the exchange to process
*/
def process(exchange: Exchange): Unit = processExchangeAdapter(new CamelExchangeAdapter(exchange))
/**
* Processes the message exchange. the caller supports having the exchange asynchronously processed.
* If there was a failure processing then the caused Exception would be set on the Exchange.
*
* @param exchange the message exchange
* @param callback the AsyncCallback will be invoked when the processing of the exchange is completed.
* If the exchange is completed synchronously, then the callback is also invoked synchronously.
* The callback should therefore be careful of starting recursive loop.
* @return (doneSync) true to continue execute synchronously, false to continue being executed asynchronously
*/
def process(exchange: Exchange, callback: AsyncCallback): Boolean =
processExchangeAdapter(new CamelExchangeAdapter(exchange), callback)
/**
* INTERNAL API
* Processes the [[akka.camel.internal.CamelExchangeAdapter]]
* @param exchange the [[akka.camel.internal.CamelExchangeAdapter]]
*/
private[camel] def processExchangeAdapter(exchange: CamelExchangeAdapter): Unit = {
val isDone = new CountDownLatch(1)
processExchangeAdapter(exchange, new AsyncCallback { def done(doneSync: Boolean): Unit = { isDone.countDown() } })
isDone.await(endpoint.replyTimeout.length, endpoint.replyTimeout.unit)
}
/**
* INTERNAL API
* Processes the [[akka.camel.internal.CamelExchangeAdapter]].
* This method is blocking when the exchange is inOnly. The method returns true if it executed synchronously/blocking.
* @param exchange the [[akka.camel.internal.CamelExchangeAdapter]]
* @param callback the callback
* @return (doneSync) true to continue execute synchronously, false to continue being executed asynchronously
*/
private[camel] def processExchangeAdapter(exchange: CamelExchangeAdapter, callback: AsyncCallback): Boolean = {
if (!exchange.isOutCapable && endpoint.autoAck) {
fireAndForget(messageFor(exchange), exchange)
callback.done(true)
true // done sync
} else {
val action: PartialFunction[Try[Any], Unit] =
if (exchange.isOutCapable) {
case Success(failure: FailureResult) => exchange.setFailure(failure)
case Success(msg) => exchange.setResponse(CamelMessage.canonicalize(msg))
case Failure(e: TimeoutException) =>
exchange.setFailure(
FailureResult(
new TimeoutException(
"Failed to get response from the actor [%s] within timeout [%s]. Check replyTimeout and blocking settings [%s]"
.format(endpoint.path, endpoint.replyTimeout, endpoint))))
case Failure(throwable) => exchange.setFailure(FailureResult(throwable))
} else {
case Success(Ack) => () /* no response message to set */
case Success(failure: FailureResult) => exchange.setFailure(failure)
case Success(msg) =>
exchange.setFailure(
FailureResult(
new IllegalArgumentException(
"Expected Ack or Failure message, but got: [%s] from actor [%s]".format(msg, endpoint.path))))
case Failure(e: TimeoutException) =>
exchange.setFailure(
FailureResult(
new TimeoutException(
"Failed to get Ack or Failure response from the actor [%s] within timeout [%s]. Check replyTimeout and blocking settings [%s]"
.format(endpoint.path, endpoint.replyTimeout, endpoint))))
case Failure(throwable) => exchange.setFailure(FailureResult(throwable))
}
// FIXME #3074 how do we solve this with actorSelection?
val async = try actorFor(endpoint.path).ask(messageFor(exchange))(Timeout(endpoint.replyTimeout))
catch { case NonFatal(e) => Future.failed(e) }
implicit val ec = camel.system.dispatcher // FIXME which ExecutionContext should be used here?
async.onComplete(action.andThen { _ =>
callback.done(false)
})
false
}
}
// FIXME #3074 how do we solve this with actorSelection?
private def fireAndForget(message: CamelMessage, exchange: CamelExchangeAdapter): Unit =
try {
actorFor(endpoint.path) ! message
} catch { case NonFatal(e) => exchange.setFailure(new FailureResult(e)) }
private[this] def actorFor(path: ActorEndpointPath): ActorRef =
path.findActorIn(camel.system).getOrElse(throw new ActorNotRegisteredException(path.actorPath))
private[this] def messageFor(exchange: CamelExchangeAdapter) =
exchange.toRequestMessage(Map(CamelMessage.MessageExchangeId -> exchange.getExchangeId))
}
/**
* INTERNAL API
* Converts Strings to [[scala.concurrent.duration.Duration]]
*/
private[camel] object DurationTypeConverter extends TypeConverterSupport {
@throws(classOf[TypeConversionException])
def convertTo[T](valueType: Class[T], exchange: Exchange, value: AnyRef): T =
valueType.cast(try {
val d = Duration(value.toString)
if (valueType.isInstance(d)) d else null
} catch {
case NonFatal(throwable) => throw new TypeConversionException(value, valueType, throwable)
})
}
/**
* INTERNAL API
* An endpoint to an [[akka.actor.ActorRef]]
* @param actorPath the String representation of the path to the actor
*/
private[camel] case class ActorEndpointPath private (actorPath: String) {
require(actorPath != null)
require(actorPath.length() > 0)
require(actorPath.startsWith("akka://"))
def findActorIn(system: ActorSystem): Option[ActorRef] = {
// FIXME #3074 how do we solve this with actorSelection?
val ref = system.actorFor(actorPath)
if (ref.isTerminated) None else Some(ref)
}
}
/**
* Converts ActorRefs and actorPaths to URI's that point to the actor through the Camel Actor Component.
* Can also be used in the Java API as a helper for custom route builders. the Scala API has an implicit conversion in the camel package to
* directly use `to(actorRef)`. In java you could use `to(CamelPath.toUri(actorRef)`.
* The URI to the actor is exactly the same as the string representation of the ActorPath, except that it can also have optional URI parameters to configure the Consumer Actor.
*/
object CamelPath {
/**
* Converts the actorRef to a Camel URI (string) which can be used in custom routes.
* The created URI will have no parameters, it is purely the string representation of the actor's path.
* @param actorRef the actorRef
* @return the Camel URI to the actor.
*/
def toUri(actorRef: ActorRef): String = actorRef.path.toString
/**
* Converts the actorRef to a Camel URI (string) which can be used in custom routes.
* Use this version of toUri when you know that the actorRef points to a Consumer Actor and you would like to
* set autoAck and replyTimeout parameters to non-default values.
*
* @param actorRef the actorRef
* @param autoAck parameter for a Consumer Actor, see [[akka.camel.ConsumerConfig]]
* @param replyTimeout parameter for a Consumer Actor, see [[akka.camel.ConsumerConfig]]
* @return the Camel URI to the Consumer Actor, including the parameters for auto acknowledgement and replyTimeout.
*/
def toUri(actorRef: ActorRef, autoAck: Boolean, replyTimeout: Duration): String =
"%s?autoAck=%s&replyTimeout=%s".format(actorRef.path.toString, autoAck, replyTimeout.toString)
}
/**
* INTERNAL API
* Companion of `ActorEndpointPath`
*/
private[camel] case object ActorEndpointPath {
def apply(actorRef: ActorRef): ActorEndpointPath = new ActorEndpointPath(actorRef.path.toString)
/**
* Creates an [[akka.camel.internal.component.ActorEndpointPath]] from the uri
* Expects the uri in the akka [[akka.actor.ActorPath]] format, i.e 'akka://system/user/someactor'.
* parameters can be optionally added to the actor path to indicate auto-acknowledgement and replyTimeout for a [[akka.camel.Consumer]] actor.
*/
def fromCamelPath(camelPath: String): ActorEndpointPath = camelPath match {
case id if id.startsWith("akka://") => new ActorEndpointPath(id.split('?')(0))
case _ =>
throw new IllegalArgumentException(
"Invalid path: [%s] - should be an actorPath starting with 'akka://', optionally followed by options".format(
camelPath))
}
}

View file

@ -1,45 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.javaapi
import akka.actor.UntypedActor
import akka.camel._
import org.apache.camel.ProducerTemplate
import org.apache.camel.impl.DefaultCamelContext
/**
* Subclass this abstract class to create an MDB-style untyped consumer actor. This
* class is meant to be used from Java.
*
* @deprecated Akka Camel is deprecated since 2.5.0 in favour of 'Alpakka', the Akka Streams based collection of integrations to various endpoints (including Camel).
*/
@Deprecated
abstract class UntypedConsumerActor extends UntypedActor with Consumer {
final def endpointUri: String = getEndpointUri
/**
* Java API: Returns the Camel endpoint URI to consume messages from.
*/
def getEndpointUri(): String
/**
* Java API: Returns the [[org.apache.camel.impl.DefaultCamelContext]]
* @return the CamelContext
*/
protected def getCamelContext(): DefaultCamelContext = camelContext
/**
* Java API: Returns the [[org.apache.camel.ProducerTemplate]]
* @return the ProducerTemplate
*/
protected def getProducerTemplate(): ProducerTemplate = camel.template
/**
* Java API: Returns the [[akka.camel.Activation]] interface
* that can be used to wait on activation or de-activation of Camel endpoints.
* @return the Activation interface
*/
protected def getActivation(): Activation = camel
}

View file

@ -1,83 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.javaapi
import akka.actor.UntypedActor
import akka.camel._
import org.apache.camel.{ ProducerTemplate }
import org.apache.camel.impl.DefaultCamelContext
/**
* Subclass this abstract class to create an untyped producer actor. This class is meant to be used from Java.
*
* @deprecated Akka Camel is deprecated since 2.5.0 in favour of 'Alpakka', the Akka Streams based collection of integrations to various endpoints (including Camel).
*/
@Deprecated
abstract class UntypedProducerActor extends UntypedActor with ProducerSupport {
/**
* Called before the message is sent to the endpoint specified by <code>getEndpointUri</code>. The original
* message is passed as argument. By default, this method simply returns the argument but may be overridden
* by subclasses.
*/
def onTransformOutgoingMessage(message: AnyRef): AnyRef = message
/**
* Called before the response message is sent to original sender. The original
* message is passed as argument. By default, this method simply returns the argument but may be overridden
* by subclasses.
*/
def onTransformResponse(message: AnyRef): AnyRef = message
/**
* Called after a response was received from the endpoint specified by <code>endpointUri</code>. The
* response is passed as argument. By default, this method sends the response back to the original sender
* if <code>oneway</code> is <code>false</code>. If <code>oneway</code> is <code>true</code>, nothing is
* done. This method may be overridden by subclasses (e.g. to forward responses to another actor).
*/
def onRouteResponse(message: AnyRef): Unit = super.routeResponse(message)
final override def transformOutgoingMessage(msg: Any): AnyRef = onTransformOutgoingMessage(msg.asInstanceOf[AnyRef])
final override def transformResponse(msg: Any): AnyRef = onTransformResponse(msg.asInstanceOf[AnyRef])
final override def routeResponse(msg: Any): Unit = onRouteResponse(msg.asInstanceOf[AnyRef])
final override def endpointUri: String = getEndpointUri
final override def oneway: Boolean = isOneway
/**
* Default implementation of UntypedActor.onReceive
*/
final def onReceive(message: Any): Unit = produce(message)
/**
* Returns the Camel endpoint URI to produce messages to.
*/
def getEndpointUri(): String
/**
* If set to false (default), this producer expects a response message from the Camel endpoint.
* If set to true, this producer communicates with the Camel endpoint with an in-only message
* exchange pattern (fire and forget).
*/
def isOneway(): Boolean = super.oneway
/**
* Returns the <code>CamelContext</code>.
*/
def getCamelContext(): DefaultCamelContext = camel.context
/**
* Returns the <code>ProducerTemplate</code>.
*/
def getProducerTemplate(): ProducerTemplate = camel.template
/**
* ''Java API'': Returns the [[akka.camel.Activation]] interface
* that can be used to wait on activation or de-activation of Camel endpoints.
* @return the Activation interface
*/
def getActivation(): Activation = camel
}

View file

@ -1,22 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka
import language.implicitConversions
import org.apache.camel.model.ProcessorDefinition
package object camel {
/**
* To allow using Actors with the Camel Route DSL:
*
* {{{
* from("file://data/input/CamelConsumer").to(actor)
* }}}
*/
implicit def toActorRouteDefinition[T <: ProcessorDefinition[T]](definition: ProcessorDefinition[T]) =
new ActorRouteDefinition(definition)
}

View file

@ -1,65 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel;
import akka.testkit.AkkaJUnitActorSystemResource;
import akka.testkit.AkkaSpec;
import akka.testkit.javadsl.EventFilter;
import akka.testkit.javadsl.TestKit;
import org.junit.ClassRule;
import org.scalatest.junit.JUnitSuite;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.util.Timeout;
import scala.concurrent.Await;
import scala.concurrent.duration.FiniteDuration;
import scala.concurrent.duration.Duration;
import scala.concurrent.ExecutionContext;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
public class ConsumerJavaTest extends JUnitSuite {
@ClassRule
public static AkkaJUnitActorSystemResource actorSystemResource =
new AkkaJUnitActorSystemResource("ConsumerJavaTest", AkkaSpec.testConf());
private final ActorSystem system = actorSystemResource.getSystem();
@Test
public void shouldHandleExceptionThrownByActorAndGenerateCustomResponse() throws Exception {
new TestKit(system) {
{
String result =
new EventFilter(Exception.class, system)
.occurrences(1)
.intercept(
() -> {
FiniteDuration duration = Duration.create(1, TimeUnit.SECONDS);
Timeout timeout = new Timeout(duration);
Camel camel = CamelExtension.get(system);
ExecutionContext executionContext = system.dispatcher();
try {
Await.result(
camel.activationFutureFor(
system.actorOf(
Props.create(SampleErrorHandlingConsumer.class),
"sample-error-handling-consumer"),
timeout,
executionContext),
duration);
return camel
.template()
.requestBody("direct:error-handler-test-java", "hello", String.class);
} catch (Exception e) {
return e.getMessage();
}
});
assertEquals("error: hello", result);
}
};
}
}

View file

@ -1,263 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel;
import akka.actor.*;
import akka.camel.internal.component.CamelPath;
import akka.camel.javaapi.UntypedConsumerActor;
import akka.camel.javaapi.UntypedProducerActor;
import akka.testkit.AkkaJUnitActorSystemResource;
import akka.util.Timeout;
import org.junit.*;
import org.scalatest.junit.JUnitSuite;
import scala.concurrent.Await;
import scala.concurrent.ExecutionContext;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
import org.apache.camel.CamelExecutionException;
import org.apache.camel.Exchange;
import org.apache.camel.Predicate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import java.util.concurrent.TimeUnit;
public class CustomRouteTest extends JUnitSuite {
@Rule
public AkkaJUnitActorSystemResource actorSystemResource =
new AkkaJUnitActorSystemResource("CustomRouteTest");
private ActorSystem system = null;
private Camel camel = null;
public static class MyActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder().build();
}
}
@Before
public void beforeEach() {
system = actorSystemResource.getSystem();
camel = (Camel) CamelExtension.get(system);
}
@Test
public void testCustomProducerRoute() throws Exception {
MockEndpoint mockEndpoint =
camel.context().getEndpoint("mock:mockProducer", MockEndpoint.class);
ActorRef producer = system.actorOf(Props.create(MockEndpointProducer.class), "mockEndpoint");
camel.context().addRoutes(new CustomRouteBuilder("direct:test", producer));
camel.template().sendBody("direct:test", "test");
assertMockEndpoint(mockEndpoint);
system.stop(producer);
}
@Test
public void testCustomProducerUriRoute() throws Exception {
MockEndpoint mockEndpoint =
camel.context().getEndpoint("mock:mockProducerUri", MockEndpoint.class);
ActorRef producer =
system.actorOf(
Props.create(EndpointProducer.class, "mock:mockProducerUri"), "mockEndpointUri");
camel.context().addRoutes(new CustomRouteBuilder("direct:test", producer));
camel.template().sendBody("direct:test", "test");
assertMockEndpoint(mockEndpoint);
system.stop(producer);
}
@Test
public void testCustomConsumerRoute() throws Exception {
MockEndpoint mockEndpoint =
camel.context().getEndpoint("mock:mockConsumer", MockEndpoint.class);
FiniteDuration duration = Duration.create(10, TimeUnit.SECONDS);
Timeout timeout = new Timeout(duration);
ExecutionContext executionContext = system.dispatcher();
ActorRef consumer =
Await.result(
camel.activationFutureFor(
system.actorOf(Props.create(TestConsumer.class), "testConsumer"),
timeout,
executionContext),
duration);
camel.context().addRoutes(new CustomRouteBuilder("direct:testRouteConsumer", consumer));
camel.template().sendBody("direct:testRouteConsumer", "test");
assertMockEndpoint(mockEndpoint);
system.stop(consumer);
}
@Test
public void testCustomAckConsumerRoute() throws Exception {
MockEndpoint mockEndpoint = camel.context().getEndpoint("mock:mockAck", MockEndpoint.class);
FiniteDuration duration = Duration.create(10, TimeUnit.SECONDS);
Timeout timeout = new Timeout(duration);
ExecutionContext executionContext = system.dispatcher();
ActorRef consumer =
Await.result(
camel.activationFutureFor(
system.actorOf(
Props.create(TestAckConsumer.class, "direct:testConsumerAck", "mock:mockAck"),
"testConsumerAck"),
timeout,
executionContext),
duration);
camel.context().addRoutes(new CustomRouteBuilder("direct:testAck", consumer, false, duration));
camel.template().sendBody("direct:testAck", "test");
assertMockEndpoint(mockEndpoint);
system.stop(consumer);
}
@Test
public void testCustomAckConsumerRouteFromUri() throws Exception {
MockEndpoint mockEndpoint = camel.context().getEndpoint("mock:mockAckUri", MockEndpoint.class);
ExecutionContext executionContext = system.dispatcher();
FiniteDuration duration = Duration.create(10, TimeUnit.SECONDS);
Timeout timeout = new Timeout(duration);
ActorRef consumer =
Await.result(
camel.activationFutureFor(
system.actorOf(
Props.create(
TestAckConsumer.class, "direct:testConsumerAckFromUri", "mock:mockAckUri"),
"testConsumerAckUri"),
timeout,
executionContext),
duration);
camel
.context()
.addRoutes(
new CustomRouteBuilder(
"direct:testAckFromUri",
"akka://CustomRouteTest/user/testConsumerAckUri?autoAck=false"));
camel.template().sendBody("direct:testAckFromUri", "test");
assertMockEndpoint(mockEndpoint);
system.stop(consumer);
}
@Test(expected = CamelExecutionException.class)
public void testCustomTimeoutConsumerRoute() throws Exception {
FiniteDuration duration = Duration.create(10, TimeUnit.SECONDS);
Timeout timeout = new Timeout(duration);
ExecutionContext executionContext = system.dispatcher();
ActorRef consumer =
Await.result(
camel.activationFutureFor(
system.actorOf(
Props.create(
TestAckConsumer.class,
"direct:testConsumerException",
"mock:mockException"),
"testConsumerException"),
timeout,
executionContext),
duration);
camel
.context()
.addRoutes(
new CustomRouteBuilder(
"direct:testException", consumer, false, Duration.create(0, TimeUnit.SECONDS)));
camel.template().sendBody("direct:testException", "test");
}
private void assertMockEndpoint(MockEndpoint mockEndpoint) throws InterruptedException {
mockEndpoint.expectedMessageCount(1);
mockEndpoint.expectedMessagesMatches(
new Predicate() {
@Override
public boolean matches(Exchange exchange) {
return exchange.getIn().getBody().equals("test");
}
});
mockEndpoint.assertIsSatisfied();
}
public static class CustomRouteBuilder extends RouteBuilder {
private final String uri;
private final String fromUri;
public CustomRouteBuilder(String from, String to) {
fromUri = from;
uri = to;
}
public CustomRouteBuilder(String from, ActorRef actor) {
fromUri = from;
uri = CamelPath.toUri(actor);
}
public CustomRouteBuilder(String from, ActorRef actor, boolean autoAck, Duration replyTimeout) {
fromUri = from;
uri = CamelPath.toUri(actor, autoAck, replyTimeout);
}
@Override
public void configure() throws Exception {
from(fromUri).to(uri);
}
}
public static class TestConsumer extends UntypedConsumerActor {
@Override
public String getEndpointUri() {
return "direct:testconsumer";
}
@Override
public void onReceive(Object message) {
this.getProducerTemplate().sendBody("mock:mockConsumer", "test");
}
}
public static class EndpointProducer extends UntypedProducerActor {
private final String uri;
public EndpointProducer(String uri) {
this.uri = uri;
}
public String getEndpointUri() {
return uri;
}
@Override
public boolean isOneway() {
return true;
}
}
public static class MockEndpointProducer extends UntypedProducerActor {
public String getEndpointUri() {
return "mock:mockProducer";
}
@Override
public boolean isOneway() {
return true;
}
}
public static class TestAckConsumer extends UntypedConsumerActor {
private final String myuri;
private final String to;
public TestAckConsumer(String uri, String to) {
myuri = uri;
this.to = to;
}
@Override
public String getEndpointUri() {
return myuri;
}
@Override
public void onReceive(Object message) {
this.getProducerTemplate().sendBody(to, "test");
getSender().tell(Ack.getInstance(), getSelf());
}
}
}

View file

@ -1,136 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel;
import akka.actor.ActorSystem;
import akka.dispatch.Mapper;
import akka.testkit.AkkaJUnitActorSystemResource;
import org.apache.camel.NoTypeConversionAvailableException;
import org.apache.camel.converter.stream.InputStreamCache;
import org.junit.ClassRule;
import org.junit.Test;
import org.scalatest.junit.JUnitSuite;
import java.io.InputStream;
import java.util.*;
import static org.junit.Assert.assertEquals;
/** */
public class MessageJavaTest extends JUnitSuite {
private Map<String, Object> empty = new HashMap<String, Object>();
@ClassRule
public static AkkaJUnitActorSystemResource actorSystemResource =
new AkkaJUnitActorSystemResource("MessageJavaTest");
private final ActorSystem system = actorSystemResource.getSystem();
private Camel camel = (Camel) CamelExtension.get(system);
CamelMessage message(Object body) {
return new CamelMessage(body, new HashMap<String, Object>());
}
CamelMessage message(Object body, Map<String, Object> headers) {
return new CamelMessage(body, headers);
}
@Test
public void shouldConvertDoubleBodyToString() {
assertEquals("1.4", message("1.4", empty).getBodyAs(String.class, camel.context()));
}
@Test(expected = NoTypeConversionAvailableException.class)
public void shouldThrowExceptionWhenConvertingDoubleBodyToInputStream() {
message(1.4).getBodyAs(InputStream.class, camel.context());
}
@Test
public void shouldConvertDoubleHeaderToString() {
CamelMessage message = message("test", createMap("test", 1.4));
assertEquals("1.4", message.getHeaderAs("test", String.class, camel.context()));
}
@Test
public void shouldReturnSubsetOfHeaders() {
CamelMessage message = message("test", createMap("A", "1", "B", "2"));
assertEquals(createMap("B", "2"), message.getHeaders(createSet("B")));
}
@Test(expected = UnsupportedOperationException.class)
public void shouldReturnSubsetOfHeadersUnmodifiable() {
CamelMessage message = message("test", createMap("A", "1", "B", "2"));
message.getHeaders(createSet("B")).put("x", "y");
}
@Test
public void shouldReturnAllHeaders() {
CamelMessage message = message("test", createMap("A", "1", "B", "2"));
assertEquals(createMap("A", "1", "B", "2"), message.getHeaders());
}
@Test(expected = UnsupportedOperationException.class)
public void shouldReturnAllHeadersUnmodifiable() {
CamelMessage message = message("test", createMap("A", "1", "B", "2"));
message.getHeaders().put("x", "y");
}
@Test
public void shouldTransformBodyAndPreserveHeaders() {
assertEquals(
message("ab", createMap("A", "1")),
message("a", createMap("A", "1")).mapBody(new TestTransformer()));
}
@Test
public void shouldConvertBodyAndPreserveHeaders() {
assertEquals(
message("1.4", createMap("A", "1")),
message(1.4, createMap("A", "1")).withBodyAs(String.class, camel.context()));
}
@Test
public void shouldSetBodyAndPreserveHeaders() {
assertEquals(
message("test2", createMap("A", "1")),
message("test1", createMap("A", "1")).withBody("test2"));
}
@Test
public void shouldSetHeadersAndPreserveBody() {
assertEquals(
message("test1", createMap("C", "3")),
message("test1", createMap("A", "1")).withHeaders(createMap("C", "3")));
}
@Test
public void shouldBeAbleToReReadStreamCacheBody() throws Exception {
CamelMessage msg = new CamelMessage(new InputStreamCache("test1".getBytes("utf-8")), empty);
assertEquals("test1", msg.getBodyAs(String.class, camel.context()));
// re-read
assertEquals("test1", msg.getBodyAs(String.class, camel.context()));
}
private static Set<String> createSet(String... entries) {
HashSet<String> set = new HashSet<String>();
set.addAll(Arrays.asList(entries));
return set;
}
private static Map<String, Object> createMap(Object... pairs) {
HashMap<String, Object> map = new HashMap<String, Object>();
for (int i = 0; i < pairs.length; i += 2) {
map.put((String) pairs[i], pairs[i + 1]);
}
return map;
}
private static class TestTransformer extends Mapper<String, String> {
@Override
public String apply(String param) {
return param + "b";
}
}
}

View file

@ -1,53 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel;
import akka.actor.Status;
import akka.camel.javaapi.UntypedConsumerActor;
import akka.dispatch.Mapper;
import scala.concurrent.duration.Duration;
import org.apache.camel.builder.Builder;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.model.RouteDefinition;
import scala.Option;
import scala.concurrent.duration.FiniteDuration;
/** */
public class SampleErrorHandlingConsumer extends UntypedConsumerActor {
private static Mapper<RouteDefinition, ProcessorDefinition<?>> mapper =
new Mapper<RouteDefinition, ProcessorDefinition<?>>() {
public ProcessorDefinition<?> apply(RouteDefinition rd) {
return rd.onException(Exception.class)
.handled(true)
.transform(Builder.exceptionMessage())
.end();
}
};
public String getEndpointUri() {
return "direct:error-handler-test-java";
}
@Override
public Mapper<RouteDefinition, ProcessorDefinition<?>> onRouteDefinition() {
return mapper;
}
@Override
public FiniteDuration replyTimeout() {
return Duration.create(1, "second");
}
public void onReceive(Object message) throws Exception {
CamelMessage msg = (CamelMessage) message;
String body = msg.getBodyAs(String.class, this.getCamelContext());
throw new Exception(String.format("error: %s", body));
}
@Override
public void preRestart(Throwable reason, Option<Object> message) {
getSender().tell(new Status.Failure(reason), getSelf());
}
}

View file

@ -1,22 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel;
import akka.camel.javaapi.UntypedConsumerActor;
/** */
public class SampleUntypedConsumer extends UntypedConsumerActor {
public String getEndpointUri() {
return "direct:test-untyped-consumer";
}
public void onReceive(Object message) {
CamelMessage msg = (CamelMessage) message;
String body = msg.getBodyAs(String.class, getCamelContext());
String header = msg.getHeaderAs("test", String.class, getCamelContext());
sender().tell(String.format("%s %s", body, header), getSelf());
}
}

View file

@ -1,21 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel;
import akka.camel.javaapi.UntypedProducerActor;
/** */
public class SampleUntypedForwardingProducer extends UntypedProducerActor {
public String getEndpointUri() {
return "direct:producer-test-1";
}
@Override
public void onRouteResponse(Object message) {
CamelMessage msg = (CamelMessage) message;
String body = msg.getBodyAs(String.class, getCamelContext());
getProducerTemplate().sendBody("direct:forward-test-1", body);
}
}

View file

@ -1,15 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel;
import akka.camel.javaapi.UntypedProducerActor;
/** */
public class SampleUntypedReplyingProducer extends UntypedProducerActor {
public String getEndpointUri() {
return "direct:producer-test-1";
}
}

View file

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>target/akka-camel.log</file>
<append>true</append>
<encoder>
<pattern>%date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n</pattern>
</encoder>
</appender>
<root level="fatal">
<appender-ref ref="FILE" />
</root>
</configuration>

View file

@ -1,82 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import language.postfixOps
import org.scalatest.Matchers
import scala.concurrent.duration._
import org.apache.camel.ProducerTemplate
import akka.actor._
import TestSupport._
import org.scalatest.WordSpec
import akka.testkit.TestLatch
import scala.concurrent.Await
import java.util.concurrent.TimeoutException
import akka.util.Timeout
class ActivationIntegrationTest extends WordSpec with Matchers with SharedCamelSystem {
val timeoutDuration = 10 seconds
implicit val timeout = Timeout(timeoutDuration)
def template: ProducerTemplate = camel.template
import system.dispatcher
"ActivationAware should be notified when endpoint is activated" in {
val latch = new TestLatch(0)
val actor = system.actorOf(Props(new TestConsumer("direct:actor-1", latch)), "act-direct-actor-1")
Await.result(camel.activationFutureFor(actor), 10 seconds) should ===(actor)
template.requestBody("direct:actor-1", "test") should ===("received test")
}
"ActivationAware should be notified when endpoint is de-activated" in {
val latch = TestLatch(1)
val actor = start(new Consumer {
def endpointUri = "direct:a3"
def receive = { case _ => {} }
override def postStop(): Unit = {
super.postStop()
latch.countDown()
}
}, name = "direct-a3")
Await.result(camel.activationFutureFor(actor), timeoutDuration)
system.stop(actor)
Await.result(camel.deactivationFutureFor(actor), timeoutDuration)
Await.ready(latch, timeoutDuration)
}
"ActivationAware must time out when waiting for endpoint de-activation for too long" in {
val latch = new TestLatch(0)
val actor = start(new TestConsumer("direct:a5", latch), name = "direct-a5")
Await.result(camel.activationFutureFor(actor), timeoutDuration)
intercept[TimeoutException] { Await.result(camel.deactivationFutureFor(actor), 1 millis) }
}
"activationFutureFor must fail if notification timeout is too short and activation is not complete yet" in {
val latch = new TestLatch(1)
val actor = system.actorOf(Props(new TestConsumer("direct:actor-4", latch)), "direct-actor-4")
intercept[TimeoutException] { Await.result(camel.activationFutureFor(actor), 1 millis) }
latch.countDown()
// after the latch is removed, complete the wait for completion so this test does not later on
// print errors because of the registerConsumer timing out.
Await.result(camel.activationFutureFor(actor), timeoutDuration)
}
class TestConsumer(uri: String, latch: TestLatch) extends Consumer {
def endpointUri = uri
override def preStart(): Unit = {
Await.ready(latch, 60 seconds)
super.preStart()
}
override def receive = {
case msg: CamelMessage => sender() ! "received " + msg.body
}
}
}

View file

@ -1,52 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import org.scalatest.Matchers
import org.scalatest.WordSpec
import akka.actor.ActorSystem
import akka.testkit.TestKit
import akka.util.Helpers.ConfigOps
class CamelConfigSpec extends WordSpec with Matchers {
val (settings, config) = {
val system = ActorSystem("CamelConfigSpec")
val result = (CamelExtension(system).settings, system.settings.config)
TestKit.shutdownActorSystem(system)
result
}
"CamelConfigSpec" must {
"have correct activationTimeout config" in {
settings.ActivationTimeout should ===(config.getMillisDuration("akka.camel.consumer.activation-timeout"))
}
"have correct autoAck config" in {
settings.AutoAck should ===(config.getBoolean("akka.camel.consumer.auto-ack"))
}
"have correct replyTimeout config" in {
settings.ReplyTimeout should ===(config.getMillisDuration("akka.camel.consumer.reply-timeout"))
}
"have correct streamingCache config" in {
settings.StreamingCache should ===(config.getBoolean("akka.camel.streamingCache"))
}
"have correct jmxStatistics config" in {
settings.JmxStatistics should ===(config.getBoolean("akka.camel.jmx"))
}
"have correct body conversions config" in {
val conversions = config.getConfig("akka.camel.conversions")
conversions.getString("file") should ===("java.io.InputStream")
conversions.entrySet.size should ===(1)
}
"have correct Context Provider" in {
settings.ContextProvider.isInstanceOf[DefaultContextProvider] should ===(true)
}
}
}

View file

@ -1,142 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import akka.camel.TestSupport.SharedCamelSystem
import akka.camel.internal.CamelExchangeAdapter
import org.apache.camel.impl.DefaultExchange
import org.apache.camel.{ Exchange, ExchangePattern }
import org.scalatest.FunSuite
class CamelExchangeAdapterTest extends FunSuite with SharedCamelSystem {
test("mustSetInMessageFromRequestMessage") {
val e1 = sampleInOnly
exchangeToAdapter(e1).setRequest(CamelMessage("x", Map.empty))
assert(e1.getIn.getBody === "x")
val e2 = sampleInOut
exchangeToAdapter(e2).setRequest(CamelMessage("y", Map.empty))
assert(e2.getIn.getBody === "y")
}
test("mustSetOutMessageFromResponseMessage") {
val e1 = sampleInOut
exchangeToAdapter(e1).setResponse(CamelMessage("y", Map.empty))
assert(e1.getOut.getBody === "y")
}
test("mustSetInMessageFromResponseMessage") {
val e1 = sampleInOnly
exchangeToAdapter(e1).setResponse(CamelMessage("x", Map.empty))
assert(e1.getIn.getBody === "x")
}
test("mustSetExceptionFromFailureMessage") {
val e1 = sampleInOnly
exchangeToAdapter(e1).setFailure(FailureResult(new Exception("test1")))
assert(e1.getException.getMessage === "test1")
val e2 = sampleInOut
exchangeToAdapter(e2).setFailure(FailureResult(new Exception("test2")))
assert(e2.getException.getMessage === "test2")
}
test("mustCreateRequestMessageFromInMessage") {
val m = exchangeToAdapter(sampleInOnly).toRequestMessage
assert(m === CamelMessage("test-in", Map("key-in" -> "val-in")))
}
test("mustCreateResponseMessageFromInMessage") {
val m = exchangeToAdapter(sampleInOnly).toResponseMessage
assert(m === CamelMessage("test-in", Map("key-in" -> "val-in")))
}
test("mustCreateResponseMessageFromOutMessage") {
val m = exchangeToAdapter(sampleInOut).toResponseMessage
assert(m === CamelMessage("test-out", Map("key-out" -> "val-out")))
}
test("mustCreateFailureMessageFromExceptionAndInMessage") {
val e1 = sampleInOnly
e1.setException(new Exception("test1"))
val a1 = exchangeToAdapter(e1)
assert(a1.toAkkaCamelException.getMessage === "test1")
assert(a1.toAkkaCamelException.headers("key-in") === "val-in")
assert(a1.toFailureMessage.cause.getMessage === "test1")
assert(a1.toFailureMessage.headers("key-in") === "val-in")
}
test("mustCreateFailureMessageFromExceptionAndOutMessage") {
val e1 = sampleInOut
e1.setException(new Exception("test2"))
val a1 = exchangeToAdapter(e1)
assert(a1.toAkkaCamelException.getMessage === "test2")
assert(a1.toAkkaCamelException.headers("key-out") === "val-out")
assert(a1.toFailureMessage.cause.getMessage === "test2")
assert(a1.toFailureMessage.headers("key-out") === "val-out")
}
test("mustCreateRequestMessageFromInMessageWithAdditionalHeader") {
val m = exchangeToAdapter(sampleInOnly).toRequestMessage(Map("x" -> "y"))
assert(m === CamelMessage("test-in", Map("key-in" -> "val-in", "x" -> "y")))
}
test("mustCreateResponseMessageFromInMessageWithAdditionalHeader") {
val m = exchangeToAdapter(sampleInOnly).toResponseMessage(Map("x" -> "y"))
assert(m === CamelMessage("test-in", Map("key-in" -> "val-in", "x" -> "y")))
}
test("mustCreateResponseMessageFromOutMessageWithAdditionalHeader") {
val m = exchangeToAdapter(sampleInOut).toResponseMessage(Map("x" -> "y"))
assert(m === CamelMessage("test-out", Map("key-out" -> "val-out", "x" -> "y")))
}
test("mustCreateFailureMessageFromExceptionAndInMessageWithAdditionalHeader") {
val e1 = sampleInOnly
e1.setException(new Exception("test1"))
val a1 = exchangeToAdapter(e1)
assert(a1.toAkkaCamelException.getMessage === "test1")
val headers = a1.toAkkaCamelException(Map("x" -> "y")).headers
assert(headers("key-in") === "val-in")
assert(headers("x") === "y")
val a2 = exchangeToAdapter(e1)
assert(a2.toFailureMessage.cause.getMessage === "test1")
val failureHeaders = a2.toFailureResult(Map("x" -> "y")).headers
assert(failureHeaders("key-in") === "val-in")
assert(failureHeaders("x") === "y")
}
test("mustCreateFailureMessageFromExceptionAndOutMessageWithAdditionalHeader") {
val e1 = sampleInOut
e1.setException(new Exception("test2"))
val a1 = exchangeToAdapter(e1)
assert(a1.toAkkaCamelException.getMessage === "test2")
val headers = a1.toAkkaCamelException(Map("x" -> "y")).headers
assert(headers("key-out") === "val-out")
assert(headers("x") === "y")
val a2 = exchangeToAdapter(e1)
assert(a2.toFailureMessage.cause.getMessage === "test2")
val failureHeaders = a2.toFailureResult(Map("x" -> "y")).headers
assert(failureHeaders("key-out") === "val-out")
assert(failureHeaders("x") === "y")
}
private def sampleInOnly = sampleExchange(ExchangePattern.InOnly)
private def sampleInOut = sampleExchange(ExchangePattern.InOut)
private def sampleExchange(pattern: ExchangePattern) = {
val exchange = new DefaultExchange(camel.context)
exchange.getIn.setBody("test-in")
exchange.getOut.setBody("test-out")
exchange.getIn.setHeader("key-in", "val-in")
exchange.getOut.setHeader("key-out", "val-out")
exchange.setPattern(pattern)
exchange
}
private def exchangeToAdapter(e: Exchange) = new CamelExchangeAdapter(e)
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import java.net.URL
import javax.activation.DataHandler
import org.apache.camel.impl.{ DefaultExchange, DefaultMessage }
import akka.camel.TestSupport.SharedCamelSystem
import org.scalatest.Matchers
import org.scalatest.WordSpecLike
//TODO merge it with MessageScalaTest
class CamelMessageTest extends Matchers with WordSpecLike with SharedCamelSystem {
"CamelMessage copyContent" must {
"create a new CamelMessage with additional headers, attachments and new body" in {
val attachment = new DataHandler(new URL("https://foo.bar"))
val message = new DefaultMessage
message.setBody("test")
message.setHeader("foo", "bar")
message.addAttachment("foo", attachment)
message.setExchange(new DefaultExchange(camel.context))
val attachmentToAdd = new DataHandler(new URL("https://another.url"))
CamelMessage.copyContent(new CamelMessage("body", Map("key" -> "baz"), Map("key" -> attachmentToAdd)), message)
assert(message.getBody === "body")
assert(message.getHeader("foo") === "bar")
assert(message.getHeader("key") === "baz")
assert(message.getAttachment("key") === attachmentToAdd)
assert(message.getAttachment("foo") === attachment)
}
}
}

View file

@ -1,186 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import org.scalatest.WordSpec
import org.scalatest.Matchers
import scala.concurrent.{ Await, Future, Promise }
import scala.collection.immutable
import akka.camel.TestSupport.NonSharedCamelSystem
import akka.actor.{ Actor, ActorRef, Props }
import akka.routing.BroadcastGroup
import scala.concurrent.duration._
import akka.testkit._
import akka.util.Timeout
import org.apache.camel.model.RouteDefinition
import org.apache.camel.builder.Builder
import akka.actor.ActorLogging
/**
* A test to concurrently register and de-register consumer and producer endpoints
*/
class ConcurrentActivationTest extends WordSpec with Matchers with NonSharedCamelSystem {
"Activation" must {
"support concurrent registrations and de-registrations" in {
implicit val ec = system.dispatcher
val number = 10
val eventFilter = EventFilter.warning(pattern = "received dead letter from .*producerRegistrar.*")
system.eventStream.publish(TestEvent.Mute(eventFilter))
try {
// A ConsumerBroadcast creates 'number' amount of ConsumerRegistrars, which will register 'number' amount of endpoints,
// in total number*number endpoints, activating and deactivating every endpoint.
// a promise to the list of registrars, which have a list of actorRefs each. A tuple of a list of activated refs and a list of deactivated refs
val promiseRegistrarLists = Promise[(Future[List[List[ActorRef]]], Future[List[List[ActorRef]]])]()
// future to all the futures of activation and deactivation
val futureRegistrarLists = promiseRegistrarLists.future
val ref = system.actorOf(Props(classOf[ConsumerBroadcast], promiseRegistrarLists), name = "broadcaster")
// create the registrars
ref ! CreateRegistrars(number)
// send a broadcast to all registrars, so that number * number messages are sent
// every Register registers a consumer and a producer
(1 to number).map(i => ref ! RegisterConsumersAndProducers("direct:concurrent-"))
// de-register all consumers and producers
ref ! DeRegisterConsumersAndProducers()
val promiseAllRefs = Promise[(List[ActorRef], List[ActorRef])]()
val allRefsFuture = promiseAllRefs.future
// map over all futures, put all futures in one list of activated and deactivated actor refs.
futureRegistrarLists.map {
case (futureActivations, futureDeactivations) =>
futureActivations.zip(futureDeactivations).map {
case (activations, deactivations) =>
promiseAllRefs.success((activations.flatten, deactivations.flatten))
}
}
val (activations, deactivations) = Await.result(allRefsFuture, 10.seconds.dilated)
// should be the size of the activated activated producers and consumers
activations.size should ===(2 * number * number)
// should be the size of the activated activated producers and consumers
deactivations.size should ===(2 * number * number)
def partitionNames(refs: immutable.Seq[ActorRef]) =
refs.map(_.path.name).partition(_.startsWith("concurrent-test-echo-consumer"))
def assertContainsSameElements(lists: (Seq[_], Seq[_])): Unit = {
val (a, b) = lists
a.intersect(b).size should ===(a.size)
}
val (activatedConsumerNames, activatedProducerNames) = partitionNames(activations)
val (deactivatedConsumerNames, deactivatedProducerNames) = partitionNames(deactivations)
assertContainsSameElements(activatedConsumerNames -> deactivatedConsumerNames)
assertContainsSameElements(activatedProducerNames -> deactivatedProducerNames)
} finally {
system.eventStream.publish(TestEvent.UnMute(eventFilter))
}
}
}
}
class ConsumerBroadcast(promise: Promise[(Future[List[List[ActorRef]]], Future[List[List[ActorRef]]])]) extends Actor {
private var broadcaster: Option[ActorRef] = None
private implicit val ec = context.dispatcher
def receive = {
case CreateRegistrars(number) =>
var allActivationFutures = List[Future[List[ActorRef]]]()
var allDeactivationFutures = List[Future[List[ActorRef]]]()
val routeePaths = (1 to number).map { i =>
val activationListPromise = Promise[List[ActorRef]]()
val deactivationListPromise = Promise[List[ActorRef]]()
val activationListFuture = activationListPromise.future
val deactivationListFuture = deactivationListPromise.future
allActivationFutures = allActivationFutures :+ activationListFuture
allDeactivationFutures = allDeactivationFutures :+ deactivationListFuture
val routee =
context.actorOf(
Props(classOf[Registrar], i, number, activationListPromise, deactivationListPromise),
"registrar-" + i)
routee.path.toString
}
promise.success(Future.sequence(allActivationFutures) -> Future.sequence(allDeactivationFutures))
broadcaster = Some(context.actorOf(BroadcastGroup(routeePaths).props(), "registrarRouter"))
case reg: Any =>
broadcaster.foreach(_.forward(reg))
}
}
final case class CreateRegistrars(number: Int)
final case class RegisterConsumersAndProducers(endpointUri: String)
final case class DeRegisterConsumersAndProducers()
final case class Activations()
final case class DeActivations()
class Registrar(
val start: Int,
val number: Int,
activationsPromise: Promise[List[ActorRef]],
deActivationsPromise: Promise[List[ActorRef]])
extends Actor
with ActorLogging {
private var actorRefs = Set[ActorRef]()
private var activations = Set[Future[ActorRef]]()
private var deActivations = Set[Future[ActorRef]]()
private var index = 0
private val camel = CamelExtension(context.system)
private implicit val ec = context.dispatcher
private implicit val timeout = Timeout(10.seconds.dilated(context.system))
def receive = {
case reg: RegisterConsumersAndProducers =>
val i = index
val endpoint = reg.endpointUri + start + "-" + i
add(new EchoConsumer(endpoint), "concurrent-test-echo-consumer-" + start + "-" + i)
add(new TestProducer(endpoint), "concurrent-test-producer-" + start + "-" + i)
index = index + 1
if (activations.size == number * 2) {
Future.sequence(activations.toList).map(activationsPromise.success)
}
case reg: DeRegisterConsumersAndProducers =>
actorRefs.foreach { aref =>
context.stop(aref)
val result = camel.deactivationFutureFor(aref)
result.failed.foreach { e =>
log.error("deactivationFutureFor {} failed: {}", aref, e.getMessage)
}
deActivations += result
if (deActivations.size == number * 2) {
Future.sequence(deActivations.toList).map(deActivationsPromise.success)
}
}
}
def add(actor: => Actor, name: String): Unit = {
val ref = context.actorOf(Props(actor), name)
actorRefs = actorRefs + ref
val result = camel.activationFutureFor(ref)
result.failed.foreach { e =>
log.error("activationFutureFor {} failed: {}", ref, e.getMessage)
}
activations += result
}
}
class EchoConsumer(endpoint: String) extends Actor with Consumer {
def endpointUri = endpoint
def receive = {
case msg: CamelMessage => sender() ! msg
}
/**
* Returns the route definition handler for creating a custom route to this consumer.
* By default it returns an identity function, override this method to
* return a custom route definition handler.
*/
override def onRouteDefinition =
(rd: RouteDefinition) => rd.onException(classOf[Exception]).handled(true).transform(Builder.exceptionMessage).end
}
class TestProducer(uri: String) extends Actor with Producer {
def endpointUri = uri
}

View file

@ -1,237 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import language.postfixOps
import akka.actor._
import org.scalatest.Matchers
import org.scalatest.WordSpec
import akka.camel.TestSupport._
import org.apache.camel.model.{ RouteDefinition }
import org.apache.camel.builder.Builder
import org.apache.camel.{ CamelExecutionException, FailedToCreateRouteException }
import java.util.concurrent.{ ExecutionException, TimeUnit, TimeoutException }
import akka.actor.Status.Failure
import scala.concurrent.duration._
import scala.concurrent.{ Await, ExecutionContext }
import akka.testkit._
import akka.util.Timeout
class ConsumerIntegrationTest extends WordSpec with Matchers with NonSharedCamelSystem {
"ConsumerIntegrationTest" must {
val defaultTimeoutDuration = 10 seconds
implicit val defaultTimeout = Timeout(defaultTimeoutDuration)
implicit def ec: ExecutionContext = system.dispatcher
"Consumer must throw FailedToCreateRouteException, while awaiting activation, if endpoint is invalid" in {
filterEvents(EventFilter[FailedToCreateRouteException](pattern = "failed to activate.*", occurrences = 1)) {
val actorRef = system.actorOf(Props(new TestActor(uri = "some invalid uri")), "invalidActor")
intercept[FailedToCreateRouteException] {
Await.result(camel.activationFutureFor(actorRef), defaultTimeoutDuration)
}
}
}
"Consumer must support in-out messaging" in {
start(new Consumer {
def endpointUri = "direct:a1"
def receive = {
case m: CamelMessage => sender() ! "received " + m.bodyAs[String]
}
}, name = "direct-a1")
camel.sendTo("direct:a1", msg = "some message") should ===("received some message")
}
"Consumer must time-out if consumer is slow" taggedAs TimingTest in {
val SHORT_TIMEOUT = 10 millis
val LONG_WAIT = 1 second
val ref = start(new Consumer {
override def replyTimeout = SHORT_TIMEOUT
def endpointUri = "direct:a3"
def receive = { case _ => { Thread.sleep(LONG_WAIT.toMillis); sender() ! "done" } }
}, name = "ignore-this-deadletter-timeout-consumer-reply")
intercept[CamelExecutionException] {
camel.sendTo("direct:a3", msg = "some msg 3")
}.getCause.getClass should ===(classOf[TimeoutException])
stop(ref)
}
"Consumer must process messages even after actor restart" in {
val restarted = TestLatch()
val consumer = start(new Consumer {
def endpointUri = "direct:a2"
def receive = {
case "throw" => throw new TestException("")
case m: CamelMessage => sender() ! "received " + m.bodyAs[String]
}
override def postRestart(reason: Throwable): Unit = {
restarted.countDown()
}
}, "direct-a2")
filterEvents(EventFilter[TestException](occurrences = 1)) {
consumer ! "throw"
Await.ready(restarted, defaultTimeoutDuration)
camel.sendTo("direct:a2", msg = "xyz") should ===("received xyz")
}
stop(consumer)
}
"Consumer must unregister itself when stopped" in {
val consumer = start(new TestActor(), name = "test-actor-unregister")
Await.result(camel.activationFutureFor(consumer), defaultTimeoutDuration)
camel.routeCount should be > (0)
system.stop(consumer)
Await.result(camel.deactivationFutureFor(consumer), defaultTimeoutDuration)
camel.routeCount should ===(0)
}
"Consumer must register on uri passed in through constructor" in {
val consumer = start(new TestActor("direct://test"), name = "direct-test")
Await.result(camel.activationFutureFor(consumer), defaultTimeoutDuration)
camel.routeCount should be > (0)
camel.routes.get(0).getEndpoint.getEndpointUri should ===("direct://test")
system.stop(consumer)
Await.result(camel.deactivationFutureFor(consumer), defaultTimeoutDuration)
camel.routeCount should ===(0)
stop(consumer)
}
"Error passing consumer supports error handling through route modification" in {
val ref = start(new ErrorThrowingConsumer("direct:error-handler-test") {
override def onRouteDefinition = (rd: RouteDefinition) => {
rd.onException(classOf[TestException]).handled(true).transform(Builder.exceptionMessage).end
}
}, name = "direct-error-handler-test")
filterEvents(EventFilter[TestException](occurrences = 1)) {
camel.sendTo("direct:error-handler-test", msg = "hello") should ===("error: hello")
}
stop(ref)
}
"Error passing consumer supports redelivery through route modification" in {
val ref = start(new FailingOnceConsumer("direct:failing-once-consumer") {
override def onRouteDefinition = (rd: RouteDefinition) => {
rd.onException(classOf[TestException]).redeliveryDelay(0L).maximumRedeliveries(1).end
}
}, name = "direct-failing-once-consumer")
filterEvents(EventFilter[TestException](occurrences = 1)) {
camel.sendTo("direct:failing-once-consumer", msg = "hello") should ===("accepted: hello")
}
stop(ref)
}
"Consumer supports manual Ack" in {
val ref = start(new ManualAckConsumer() {
def endpointUri = "direct:manual-ack"
def receive = { case _ => sender() ! Ack }
}, name = "direct-manual-ack-1")
camel.template
.asyncSendBody("direct:manual-ack", "some message")
.get(defaultTimeoutDuration.toSeconds, TimeUnit.SECONDS) should ===(null) //should not timeout
stop(ref)
}
"Consumer handles manual Ack failure" in {
val someException = new Exception("e1")
val ref = start(new ManualAckConsumer() {
def endpointUri = "direct:manual-ack"
def receive = { case _ => sender() ! Failure(someException) }
}, name = "direct-manual-ack-2")
intercept[ExecutionException] {
camel.template
.asyncSendBody("direct:manual-ack", "some message")
.get(defaultTimeoutDuration.toSeconds, TimeUnit.SECONDS)
}.getCause.getCause should ===(someException)
stop(ref)
}
"Consumer should time-out, if manual Ack not received within replyTimeout and should give a human readable error message" in {
val ref = start(new ManualAckConsumer() {
override def replyTimeout = 10 millis
def endpointUri = "direct:manual-ack"
def receive = { case _ => }
}, name = "direct-manual-ack-3")
intercept[ExecutionException] {
camel.template
.asyncSendBody("direct:manual-ack", "some message")
.get(defaultTimeoutDuration.toSeconds, TimeUnit.SECONDS)
}.getCause.getCause.getMessage should include("Failed to get Ack")
stop(ref)
}
"respond to onRouteDefinition" in {
val ref = start(new ErrorRespondingConsumer("direct:error-responding-consumer-1"), "error-responding-consumer")
filterEvents(EventFilter[TestException](occurrences = 1)) {
val response = camel.sendTo("direct:error-responding-consumer-1", "some body")
response should ===("some body has an error")
}
stop(ref)
}
}
}
class ErrorThrowingConsumer(override val endpointUri: String) extends Consumer {
def receive = {
case msg: CamelMessage => throw new TestException("error: %s".format(msg.body))
}
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
super.preRestart(reason, message)
sender() ! Failure(reason)
}
}
class ErrorRespondingConsumer(override val endpointUri: String) extends Consumer {
def receive = {
case msg: CamelMessage => throw new TestException("Error!")
}
override def onRouteDefinition = (rd: RouteDefinition) => {
// Catch TestException and handle it by returning a modified version of the in message
rd.onException(classOf[TestException]).handled(true).transform(Builder.body.append(" has an error")).end
}
final override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
super.preRestart(reason, message)
sender() ! Failure(reason)
}
}
class FailingOnceConsumer(override val endpointUri: String) extends Consumer {
def receive = {
case msg: CamelMessage =>
if (msg.headerAs[Boolean]("CamelRedelivered").getOrElse(false))
sender() ! ("accepted: %s".format(msg.body))
else
throw new TestException("rejected: %s".format(msg.body))
}
final override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
super.preRestart(reason, message)
sender() ! Failure(reason)
}
}
class TestActor(uri: String = "file://target/abcde") extends Consumer {
def endpointUri = uri
def receive = { case _ => /* do nothing */ }
}
trait ManualAckConsumer extends Consumer {
override def autoAck = false
}
class TestException(msg: String) extends Exception(msg)

View file

@ -1,67 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import akka.camel.TestSupport.SharedCamelSystem
import internal.DefaultCamel
import org.scalatest.Matchers
import org.scalatest.mock.MockitoSugar
import org.apache.camel.ProducerTemplate
import org.scalatest.WordSpec
import akka.event.MarkerLoggingAdapter
import akka.actor.ActorSystem.Settings
import com.typesafe.config.ConfigFactory
import org.apache.camel.impl.DefaultCamelContext
import akka.actor.ExtendedActorSystem
class DefaultCamelTest extends WordSpec with SharedCamelSystem with Matchers with MockitoSugar {
import org.mockito.Mockito.{ verify, when }
val sys = mock[ExtendedActorSystem]
val config = ConfigFactory.defaultReference()
when(sys.dynamicAccess).thenReturn(system.asInstanceOf[ExtendedActorSystem].dynamicAccess)
when(sys.settings).thenReturn(new Settings(this.getClass.getClassLoader, config, "mocksystem"))
when(sys.name).thenReturn("mocksystem")
def camelWithMocks = new DefaultCamel(sys) {
override val log = mock[MarkerLoggingAdapter]
override lazy val template = mock[ProducerTemplate]
override lazy val context = mock[DefaultCamelContext]
override val settings = mock[CamelSettings]
}
"during shutdown, when both context and template fail to shutdown" when {
val camel = camelWithMocks
when(camel.context.stop()).thenThrow(new RuntimeException("context"))
when(camel.template.stop()).thenThrow(new RuntimeException("template"))
val exception = intercept[RuntimeException] {
camel.shutdown()
}
"throws exception thrown by context.stop()" in {
exception.getMessage() should ===("context")
}
"tries to stop both template and context" in {
verify(camel.template).stop()
verify(camel.context).stop()
}
}
"during start, if template fails to start, it will stop the context" in {
val camel = camelWithMocks
when(camel.template.start()).thenThrow(new RuntimeException)
intercept[RuntimeException] {
camel.start
}
verify(camel.context).stop()
}
}

View file

@ -1,60 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import java.io.InputStream
import org.apache.camel.NoTypeConversionAvailableException
import akka.camel.TestSupport.{ SharedCamelSystem }
import org.scalatest.FunSuite
import org.scalatest.Matchers
import org.apache.camel.converter.stream.InputStreamCache
class MessageScalaTest extends FunSuite with Matchers with SharedCamelSystem {
implicit def camelContext = camel.context
test("mustConvertDoubleBodyToString") {
CamelMessage(1.4, Map.empty).bodyAs[String] should ===("1.4")
}
test("mustThrowExceptionWhenConvertingDoubleBodyToInputStream") {
intercept[NoTypeConversionAvailableException] {
CamelMessage(1.4, Map.empty).bodyAs[InputStream]
}
}
test("mustConvertDoubleHeaderToString") {
val message = CamelMessage("test", Map("test" -> 1.4))
message.headerAs[String]("test").get should ===("1.4")
}
test("mustReturnSubsetOfHeaders") {
val message = CamelMessage("test", Map("A" -> "1", "B" -> "2"))
message.headers(Set("B")) should ===(Map("B" -> "2"))
}
test("mustTransformBodyAndPreserveHeaders") {
CamelMessage("a", Map("A" -> "1")).mapBody((body: String) => body + "b") should ===(
CamelMessage("ab", Map("A" -> "1")))
}
test("mustConvertBodyAndPreserveHeaders") {
CamelMessage(1.4, Map("A" -> "1")).withBodyAs[String] should ===(CamelMessage("1.4", Map("A" -> "1")))
}
test("mustSetBodyAndPreserveHeaders") {
CamelMessage("test1", Map("A" -> "1")).copy(body = "test2") should ===(CamelMessage("test2", Map("A" -> "1")))
}
test("mustSetHeadersAndPreserveBody") {
CamelMessage("test1", Map("A" -> "1")).copy(headers = Map("C" -> "3")) should ===(
CamelMessage("test1", Map("C" -> "3")))
}
test("mustBeAbleToReReadStreamCacheBody") {
val msg = CamelMessage(new InputStreamCache("test1".getBytes("utf-8")), Map.empty)
msg.bodyAs[String] should ===("test1")
// re-read
msg.bodyAs[String] should ===("test1")
}
}

View file

@ -1,411 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import language.postfixOps
import org.apache.camel.{ Exchange, Processor }
import org.apache.camel.builder.RouteBuilder
import org.apache.camel.component.mock.MockEndpoint
import scala.concurrent.Await
import akka.actor.SupervisorStrategy.Stop
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach, WordSpecLike }
import akka.actor._
import scala.concurrent.duration._
import akka.util.Timeout
import org.scalatest.Matchers
import akka.testkit._
import akka.actor.Status.Failure
/**
* Tests the features of the Camel Producer.
*/
class ProducerFeatureTest
extends TestKit(ActorSystem("ProducerFeatureTest", AkkaSpec.testConf))
with WordSpecLike
with BeforeAndAfterAll
with BeforeAndAfterEach
with Matchers {
import ProducerFeatureTest._
implicit def camel = CamelExtension(system)
override protected def afterAll(): Unit = {
super.afterAll()
shutdown()
}
val camelContext = camel.context
// to make testing equality of messages easier, otherwise the breadcrumb shows up in the result.
camelContext.setUseBreadcrumb(false)
val timeoutDuration = 1 second
implicit val timeout = Timeout(timeoutDuration)
override protected def beforeAll: Unit = { camelContext.addRoutes(new TestRoute(system)) }
override protected def afterEach: Unit = { mockEndpoint.reset() }
"A Producer on a sync Camel route" must {
"01 produce a message and receive normal response" in {
val producer =
system.actorOf(Props(new TestProducer("direct:producer-test-2", true)), name = "01-direct-producer-2")
val message = CamelMessage("test", Map(CamelMessage.MessageExchangeId -> "123"))
producer.tell(message, testActor)
expectMsg(CamelMessage("received TEST", Map(CamelMessage.MessageExchangeId -> "123")))
}
"02 produce a message and receive failure response" in {
val latch = TestLatch()
var deadActor: Option[ActorRef] = None
val supervisor = system.actorOf(
Props(new Actor {
def receive = {
case p: Props => {
val producer = context.actorOf(p)
context.watch(producer)
sender() ! producer
}
case Terminated(actorRef) => {
deadActor = Some(actorRef)
latch.countDown()
}
}
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case _: AkkaCamelException => Stop
}
}),
name = "02-prod-anonymous-supervisor")
supervisor.tell(Props(new TestProducer("direct:producer-test-2")), testActor)
val producer = receiveOne(timeoutDuration).asInstanceOf[ActorRef]
val message = CamelMessage("fail", Map(CamelMessage.MessageExchangeId -> "123"))
filterEvents(EventFilter[AkkaCamelException](occurrences = 1)) {
producer.tell(message, testActor)
expectMsgPF(timeoutDuration) {
case Failure(e: AkkaCamelException) =>
e.getMessage should ===("failure")
e.headers should ===(Map(CamelMessage.MessageExchangeId -> "123"))
}
}
Await.ready(latch, timeoutDuration)
deadActor should ===(Some(producer))
}
"03 produce a message oneway" in {
val producer = system.actorOf(
Props(new TestProducer("direct:producer-test-1", true) with Oneway),
name = "03-direct-producer-1-oneway")
mockEndpoint.expectedBodiesReceived("TEST")
producer ! CamelMessage("test", Map())
mockEndpoint.assertIsSatisfied()
}
"04 produces message twoway without sender reference" in {
// this test causes a dead letter which can be ignored. The producer is two-way but a oneway tell is used
// to communicate with it and the response is ignored, which ends up in a dead letter
val producer = system.actorOf(
Props(new TestProducer("direct:producer-test-1")),
name = "04-ignore-this-deadletter-direct-producer-test-no-sender")
mockEndpoint.expectedBodiesReceived("test")
producer ! CamelMessage("test", Map())
mockEndpoint.assertIsSatisfied()
}
}
"A Producer on an async Camel route" must {
"10 produce message to direct:producer-test-3 and receive normal response" in {
val producer =
system.actorOf(Props(new TestProducer("direct:producer-test-3")), name = "10-direct-producer-test-3")
val message = CamelMessage("test", Map(CamelMessage.MessageExchangeId -> "123"))
producer.tell(message, testActor)
expectMsg(CamelMessage("received test", Map(CamelMessage.MessageExchangeId -> "123")))
}
"11 produce message to direct:producer-test-3 and receive failure response" in {
val producer = system.actorOf(
Props(new TestProducer("direct:producer-test-3")),
name = "11-direct-producer-test-3-receive-failure")
val message = CamelMessage("fail", Map(CamelMessage.MessageExchangeId -> "123"))
filterEvents(EventFilter[AkkaCamelException](occurrences = 1)) {
producer.tell(message, testActor)
expectMsgPF(timeoutDuration) {
case Failure(e: AkkaCamelException) =>
e.getMessage should ===("failure")
e.headers should ===(Map(CamelMessage.MessageExchangeId -> "123"))
}
}
}
"12 produce message, forward normal response of direct:producer-test-2 to a replying target actor and receive response" in {
val target = system.actorOf(Props[ReplyingForwardTarget], name = "12-reply-forwarding-target")
val producer = system.actorOf(
Props(new TestForwarder("direct:producer-test-2", target)),
name = "12-direct-producer-test-2-forwarder")
val message = CamelMessage("test", Map(CamelMessage.MessageExchangeId -> "123"))
producer.tell(message, testActor)
expectMsg(CamelMessage("received test", Map(CamelMessage.MessageExchangeId -> "123", "test" -> "result")))
}
"13 produce message, forward failure response of direct:producer-test-2 to a replying target actor and receive response" in {
val target = system.actorOf(Props[ReplyingForwardTarget], name = "13-reply-forwarding-target")
val producer = system.actorOf(
Props(new TestForwarder("direct:producer-test-2", target)),
name = "13-direct-producer-test-2-forwarder-failure")
val message = CamelMessage("fail", Map(CamelMessage.MessageExchangeId -> "123"))
filterEvents(EventFilter[AkkaCamelException](occurrences = 1)) {
producer.tell(message, testActor)
expectMsgPF(timeoutDuration) {
case Failure(e: AkkaCamelException) =>
e.getMessage should ===("failure")
e.headers should ===(Map(CamelMessage.MessageExchangeId -> "123", "test" -> "failure"))
}
}
}
"14 produce message, forward normal response to a producing target actor and produce response to direct:forward-test-1" in {
val target = system.actorOf(Props[ProducingForwardTarget], name = "14-producer-forwarding-target")
val producer = system.actorOf(
Props(new TestForwarder("direct:producer-test-2", target)),
name = "14-direct-producer-test-2-forwarder-to-producing-target")
mockEndpoint.expectedBodiesReceived("received test")
producer.tell(CamelMessage("test", Map()), producer)
mockEndpoint.assertIsSatisfied()
}
"15 produce message, forward failure response to a producing target actor and produce response to direct:forward-test-1" in {
val target = system.actorOf(Props[ProducingForwardTarget], name = "15-producer-forwarding-target-failure")
val producer = system.actorOf(
Props(new TestForwarder("direct:producer-test-2", target)),
name = "15-direct-producer-test-2-forward-failure")
filterEvents(EventFilter[AkkaCamelException](occurrences = 1)) {
mockEndpoint.expectedMessageCount(1)
mockEndpoint.message(0).body().isInstanceOf(classOf[akka.actor.Status.Failure])
producer.tell(CamelMessage("fail", Map()), producer)
mockEndpoint.assertIsSatisfied()
}
}
"16 produce message, forward normal response from direct:producer-test-3 to a replying target actor and receive response" in {
val target = system.actorOf(Props[ReplyingForwardTarget], name = "16-reply-forwarding-target")
val producer = system.actorOf(
Props(new TestForwarder("direct:producer-test-3", target)),
name = "16-direct-producer-test-3-to-replying-actor")
val message = CamelMessage("test", Map(CamelMessage.MessageExchangeId -> "123"))
producer.tell(message, testActor)
expectMsg(CamelMessage("received test", Map(CamelMessage.MessageExchangeId -> "123", "test" -> "result")))
}
"17 produce message, forward failure response from direct:producer-test-3 to a replying target actor and receive response" in {
val target = system.actorOf(Props[ReplyingForwardTarget], name = "17-reply-forwarding-target")
val producer = system.actorOf(
Props(new TestForwarder("direct:producer-test-3", target)),
name = "17-direct-producer-test-3-forward-failure")
val message = CamelMessage("fail", Map(CamelMessage.MessageExchangeId -> "123"))
filterEvents(EventFilter[AkkaCamelException](occurrences = 1)) {
producer.tell(message, testActor)
expectMsgPF(timeoutDuration) {
case Failure(e: AkkaCamelException) =>
e.getMessage should ===("failure")
e.headers should ===(Map(CamelMessage.MessageExchangeId -> "123", "test" -> "failure"))
}
}
}
"18 produce message, forward normal response from direct:producer-test-3 to a producing target actor and produce response to direct:forward-test-1" in {
val target = system.actorOf(Props[ProducingForwardTarget], "18-producing-forward-target-normal")
val producer = system.actorOf(
Props(new TestForwarder("direct:producer-test-3", target)),
name = "18-direct-producer-test-3-forward-normal")
mockEndpoint.expectedBodiesReceived("received test")
producer.tell(CamelMessage("test", Map()), producer)
mockEndpoint.assertIsSatisfied()
}
"19 produce message, forward failure response from direct:producer-test-3 to a producing target actor and produce response to direct:forward-test-1" in {
val target = system.actorOf(Props[ProducingForwardTarget], "19-producing-forward-target-failure")
val producer = system.actorOf(
Props(new TestForwarder("direct:producer-test-3", target)),
name = "19-direct-producer-test-3-forward-failure-producing-target")
filterEvents(EventFilter[AkkaCamelException](occurrences = 1)) {
mockEndpoint.expectedMessageCount(1)
mockEndpoint.message(0).body().isInstanceOf(classOf[akka.actor.Status.Failure])
producer.tell(CamelMessage("fail", Map()), producer)
mockEndpoint.assertIsSatisfied()
}
}
"20 keep producing messages after error" in {
import TestSupport._
val consumer =
start(new IntermittentErrorConsumer("direct:intermittentTest-1"), "20-intermittentTest-error-consumer")
val producer = start(new SimpleProducer("direct:intermittentTest-1"), "20-intermittentTest-producer")
filterEvents(EventFilter[AkkaCamelException](occurrences = 1)) {
val futureFailed = producer.tell("fail", testActor)
expectMsgPF(timeoutDuration) {
case Failure(e) =>
e.getMessage should ===("fail")
}
producer.tell("OK", testActor)
expectMsg("OK")
}
stop(consumer)
stop(producer)
}
"21 be able to transform outgoing messages and have a valid sender reference" in {
import TestSupport._
filterEvents(EventFilter[Exception](occurrences = 1)) {
val producerSupervisor =
system.actorOf(
Props(new ProducerSupervisor(Props(new ChildProducer("mock:mock", true)))),
"21-ignore-deadletter-sender-ref-test")
mockEndpoint.reset()
producerSupervisor.tell(CamelMessage("test", Map()), testActor)
producerSupervisor.tell(CamelMessage("err", Map()), testActor)
mockEndpoint.expectedMessageCount(1)
mockEndpoint.expectedBodiesReceived("TEST")
expectMsg("TEST")
}
}
}
private def mockEndpoint = camel.context.getEndpoint("mock:mock", classOf[MockEndpoint])
}
object ProducerFeatureTest {
class ProducerSupervisor(childProps: Props) extends Actor {
override def supervisorStrategy = SupervisorStrategy.stoppingStrategy
val child = context.actorOf(childProps, "producer-supervisor-child")
val duration = 10 seconds
implicit val timeout = Timeout(duration)
implicit val ec = context.system.dispatcher
Await.ready(CamelExtension(context.system).activationFutureFor(child), timeout.duration)
def receive = {
case msg: CamelMessage =>
child.forward(msg)
case (aref: ActorRef, msg: String) =>
aref ! msg
}
}
class ChildProducer(uri: String, upper: Boolean = false) extends Actor with Producer {
override def oneway = true
var lastSender: Option[ActorRef] = None
var lastMessage: Option[String] = None
def endpointUri = uri
override def transformOutgoingMessage(msg: Any) = msg match {
case msg: CamelMessage =>
if (upper) msg.mapBody { body: String =>
if (body == "err") throw new Exception("Crash!")
val upperMsg = body.toUpperCase
lastSender = Some(sender())
lastMessage = Some(upperMsg)
} else msg
}
override def postStop(): Unit = {
for (msg <- lastMessage; aref <- lastSender) context.parent ! ((aref, msg))
super.postStop()
}
}
class TestProducer(uri: String, upper: Boolean = false) extends Actor with Producer {
def endpointUri = uri
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
//overriding on purpose so it doesn't try to deRegister and reRegister at restart,
// which would cause a deadletter message in the test output.
}
override protected def transformOutgoingMessage(msg: Any) = msg match {
case msg: CamelMessage =>
if (upper) msg.mapBody { body: String =>
body.toUpperCase
} else msg
}
}
class TestForwarder(uri: String, target: ActorRef) extends Actor with Producer {
def endpointUri = uri
override def headersToCopy = Set(CamelMessage.MessageExchangeId, "test")
override def routeResponse(msg: Any): Unit = target.forward(msg)
}
class TestResponder extends Actor {
def receive = {
case msg: CamelMessage =>
msg.body match {
case "fail" =>
context.sender() ! akka.actor.Status.Failure(new AkkaCamelException(new Exception("failure"), msg.headers))
case _ =>
context.sender() ! (msg.mapBody { body: String =>
"received %s".format(body)
})
}
}
}
class ReplyingForwardTarget extends Actor {
def receive = {
case msg: CamelMessage =>
context.sender() ! (msg.copy(headers = msg.headers + ("test" -> "result")))
case msg: akka.actor.Status.Failure =>
msg.cause match {
case e: AkkaCamelException =>
context.sender() ! Status.Failure(new AkkaCamelException(e, e.headers + ("test" -> "failure")))
}
}
}
class ProducingForwardTarget extends Actor with Producer with Oneway {
def endpointUri = "direct:forward-test-1"
}
class TestRoute(system: ActorSystem) extends RouteBuilder {
val responder = system.actorOf(Props[TestResponder], name = "TestResponder")
def configure: Unit = {
from("direct:forward-test-1").to("mock:mock")
// for one-way messaging tests
from("direct:producer-test-1").to("mock:mock")
// for two-way messaging tests (async)
from("direct:producer-test-3").to(responder)
// for two-way messaging tests (sync)
from("direct:producer-test-2").process(new Processor() {
def process(exchange: Exchange) = {
exchange.getIn.getBody match {
case "fail" => throw new Exception("failure")
case body => exchange.getOut.setBody("received %s".format(body))
}
}
})
}
}
class SimpleProducer(override val endpointUri: String) extends Producer {
override protected def transformResponse(msg: Any) = msg match {
case m: CamelMessage => m.bodyAs[String]
case m: Any => m
}
}
class IntermittentErrorConsumer(override val endpointUri: String) extends Consumer {
def receive = {
case msg: CamelMessage if msg.bodyAs[String] == "fail" => sender() ! Failure(new Exception("fail"))
case msg: CamelMessage => sender() ! msg
}
}
}

View file

@ -1,96 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import language.postfixOps
import language.implicitConversions
import scala.concurrent.duration._
import java.util.concurrent.{ ExecutionException, TimeUnit, TimeoutException }
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach, Suite }
import org.scalatest.matchers.{ BePropertyMatchResult, BePropertyMatcher }
import scala.reflect.ClassTag
import akka.actor.{ Actor, ActorRef, ActorSystem, Props }
import scala.concurrent.Await
import akka.util.Timeout
import akka.testkit.{ AkkaSpec, TestKit }
private[camel] object TestSupport {
def start(actor: => Actor, name: String)(implicit system: ActorSystem, timeout: Timeout): ActorRef =
Await.result(
CamelExtension(system).activationFutureFor(system.actorOf(Props(actor), name))(timeout, system.dispatcher),
timeout.duration)
def stop(actorRef: ActorRef)(implicit system: ActorSystem, timeout: Timeout): Unit = {
system.stop(actorRef)
Await.result(CamelExtension(system).deactivationFutureFor(actorRef)(timeout, system.dispatcher), timeout.duration)
}
private[camel] implicit def camelToTestWrapper(camel: Camel) = new CamelTestWrapper(camel)
class CamelTestWrapper(camel: Camel) {
/**
* Sends msg to the endpoint and returns response.
* It only waits for the response until timeout passes.
* This is to reduce cases when unit-tests block infinitely.
*/
def sendTo(to: String, msg: String, timeout: Duration = 1 second): AnyRef = {
try {
camel.template.asyncRequestBody(to, msg).get(timeout.toNanos, TimeUnit.NANOSECONDS)
} catch {
case e: ExecutionException => throw e.getCause
case e: TimeoutException =>
throw new AssertionError(
"Failed to get response to message [%s], send to endpoint [%s], within [%s]".format(msg, to, timeout))
}
}
def routeCount = camel.context.getRoutes().size()
def routes = camel.context.getRoutes
}
trait SharedCamelSystem extends BeforeAndAfterAll { this: Suite =>
implicit lazy val system = ActorSystem("SharedCamelSystem", AkkaSpec.testConf)
implicit lazy val camel = CamelExtension(system)
abstract override protected def afterAll(): Unit = {
super.afterAll()
TestKit.shutdownActorSystem(system)
}
}
trait NonSharedCamelSystem extends BeforeAndAfterEach { this: Suite =>
implicit var system: ActorSystem = _
implicit var camel: Camel = _
override protected def beforeEach(): Unit = {
super.beforeEach()
system = ActorSystem("NonSharedCamelSystem", AkkaSpec.testConf)
camel = CamelExtension(system)
}
override protected def afterEach(): Unit = {
TestKit.shutdownActorSystem(system)
super.afterEach()
}
}
def time[A](block: => A): FiniteDuration = {
val start = System.nanoTime()
block
val duration = System.nanoTime() - start
duration nanos
}
def anInstanceOf[T](implicit tag: ClassTag[T]) = {
val clazz = tag.runtimeClass.asInstanceOf[Class[T]]
new BePropertyMatcher[AnyRef] {
def apply(left: AnyRef) =
BePropertyMatchResult(clazz.isAssignableFrom(left.getClass), "an instance of " + clazz.getName)
}
}
}

View file

@ -1,101 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel
import language.postfixOps
import org.apache.camel.{ Exchange, Processor }
import org.apache.camel.builder.RouteBuilder
import org.apache.camel.component.mock.MockEndpoint
import akka.camel.TestSupport.SharedCamelSystem
import akka.actor.Props
import akka.pattern._
import scala.concurrent.Await
import scala.concurrent.duration._
import org.scalatest._
import akka.testkit._
class UntypedProducerTest
extends WordSpec
with Matchers
with BeforeAndAfterAll
with BeforeAndAfterEach
with SharedCamelSystem
with GivenWhenThen {
import UntypedProducerTest._
val timeout = 1 second
override protected def beforeAll = {
camel.context.addRoutes(new TestRoute)
}
override protected def afterEach = {
mockEndpoint.reset
}
"An UntypedProducer producing a message to a sync Camel route" must {
"produce a message and receive a normal response" in {
val producer = system.actorOf(Props[SampleUntypedReplyingProducer], name = "sample-untyped-replying-producer")
val message = CamelMessage("test", Map(CamelMessage.MessageExchangeId -> "123"))
val future = producer.ask(message)(timeout)
val expected = CamelMessage("received test", Map(CamelMessage.MessageExchangeId -> "123"))
Await.result(future, timeout) match {
case result: CamelMessage => result should ===(expected)
case unexpected => fail("Actor responded with unexpected message:" + unexpected)
}
}
"produce a message and receive a failure response" in {
val producer =
system.actorOf(Props[SampleUntypedReplyingProducer], name = "sample-untyped-replying-producer-failure")
val message = CamelMessage("fail", Map(CamelMessage.MessageExchangeId -> "123"))
filterEvents(EventFilter[AkkaCamelException](occurrences = 1)) {
val future = producer.ask(message)(timeout).failed
Await.result(future, timeout) match {
case e: AkkaCamelException =>
e.getMessage should ===("failure")
e.headers should ===(Map(CamelMessage.MessageExchangeId -> "123"))
case unexpected => fail("Actor responded with unexpected message:" + unexpected)
}
}
}
}
"An UntypedProducer producing a message to a sync Camel route and then forwarding the response" must {
"produce a message and send a normal response to direct:forward-test-1" in {
val producer = system.actorOf(Props[SampleUntypedForwardingProducer], name = "sample-untyped-forwarding-producer")
mockEndpoint.expectedBodiesReceived("received test")
producer.tell(CamelMessage("test", Map[String, Any]()), producer)
mockEndpoint.assertIsSatisfied
}
}
private def mockEndpoint = camel.context.getEndpoint("mock:mock", classOf[MockEndpoint])
}
object UntypedProducerTest {
class TestRoute extends RouteBuilder {
def configure: Unit = {
from("direct:forward-test-1").to("mock:mock")
from("direct:producer-test-1").process(new Processor() {
def process(exchange: Exchange) = {
exchange.getIn.getBody match {
case "fail" => throw new Exception("failure")
case body => exchange.getOut.setBody("received %s".format(body))
}
}
})
}
}
}

View file

@ -1,150 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal
import org.scalatest.Matchers
import scala.concurrent.duration._
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach, GivenWhenThen, WordSpecLike }
import akka.actor.{ ActorSystem, Props }
import akka.testkit.{ TestKit, TestProbe, TimingTest }
import akka.camel.internal.ActivationProtocol._
class ActivationTrackerTest
extends TestKit(ActorSystem("ActivationTrackerTest"))
with WordSpecLike
with Matchers
with BeforeAndAfterAll
with BeforeAndAfterEach
with GivenWhenThen {
override protected def afterAll(): Unit = { shutdown() }
var actor: TestProbe = _
var awaiting: Awaiting = _
var anotherAwaiting: Awaiting = _
val cause = new Exception("cause of failure")
override protected def beforeEach(): Unit = {
actor = TestProbe()
awaiting = new Awaiting(actor)
anotherAwaiting = new Awaiting(actor)
}
val at = system.actorOf(Props[ActivationTracker], name = "activationTrackker")
"ActivationTracker" must {
def publish(msg: Any) = at ! msg
implicit def timeout = remainingOrDefault
"forwards activation message to all awaiting parties" taggedAs TimingTest in {
awaiting.awaitActivation()
anotherAwaiting.awaitActivation()
publish(EndpointActivated(actor.ref))
awaiting.verifyActivated()
anotherAwaiting.verifyActivated()
}
"send activation message even if activation happened earlier" taggedAs TimingTest in {
publish(EndpointActivated(actor.ref))
Thread.sleep(50)
awaiting.awaitActivation()
awaiting.verifyActivated()
}
"send activation message even if actor is already deactivated" taggedAs TimingTest in {
publish(EndpointActivated(actor.ref))
publish(EndpointDeActivated(actor.ref))
Thread.sleep(50)
awaiting.awaitActivation()
awaiting.verifyActivated()
}
"forward de-activation message to all awaiting parties" taggedAs TimingTest in {
publish(EndpointActivated(actor.ref))
publish(EndpointDeActivated(actor.ref))
awaiting.awaitDeActivation()
anotherAwaiting.awaitDeActivation()
awaiting.verifyDeActivated()
anotherAwaiting.verifyDeActivated()
}
"forward de-activation message even if deactivation happened earlier" taggedAs TimingTest in {
publish(EndpointActivated(actor.ref))
awaiting.awaitDeActivation()
publish(EndpointDeActivated(actor.ref))
awaiting.verifyDeActivated()
}
"forward de-activation message even if someone awaits de-activation even before activation happens" taggedAs TimingTest in {
val awaiting = new Awaiting(actor)
awaiting.awaitDeActivation()
publish(EndpointActivated(actor.ref))
publish(EndpointDeActivated(actor.ref))
awaiting.verifyDeActivated()
}
"send activation failure when failed to activate" taggedAs TimingTest in {
awaiting.awaitActivation()
publish(EndpointFailedToActivate(actor.ref, cause))
awaiting.verifyFailedToActivate()
}
"send de-activation failure when failed to de-activate" taggedAs TimingTest in {
publish(EndpointActivated(actor.ref))
awaiting.awaitDeActivation()
publish(EndpointFailedToDeActivate(actor.ref, cause))
awaiting.verifyFailedToDeActivate()
}
"send activation message even if it failed to de-activate" taggedAs TimingTest in {
publish(EndpointActivated(actor.ref))
publish(EndpointFailedToDeActivate(actor.ref, cause))
awaiting.awaitActivation()
awaiting.verifyActivated()
}
"send activation message when an actor is activated, deactivated and activated again" taggedAs TimingTest in {
publish(EndpointActivated(actor.ref))
publish(EndpointDeActivated(actor.ref))
publish(EndpointActivated(actor.ref))
awaiting.awaitActivation()
awaiting.verifyActivated()
}
}
class Awaiting(actor: TestProbe) {
val probe = TestProbe()
def awaitActivation() = at.tell(AwaitActivation(actor.ref), probe.ref)
def awaitDeActivation() = at.tell(AwaitDeActivation(actor.ref), probe.ref)
def verifyActivated()(implicit timeout: FiniteDuration) = within(timeout) {
probe.expectMsg(EndpointActivated(actor.ref))
}
def verifyDeActivated()(implicit timeout: FiniteDuration) = within(timeout) {
probe.expectMsg(EndpointDeActivated(actor.ref))
}
def verifyFailedToActivate()(implicit timeout: FiniteDuration) = within(timeout) {
probe.expectMsg(EndpointFailedToActivate(actor.ref, cause))
}
def verifyFailedToDeActivate()(implicit timeout: FiniteDuration) = within(timeout) {
probe.expectMsg(EndpointFailedToDeActivate(actor.ref, cause))
}
}
}

View file

@ -1,31 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal.component
import language.postfixOps
import org.scalatest.Matchers
import scala.concurrent.duration._
import akka.camel.TestSupport.SharedCamelSystem
import org.apache.camel.Component
import org.scalatest.WordSpec
class ActorComponentConfigurationTest extends WordSpec with Matchers with SharedCamelSystem {
val component: Component = camel.context.getComponent("akka")
"Endpoint url config should be correctly parsed" in {
val actorEndpointConfig = component
.createEndpoint(s"akka://test/user/$$a?autoAck=false&replyTimeout=987000000+nanos")
.asInstanceOf[ActorEndpointConfig]
actorEndpointConfig should have(
'endpointUri (s"akka://test/user/$$a?autoAck=false&replyTimeout=987000000+nanos"),
'path (ActorEndpointPath.fromCamelPath(s"akka://test/user/$$a")),
'autoAck (false),
'replyTimeout (987000000 nanos))
}
}

View file

@ -1,32 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal.component
import org.scalatest.mock.MockitoSugar
import org.scalatest.Matchers
import akka.camel.TestSupport.SharedCamelSystem
import org.scalatest.WordSpec
import akka.actor.{ Actor, Props }
class ActorEndpointPathTest extends WordSpec with SharedCamelSystem with Matchers with MockitoSugar {
def find(path: String) = ActorEndpointPath.fromCamelPath(path).findActorIn(system)
"findActorIn returns Some(actor ref) if actor exists" in {
val path = system.actorOf(Props(new Actor { def receive = { case _ => } }), "knownactor").path
find(path.toString) should be('defined)
}
"findActorIn returns None" when {
"non existing valid path" in { find("akka://system/user/unknownactor") should ===(None) }
}
"fromCamelPath throws IllegalArgumentException" when {
"invalid path" in {
intercept[IllegalArgumentException] {
find("invalidpath")
}
}
}
}

View file

@ -1,451 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal.component
import language.postfixOps
import org.scalatest.mock.MockitoSugar
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito._
import org.apache.camel.{ AsyncCallback, ProducerTemplate }
import java.util.concurrent.atomic.AtomicBoolean
import scala.concurrent.duration._
import akka.camel._
import internal.{ CamelExchangeAdapter, DefaultCamel }
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach, Suite, WordSpecLike }
import akka.camel.TestSupport._
import java.util.concurrent.{ CountDownLatch, TimeoutException }
import org.mockito.{ ArgumentMatcher, ArgumentMatchers, Mockito }
import org.scalatest.Matchers
import akka.actor.Status.Failure
import com.typesafe.config.ConfigFactory
import akka.actor.ActorSystem.Settings
import akka.event.MarkerLoggingAdapter
import akka.testkit.{ TestKit, TestLatch, TestProbe, TimingTest }
import org.apache.camel.impl.DefaultCamelContext
import scala.concurrent.{ Await, Future }
import akka.util.Timeout
import akka.actor._
import akka.testkit._
class ActorProducerTest
extends TestKit(ActorSystem("ActorProducerTest"))
with WordSpecLike
with Matchers
with ActorProducerFixture {
implicit val timeout = Timeout(10 seconds)
"ActorProducer" when {
"synchronous" when {
"consumer actor doesnt exist" must {
"set failure message on exchange" in {
producer = given(actor = null)
producer.processExchangeAdapter(exchange)
verify(exchange).setFailure(any[FailureResult])
}
}
"in-only" must {
def producer = given(outCapable = false)
"pass the message to the consumer" taggedAs TimingTest in {
producer.processExchangeAdapter(exchange)
within(1 second)(probe.expectMsg(message))
}
"not expect response and not block" taggedAs TimingTest in {
time(producer.processExchangeAdapter(exchange)) should be < (200 millis)
}
}
"manualAck" when {
"response is Ack" must {
"process the exchange" in {
producer = given(outCapable = false, autoAck = false)
import system.dispatcher
val future = Future { producer.processExchangeAdapter(exchange) }
within(1 second) {
probe.expectMsgType[CamelMessage]
info("message sent to consumer")
probe.sender() ! Ack
}
verify(exchange, never()).setResponse(any[CamelMessage])
info("no response forwarded to exchange")
Await.ready(future, timeout.duration)
}
}
"the consumer does not respond wit Ack" must {
"not block forever" in {
producer = given(outCapable = false, autoAck = false)
import system.dispatcher
val future = Future {
producer.processExchangeAdapter(exchange)
}
within(1 second) {
probe.expectMsgType[CamelMessage]
info("message sent to consumer")
}
verify(exchange, never()).setResponse(any[CamelMessage])
info("no response forwarded to exchange")
intercept[TimeoutException] {
Await.ready(future, camel.settings.ReplyTimeout - (1 seconds))
}
}
}
}
"out capable" when {
"response is sent back by actor" must {
"get a response" in {
producer = given(actor = echoActor, outCapable = true)
producer.processExchangeAdapter(exchange)
verify(exchange).setResponse(msg("received " + message))
}
}
"response is not sent by actor" must {
val latch = TestLatch(1)
val callback = new AsyncCallback {
def done(doneSync: Boolean): Unit = {
latch.countDown()
}
}
def process() = {
producer = given(outCapable = true, replyTimeout = 100 millis)
val duration = time {
producer.processExchangeAdapter(exchange, callback)
// wait for the actor to complete the callback
Await.ready(latch, 1.seconds.dilated)
}
latch.reset()
duration
}
"timeout after replyTimeout" taggedAs TimingTest in {
val duration = process()
duration should (be >= (100 millis) and be < (2000 millis))
}
"never set the response on exchange" in {
process()
verify(exchange, Mockito.never()).setResponse(any[CamelMessage])
}
"set failure message to timeout" in {
process()
verify(exchange).setFailure(any[FailureResult])
}
}
}
}
"asynchronous" when {
def verifyFailureIsSet(): Unit = {
producer.processExchangeAdapter(exchange, asyncCallback)
asyncCallback.awaitCalled()
verify(exchange).setFailure(any[FailureResult])
}
"out-capable" when {
"consumer actor doesnt exist" must {
"set failure message on exchange" in {
producer = given(actor = null, outCapable = true)
verifyFailureIsSet()
}
}
"response is ok" must {
"get a response and async callback as soon as it gets the response (but not before)" in {
producer = given(outCapable = true)
val doneSync = producer.processExchangeAdapter(exchange, asyncCallback)
asyncCallback.expectNoCallWithin(100 millis)
info("no async callback before response")
within(1 second) {
probe.expectMsgType[CamelMessage]
probe.sender() ! "some message"
}
doneSync should ===(false)
info("done async")
asyncCallback.expectDoneAsyncWithin(1 second)
info("async callback received")
verify(exchange).setResponse(msg("some message"))
info("response as expected")
}
}
"response is Failure" must {
"set an exception on exchange" in {
val exception = new RuntimeException("some failure")
val failure = Failure(exception)
producer = given(outCapable = true)
producer.processExchangeAdapter(exchange, asyncCallback)
within(1 second) {
probe.expectMsgType[CamelMessage]
probe.sender() ! failure
asyncCallback.awaitCalled(remaining)
}
verify(exchange).setFailure(FailureResult(exception))
}
}
"no response is sent within timeout" must {
"set TimeoutException on exchange" in {
producer = given(outCapable = true, replyTimeout = 10 millis)
producer.processExchangeAdapter(exchange, asyncCallback)
asyncCallback.awaitCalled(100 millis)
verify(exchange).setFailure(ArgumentMatchers.argThat(new ArgumentMatcher[FailureResult] {
def matches(failure: FailureResult) = {
failure.asInstanceOf[FailureResult].cause should be(anInstanceOf[TimeoutException])
true
}
}))
}
}
}
"in-only" when {
"consumer actor doesnt exist" must {
"set failure message on exchange" in {
producer = given(actor = null, outCapable = false)
verifyFailureIsSet()
}
}
"autoAck" must {
"get sync callback as soon as it sends a message" in {
producer = given(outCapable = false, autoAck = true)
val doneSync = producer.processExchangeAdapter(exchange, asyncCallback)
doneSync should ===(true)
info("done sync")
asyncCallback.expectDoneSyncWithin(1 second)
info("async callback called")
verify(exchange, never()).setResponse(any[CamelMessage])
info("no response forwarded to exchange")
}
}
"manualAck" when {
"response is Ack" must {
"get async callback" in {
producer = given(outCapable = false, autoAck = false)
val doneSync = producer.processExchangeAdapter(exchange, asyncCallback)
doneSync should ===(false)
within(1 second) {
probe.expectMsgType[CamelMessage]
info("message sent to consumer")
probe.sender() ! Ack
asyncCallback.expectDoneAsyncWithin(remaining)
info("async callback called")
}
verify(exchange, never()).setResponse(any[CamelMessage])
info("no response forwarded to exchange")
}
}
"expecting Ack or Failure message and some other message is sent as a response" must {
"fail" in {
producer = given(outCapable = false, autoAck = false)
producer.processExchangeAdapter(exchange, asyncCallback)
within(1 second) {
probe.expectMsgType[CamelMessage]
info("message sent to consumer")
probe.sender() ! "some neither Ack nor Failure response"
asyncCallback.expectDoneAsyncWithin(remaining)
info("async callback called")
}
verify(exchange, never()).setResponse(any[CamelMessage])
info("no response forwarded to exchange")
verify(exchange).setFailure(any[FailureResult])
info("failure set")
}
}
"no Ack is sent within timeout" must {
"set failure on exchange" in {
producer = given(outCapable = false, replyTimeout = 10 millis, autoAck = false)
producer.processExchangeAdapter(exchange, asyncCallback)
asyncCallback.awaitCalled(100 millis)
verify(exchange).setFailure(any[FailureResult])
}
}
"response is Failure" must {
"set an exception on exchange" in {
producer = given(outCapable = false, autoAck = false)
val doneSync = producer.processExchangeAdapter(exchange, asyncCallback)
doneSync should ===(false)
within(1 second) {
probe.expectMsgType[CamelMessage]
info("message sent to consumer")
probe.sender() ! Failure(new Exception)
asyncCallback.awaitCalled(remaining)
}
verify(exchange, never()).setResponse(any[CamelMessage])
info("no response forwarded to exchange")
verify(exchange).setFailure(any[FailureResult])
info("failure set")
}
}
}
}
}
}
}
private[camel] trait ActorProducerFixture extends MockitoSugar with BeforeAndAfterAll with BeforeAndAfterEach {
self: TestKit with Matchers with Suite =>
var camel: Camel = _
var exchange: CamelExchangeAdapter = _
var callback: AsyncCallback = _
var producer: ActorProducer = _
var message: CamelMessage = _
var probe: TestProbe = _
var asyncCallback: TestAsyncCallback = _
var actorEndpointPath: ActorEndpointPath = _
var actorComponent: ActorComponent = _
override protected def beforeEach(): Unit = {
asyncCallback = createAsyncCallback
probe = TestProbe()
val sys = mock[ExtendedActorSystem]
val config = ConfigFactory.defaultReference()
when(sys.dispatcher).thenReturn(system.dispatcher)
when(sys.dynamicAccess).thenReturn(system.asInstanceOf[ExtendedActorSystem].dynamicAccess)
when(sys.settings).thenReturn(new Settings(this.getClass.getClassLoader, config, "mocksystem"))
when(sys.name).thenReturn("mocksystem")
def camelWithMocks = new DefaultCamel(sys) {
override val log = mock[MarkerLoggingAdapter]
override lazy val template = mock[ProducerTemplate]
override lazy val context = mock[DefaultCamelContext]
override val settings = new CamelSettings(
ConfigFactory.parseString("""
akka {
camel {
jmx = off
streamingCache = on
consumer {
auto-ack = on
reply-timeout = 2s
activation-timeout = 10s
}
}
}
""").withFallback(config),
sys.dynamicAccess)
}
camel = camelWithMocks
exchange = mock[CamelExchangeAdapter]
callback = mock[AsyncCallback]
actorEndpointPath = mock[ActorEndpointPath]
actorComponent = mock[ActorComponent]
producer = new ActorProducer(configure(), camel)
message = CamelMessage(null, null)
}
override protected def afterAll(): Unit = {
shutdown()
}
def msg(s: String) = CamelMessage(s, Map.empty)
def given(
actor: ActorRef = probe.ref,
outCapable: Boolean = true,
autoAck: Boolean = true,
replyTimeout: FiniteDuration = 20 seconds) = {
prepareMocks(actor, outCapable = outCapable)
new ActorProducer(configure(isAutoAck = autoAck, _replyTimeout = replyTimeout), camel)
}
def createAsyncCallback = new TestAsyncCallback
class TestAsyncCallback extends AsyncCallback {
def expectNoCallWithin(duration: Duration): Unit =
if (callbackReceived.await(duration.length, duration.unit)) fail("NOT expected callback, but received one!")
def awaitCalled(timeout: Duration = 1 second): Unit = { valueWithin(1 second) }
val callbackReceived = new CountDownLatch(1)
val callbackValue = new AtomicBoolean()
def done(doneSync: Boolean): Unit = {
callbackValue.set(doneSync)
callbackReceived.countDown()
}
private[this] def valueWithin(implicit timeout: FiniteDuration) =
if (!callbackReceived.await(timeout.length, timeout.unit)) fail("Callback not received!")
else callbackValue.get
def expectDoneSyncWithin(implicit timeout: FiniteDuration): Unit =
if (!valueWithin(timeout)) fail("Expected to be done Synchronously")
def expectDoneAsyncWithin(implicit timeout: FiniteDuration): Unit =
if (valueWithin(timeout)) fail("Expected to be done Asynchronously")
}
def configure(
endpointUri: String = "test-uri",
isAutoAck: Boolean = true,
_replyTimeout: FiniteDuration = 20 seconds) = {
val endpoint = new ActorEndpoint(endpointUri, actorComponent, actorEndpointPath, camel)
endpoint.autoAck = isAutoAck
endpoint.replyTimeout = _replyTimeout
endpoint
}
def prepareMocks(actor: ActorRef, message: CamelMessage = message, outCapable: Boolean): Unit = {
when(actorEndpointPath.findActorIn(any[ActorSystem])).thenReturn(Option(actor))
when(exchange.toRequestMessage(any[Map[String, Any]])).thenReturn(message)
when(exchange.isOutCapable).thenReturn(outCapable)
}
def echoActor =
system.actorOf(Props(new Actor {
def receive = { case msg => sender() ! "received " + msg }
}), name = "echoActor")
}

View file

@ -1,41 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.camel.internal.component
import language.postfixOps
import org.scalatest.Matchers
import scala.concurrent.duration._
import org.scalatest.WordSpec
import org.apache.camel.TypeConversionException
class DurationConverterSpec extends WordSpec with Matchers {
import DurationTypeConverter._
"DurationTypeConverter must convert '10 nanos'" in {
convertTo(classOf[Duration], "10 nanos") should ===(10 nanos)
}
"DurationTypeConverter must do the roundtrip" in {
convertTo(classOf[Duration], (10 seconds).toString()) should ===(10 seconds)
}
"DurationTypeConverter must throw if invalid format" in {
tryConvertTo(classOf[Duration], "abc nanos") should ===(null)
intercept[TypeConversionException] {
mandatoryConvertTo(classOf[Duration], "abc nanos") should ===(10 nanos)
}.getValue should ===("abc nanos")
}
"DurationTypeConverter must throw if doesn't end with time unit" in {
tryConvertTo(classOf[Duration], "10233") should ===(null)
intercept[TypeConversionException] {
mandatoryConvertTo(classOf[Duration], "10233") should ===(10 nanos)
}.getValue should ===("10233")
}
}

View file

@ -1,3 +0,0 @@
akka{
loglevel = "ERROR"
}

View file

@ -1,526 +1,7 @@
# Camel
## Dependency
The akka-camel module was deprecated in 2.5 and has been removed in 2.6.
To use Camel, you must add the following dependency in your project:
As an alternative we recommend [Alpakka](https://doc.akka.io/docs/alpakka/current/). This is of course not a drop-in replacement.
@@dependency[sbt,Maven,Gradle] {
group="com.typesafe.akka"
artifact="akka-camel_$scala.binary_version$"
version="$akka.version$"
}
Camel depends on `jaxb-api` and `javax.activation` that were removed from the JDK. If running on a version of the JDK 9 or above also add
the following dependencies:
@@dependency[sbt,Maven,Gradle] {
group="javax.xml.bind"
artifact="jaxb-api"
version="2.3.0"
}
@@dependency[sbt,Maven,Gradle] {
group="com.sun.activation"
artifact="javax.activation"
version="1.2.0"
}
## Introduction
@@@ warning
Akka Camel is deprecated in favour of [Alpakka](https://github.com/akka/alpakka) , the Akka Streams based collection of integrations to various endpoints (including Camel).
@@@
## Introduction
The akka-camel module allows Untyped Actors to receive
and send messages over a great variety of protocols and APIs.
In addition to the native Scala and Java actor API, actors can now exchange messages with other systems over large number
of protocols and APIs such as HTTP, SOAP, TCP, FTP, SMTP or JMS, to mention a
few. At the moment, approximately 80 protocols and APIs are supported.
### Apache Camel
The akka-camel module is based on [Apache Camel](http://camel.apache.org/), a powerful and light-weight
integration framework for the JVM. For an introduction to Apache Camel you may
want to read this [Apache Camel article](http://architects.dzone.com/articles/apache-camel-integration). Camel comes with a
large number of [components](http://camel.apache.org/components.html) that provide bindings to different protocols and
APIs. The [camel-extra](http://code.google.com/p/camel-extra/) project provides further components.
### Consumer
Here's an example of using Camel's integration components in Akka.
Scala
: @@snip [Introduction.scala](/akka-docs/src/test/scala/docs/camel/Introduction.scala) { #Consumer-mina }
Java
: @@snip [MyEndpoint.java](/akka-docs/src/test/java/jdocs/camel/MyEndpoint.java) { #Consumer-mina }
The above example exposes an actor over a TCP endpoint via Apache
Camel's [Mina component](http://camel.apache.org/mina2.html). The actor implements the @scala[`endpointUri`]@java[`getEndpointUri`] method to define
an endpoint from which it can receive messages. After starting the actor, TCP
clients can immediately send messages to and receive responses from that
actor. If the message exchange should go over HTTP (via Camel's Jetty
component), the actor's @scala[`endpointUri`]@java[`getEndpointUri`] method should return a different URI, for instance `jetty:http://localhost:8877/example`.
@@@ div { .group-scala }
@@snip [Introduction.scala](/akka-docs/src/test/scala/docs/camel/Introduction.scala) { #Consumer }
@@@
@@@ div { .group-java }
In the above case an extra constructor is added that can set the endpoint URI, which would result in
the `getEndpointUri` returning the URI that was set using this constructor.
@@@
### Producer
Actors can also trigger message exchanges with external systems i.e. produce to
Camel endpoints.
Scala
: @@snip [Introduction.scala](/akka-docs/src/test/scala/docs/camel/Introduction.scala) { #imports #Producer }
Java
: @@snip [Orders.java](/akka-docs/src/test/java/jdocs/camel/Orders.java) { #Producer }
In the above example, any message sent to this actor will be sent to
the JMS queue @scala[`orders`]@java[`Orders`]. Producer actors may choose from the same set of Camel
components as Consumer actors do.
@@@ div { .group-java }
Below an example of how to send a message to the `Orders` producer.
@@snip [ProducerTestBase.java](/akka-docs/src/test/java/jdocs/camel/ProducerTestBase.java) { #TellProducer }
@@@
### CamelMessage
The number of Camel components is constantly increasing. The akka-camel module
can support these in a plug-and-play manner. Just add them to your application's
classpath, define a component-specific endpoint URI and use it to exchange
messages over the component-specific protocols or APIs. This is possible because
Camel components bind protocol-specific message formats to a Camel-specific
[normalized message format](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/Message.java). The normalized message format hides
protocol-specific details from Akka and makes it therefore very easy to support
a large number of protocols through a uniform Camel component interface. The
akka-camel module further converts mutable Camel messages into immutable
representations which are used by Consumer and Producer actors for pattern
matching, transformation, serialization or storage. In the above example of the Orders Producer,
the XML message is put in the body of a newly created Camel Message with an empty set of headers.
You can also create a CamelMessage yourself with the appropriate body and headers as you see fit.
### CamelExtension
The akka-camel module is implemented as an Akka Extension, the `CamelExtension` object.
Extensions will only be loaded once per `ActorSystem`, which will be managed by Akka.
The `CamelExtension` object provides access to the @extref[Camel](github:akka-camel/src/main/scala/akka/camel/Camel.scala) @scala[trait]@java[interface].
The @extref[Camel](github:akka-camel/src/main/scala/akka/camel/Camel.scala) @scala[trait]@java[interface] in turn provides access to two important Apache Camel objects, the [CamelContext](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/CamelContext.java) and the `ProducerTemplate`.
Below you can see how you can get access to these Apache Camel objects.
Scala
: @@snip [Introduction.scala](/akka-docs/src/test/scala/docs/camel/Introduction.scala) { #CamelExtension }
Java
: @@snip [CamelExtensionTest.java](/akka-docs/src/test/java/jdocs/camel/CamelExtensionTest.java) { #CamelExtension }
One `CamelExtension` is only loaded once for every one `ActorSystem`, which makes it safe to call the `CamelExtension` at any point in your code to get to the
Apache Camel objects associated with it. There is one [CamelContext](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/CamelContext.java) and one `ProducerTemplate` for every one `ActorSystem` that uses a `CamelExtension`.
By Default, a new [CamelContext](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/CamelContext.java) is created when the `CamelExtension` starts. If you want to inject your own context instead,
you can @scala[extend]@java[implement] the @extref[ContextProvider](github:akka-camel/src/main/scala/akka/camel/ContextProvider.scala) @scala[trait]@java[interface] and add the FQCN of your implementation in the config, as the value of the "akka.camel.context-provider".
This interface define a single method `getContext()` used to load the [CamelContext](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/CamelContext.java).
Below an example on how to add the ActiveMQ component to the [CamelContext](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/CamelContext.java), which is required when you would like to use the ActiveMQ component.
Scala
: @@snip [Introduction.scala](/akka-docs/src/test/scala/docs/camel/Introduction.scala) { #CamelExtensionAddComponent }
Java
: @@snip [CamelExtensionTest.java](/akka-docs/src/test/java/jdocs/camel/CamelExtensionTest.java) { #CamelExtensionAddComponent }
The [CamelContext](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/CamelContext.java) joins the lifecycle of the `ActorSystem` and `CamelExtension` it is associated with; the [CamelContext](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/CamelContext.java) is started when
the `CamelExtension` is created, and it is shut down when the associated `ActorSystem` is shut down. The same is true for the `ProducerTemplate`.
The `CamelExtension` is used by both `Producer` and `Consumer` actors to interact with Apache Camel internally.
You can access the `CamelExtension` inside a `Producer` or a `Consumer` using the `camel` @scala[definition]@java[method], or get straight at the `CamelContext`
using the @scala[`camelContext` definition]@java[`getCamelContext` method or to the `ProducerTemplate` using the `getProducerTemplate` method].
Actors are created and started asynchronously. When a `Consumer` actor is created, the `Consumer` is published at its Camel endpoint (more precisely, the route is added to the [CamelContext](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/CamelContext.java) from the [Endpoint](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/Endpoint.java) to the actor).
When a `Producer` actor is created, a [SendProcessor](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/processor/SendProcessor.java) and [Endpoint](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/Endpoint.java) are created so that the Producer can send messages to it.
Publication is done asynchronously; setting up an endpoint may still be in progress after you have
requested the actor to be created. Some Camel components can take a while to startup, and in some cases you might want to know when the endpoints are activated and ready to be used.
The @extref[Camel](github:akka-camel/src/main/scala/akka/camel/Camel.scala) @scala[trait]@java[interface] allows you to find out when the endpoint is activated or deactivated.
Scala
: @@snip [Introduction.scala](/akka-docs/src/test/scala/docs/camel/Introduction.scala) { #CamelActivation }
Java
: @@snip [ActivationTestBase.java](/akka-docs/src/test/java/jdocs/camel/ActivationTestBase.java) { #CamelActivation }
The above code shows that you can get a `Future` to the activation of the route from the endpoint to the actor, or you can wait in a blocking fashion on the activation of the route.
An `ActivationTimeoutException` is thrown if the endpoint could not be activated within the specified timeout. Deactivation works in a similar fashion:
Scala
: @@snip [Introduction.scala](/akka-docs/src/test/scala/docs/camel/Introduction.scala) { #CamelDeactivation }
Java
: @@snip [ActivationTestBase.java](/akka-docs/src/test/java/jdocs/camel/ActivationTestBase.java) { #CamelDeactivation }
Deactivation of a Consumer or a Producer actor happens when the actor is terminated. For a Consumer, the route to the actor is stopped. For a Producer, the [SendProcessor](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/processor/SendProcessor.java) is stopped.
A `DeActivationTimeoutException` is thrown if the associated camel objects could not be deactivated within the specified timeout.
## Consumer Actors
For objects to receive messages, they must @scala[mixin the @extref[Consumer](github:akka-camel/src/main/scala/akka/camel/Consumer.scala) trait]@java[inherit from the @extref[UntypedConsumerActor](github:akka-camel/src/main/scala/akka/camel/javaapi/UntypedConsumer.scala) class].
For example, the following actor class (Consumer1) implements the
@scala[`endpointUri`]@java[`getEndpointUri`] method, which is declared in the @scala[`Consumer` trait]@java[@extref[UntypedConsumerActor](github:akka-camel/src/main/scala/akka/camel/javaapi/UntypedConsumer.scala) class], in order to receive
messages from the `file:data/input/actor` Camel endpoint.
Scala
: @@snip [Consumers.scala](/akka-docs/src/test/scala/docs/camel/Consumers.scala) { #Consumer1 }
Java
: @@snip [Consumer1.java](/akka-docs/src/test/java/jdocs/camel/Consumer1.java) { #Consumer1 }
Whenever a file is put into the data/input/actor directory, its content is
picked up by the Camel [file component](http://camel.apache.org/file2.html) and sent as message to the
actor. Messages consumed by actors from Camel endpoints are of type
[CamelMessage](#camelmessage). These are immutable representations of Camel messages.
Here's another example that sets the endpointUri to
`jetty:http://localhost:8877/camel/default`. It causes Camel's Jetty
component to start an embedded [Jetty](http://www.eclipse.org/jetty/) server, accepting HTTP connections
from localhost on port 8877.
Scala
: @@snip [Consumers.scala](/akka-docs/src/test/scala/docs/camel/Consumers.scala) { #Consumer2 }
Java
: @@snip [Consumer2.java](/akka-docs/src/test/java/jdocs/camel/Consumer2.java) { #Consumer2 }
After starting the actor, clients can send messages to that actor by POSTing to
`http://localhost:8877/camel/default`. The actor sends a response by using the
sender @scala[`!`]@java[`getSender().tell`] method. For returning a message body and headers to the HTTP
client the response type should be [CamelMessage](#camelmessage). For any other response type, a
new CamelMessage object is created by akka-camel with the actor response as message
body.
<a id="camel-acknowledgements"></a>
### Delivery acknowledgements
With in-out message exchanges, clients usually know that a message exchange is
done when they receive a reply from a consumer actor. The reply message can be a
CamelMessage (or any object which is then internally converted to a CamelMessage) on
success, and a Failure message on failure.
With in-only message exchanges, by default, an exchange is done when a message
is added to the consumer actor's mailbox. Any failure or exception that occurs
during processing of that message by the consumer actor cannot be reported back
to the endpoint in this case. To allow consumer actors to positively or
negatively acknowledge the receipt of a message from an in-only message
exchange, they need to override the `autoAck` method to return false.
In this case, consumer actors must reply either with a
special akka.camel.Ack message (positive acknowledgement) or a akka.actor.Status.Failure (negative
acknowledgement).
Scala
: @@snip [Consumers.scala](/akka-docs/src/test/scala/docs/camel/Consumers.scala) { #Consumer3 }
Java
: @@snip [Consumer3.java](/akka-docs/src/test/java/jdocs/camel/Consumer3.java) { #Consumer3 }
<a id="camel-timeout"></a>
### Consumer timeout
Camel Exchanges (and their corresponding endpoints) that support two-way communications need to wait for a response from
an actor before returning it to the initiating client.
For some endpoint types, timeout values can be defined in an endpoint-specific
way which is described in the documentation of the individual Camel
components. Another option is to configure timeouts on the level of consumer actors.
Two-way communications between a Camel endpoint and an actor are
initiated by sending the request message to the actor with the @scala[@extref[ask](github:akka-actor/src/main/scala/akka/pattern/AskSupport.scala)]@java[@extref[ask](github:akka-actor/src/main/scala/akka/pattern/Patterns.scala)] pattern
and the actor replies to the endpoint when the response is ready. The ask request to the actor can timeout, which will
result in the [Exchange](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/Exchange.java) failing with a TimeoutException set on the failure of the [Exchange](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/Exchange.java).
The timeout on the consumer actor can be overridden with the `replyTimeout`, as shown below.
Scala
: @@snip [Consumers.scala](/akka-docs/src/test/scala/docs/camel/Consumers.scala) { #Consumer4 }
Java
: @@snip [Consumer4.java](/akka-docs/src/test/java/jdocs/camel/Consumer4.java) { #Consumer4 }
## Producer Actors
For sending messages to Camel endpoints, actors need to @scala[mixin the @extref[Producer](github:akka-camel/src/main/scala/akka/camel/Producer.scala) trait]
@java[inherit from the @extref[UntypedProducerActor](github:akka-camel/src/main/scala/akka/camel/javaapi/UntypedProducerActor.scala) class] and implement the `getEndpointUri` method.
Scala
: @@snip [Producers.scala](/akka-docs/src/test/scala/docs/camel/Producers.scala) { #Producer1 }
Java
: @@snip [Producer1.java](/akka-docs/src/test/java/jdocs/camel/Producer1.java) { #Producer1 }
Producer1 inherits a default implementation of the @scala[`receive`]@java[`onReceive`] method from the
@scala[Producer trait]@java[@extref[UntypedProducerActor](github:akka-camel/src/main/scala/akka/camel/javaapi/UntypedProducerActor.scala)] class. To customize a producer actor's default behavior you must override the
@scala[@extref[Producer](github:akka-camel/src/main/scala/akka/camel/Producer.scala).transformResponse]@java[@extref[UntypedProducerActor](github:akka-camel/src/main/scala/akka/camel/javaapi/UntypedProducerActor.scala).onTransformResponse] and
@scala[@extref[Producer](github:akka-camel/src/main/scala/akka/camel/Producer.scala).transformOutgoingMessage methods]@java[@extref[UntypedProducerActor](github:akka-camel/src/main/scala/akka/camel/javaapi/UntypedProducerActor.scala).onTransformOutgoingMessage methods]. This is explained later in more detail.
Producer Actors cannot override the @scala[default @extref[Producer](github:akka-camel/src/main/scala/akka/camel/Producer.scala).receive]@java[@extref[UntypedProducerActor](github:akka-camel/src/main/scala/akka/camel/javaapi/UntypedProducerActor.scala).onReceive] method.
Any message sent to a @scala[@extref[`Producer`](github:akka-camel/src/main/scala/akka/camel/Producer.scala)]@java[Producer] actor will be sent to
the associated Camel endpoint, in the above example to
`http://localhost:8080/news`. The @scala[@extref[`Producer`](github:akka-camel/src/main/scala/akka/camel/Producer.scala)]@java[@extref[`UntypedProducerActor`](github:akka-camel/src/main/scala/akka/camel/javaapi/UntypedProducerActor.scala)] always sends messages asynchronously. Response messages (if supported by the
configured endpoint) will, by default, be returned to the original sender. The
following example uses the ask pattern to send a message to a
Producer actor and waits for a response.
Scala
: @@snip [Producers.scala](/akka-docs/src/test/scala/docs/camel/Producers.scala) { #AskProducer }
Java
: @@snip [ProducerTestBase.java](/akka-docs/src/test/java/jdocs/camel/ProducerTestBase.java) { #AskProducer }
The future contains the response `CamelMessage`, or an `AkkaCamelException` when an error occurred, which contains the headers of the response.
<a id="camel-custom-processing"></a>
### Custom Processing
Instead of replying to the initial sender, producer actors can implement custom
response processing by overriding the @scala[`routeResponse`]@java[`onRouteResponse`] method. In the following example, the response
message is forwarded to a target actor instead of being replied to the original
sender.
Scala
: @@snip [Producers.scala](/akka-docs/src/test/scala/docs/camel/Producers.scala) { #RouteResponse }
Java
: @@snip [ResponseReceiver.java](/akka-docs/src/test/java/jdocs/camel/ResponseReceiver.java) { #RouteResponse }
@@snip [Forwarder.java](/akka-docs/src/test/java/jdocs/camel/Forwarder.java) { #RouteResponse }
@@snip [OnRouteResponseTestBase.java](/akka-docs/src/test/java/jdocs/camel/OnRouteResponseTestBase.java) { #RouteResponse }
Before producing messages to endpoints, producer actors can pre-process them by
overriding the @scala[@extref[Producer](github:akka-camel/src/main/scala/akka/camel/Producer.scala).transformOutgoingMessage]
@java[@extref[UntypedProducerActor](github:akka-camel/src/main/scala/akka/camel/javaapi/UntypedProducerActor.scala).onTransformOutgoingMessag] method.
Scala
: @@snip [Producers.scala](/akka-docs/src/test/scala/docs/camel/Producers.scala) { #TransformOutgoingMessage }
Java
: @@snip [Transformer.java](/akka-docs/src/test/java/jdocs/camel/Transformer.java) { #TransformOutgoingMessage }
### Producer configuration options
The interaction of producer actors with Camel endpoints can be configured to be
one-way or two-way (by initiating in-only or in-out message exchanges,
respectively). By default, the producer initiates an in-out message exchange
with the endpoint. For initiating an in-only exchange, producer actors have to override the @scala[`oneway`]@java[`isOneway`] method to return true.
Scala
: @@snip [Producers.scala](/akka-docs/src/test/scala/docs/camel/Producers.scala) { #Oneway }
Java
: @@snip [OnewaySender.java](/akka-docs/src/test/java/jdocs/camel/OnewaySender.java) { #Oneway }
### Message correlation
To correlate request with response messages, applications can set the
`Message.MessageExchangeId` message header.
Scala
: @@snip [Producers.scala](/akka-docs/src/test/scala/docs/camel/Producers.scala) { #Correlate }
Java
: @@snip [ProducerTestBase.java](/akka-docs/src/test/java/jdocs/camel/ProducerTestBase.java) { #Correlate }
### ProducerTemplate
The @scala[@extref[Producer](github:akka-camel/src/main/scala/akka/camel/Producer.scala) trait]@java[@extref[UntypedProducerActor](github:akka-camel/src/main/scala/akka/camel/javaapi/UntypedProducerActor.scala) class] is a very
convenient way for actors to produce messages to Camel endpoints. Actors may also use a Camel
`ProducerTemplate` for producing messages to endpoints.
Scala
: @@snip [Producers.scala](/akka-docs/src/test/scala/docs/camel/Producers.scala) { #ProducerTemplate }
Java
: @@snip [MyActor.java](/akka-docs/src/test/java/jdocs/camel/MyActor.java) { #ProducerTemplate }
For initiating a two-way message exchange, one of the
`ProducerTemplate.request*` methods must be used.
Scala
: @@snip [Producers.scala](/akka-docs/src/test/scala/docs/camel/Producers.scala) { #RequestProducerTemplate }
Java
: @@snip [RequestBodyActor.java](/akka-docs/src/test/java/jdocs/camel/RequestBodyActor.java) { #RequestProducerTemplate }
<a id="camel-asynchronous-routing"></a>
## Asynchronous routing
In-out message exchanges between endpoints and actors are
designed to be asynchronous. This is the case for both, consumer and producer
actors.
* A consumer endpoint sends request messages to its consumer actor using the @scala[`!` (tell) operator ]@java[`tell` method]
and the actor returns responses with @scala[`sender !`]@java[`getSender().tell`] once they are
ready.
* A producer actor sends request messages to its endpoint using Camel's
asynchronous routing engine. Asynchronous responses are wrapped and added to the
producer actor's mailbox for later processing. By default, response messages are
returned to the initial sender but this can be overridden by Producer
implementations (see also description of the @scala[`routeResponse`]@java[`onRouteResponse`] method
in [Custom Processing](#camel-custom-processing)).
However, asynchronous two-way message exchanges, without allocating a thread for
the full duration of exchange, cannot be generically supported by Camel's
asynchronous routing engine alone. This must be supported by the individual
Camel components (from which endpoints are created) as well. They must be
able to suspend any work started for request processing (thereby freeing threads
to do other work) and resume processing when the response is ready. This is
currently the case for a [subset of components](http://camel.apache.org/asynchronous-routing-engine.html)
such as the Jetty component.
All other Camel components can still be used, but they will cause
allocation of a thread for the duration of an in-out message exchange.
If the used Camel component is blocking it might be necessary to use a separate
@ref:[dispatcher](dispatchers.md) for the producer. The Camel processor is
invoked by a child actor of the producer and the dispatcher can be defined in
the deployment section of the configuration. For example, if your producer actor
has path `/user/integration/output` the dispatcher of the child actor can be
defined with:
```
akka.actor.deployment {
/integration/output/* {
dispatcher = my-dispatcher
}
}
```
## Custom Camel routes
In all the examples so far, routes to consumer actors have been automatically
constructed by akka-camel, when the actor was started. Although the default
route construction templates, used by akka-camel internally, are sufficient for
most use cases, some applications may require more specialized routes to actors.
The akka-camel module provides two mechanisms for customizing routes to actors,
which will be explained in this section. These are:
* Usage of [Akka Camel components](#camel-components) to access actors.
Any Camel route can use these components to access Akka actors.
* [Intercepting route construction](#camel-intercepting-route-construction) to actors.
This option gives you the ability to change routes that have already been added to Camel.
Consumer actors have a hook into the route definition process which can be used to change the route.
<a id="camel-components"></a>
### Akka Camel components
Akka actors can be accessed from Camel routes using the actor Camel component. This component can be used to
access any Akka actor (not only consumer actors) from Camel routes, as described in the following sections.
<a id="access-to-actors"></a>
### Access to actors
To access actors from custom Camel routes, the actor Camel
component should be used. It fully supports Camel's [asynchronous routing
engine](http://camel.apache.org/asynchronous-routing-engine.html).
This component accepts the following endpoint URI format:
* `[<actor-path>]?<options>`
where `<actor-path>` is the `ActorPath` to the actor. The `<options>` are
name-value pairs separated by `&` (i.e. `name1=value1&name2=value2&...`).
#### URI options
The following URI options are supported:
|Name | Type | Default | Description |
|-------------|----------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|replyTimeout | Duration | false | The reply timeout, specified in the same way that you use the duration in akka, for instance `10 seconds` except that in the url it is handy to use a + between the amount and the unit, like for example `200+millis` See also [Consumer timeout](#camel-timeout).|
|autoAck | Boolean | true | If set to true, in-only message exchanges are auto-acknowledged when the message is added to the actor's mailbox. If set to false, actors must acknowledge the receipt of the message. See also [Delivery acknowledgements](#camel-acknowledgements). |
Here's an actor endpoint URI example containing an actor path:
```
akka://some-system/user/myconsumer?autoAck=false&replyTimeout=100+millis
```
In the following example, a custom route to an actor is created, using the
actor's path.
The Akka camel package contains an implicit `toActorRouteDefinition` that allows for a route to
reference an `ActorRef` directly as shown in the below example, The route starts from a [Jetty](http://www.eclipse.org/jetty/) endpoint and
ends at the target actor.
Scala
: @@snip [CustomRoute.scala](/akka-docs/src/test/scala/docs/camel/CustomRoute.scala) { #CustomRoute }
Java
: @@snip [Responder.java](/akka-docs/src/test/java/jdocs/camel/Responder.java) { #CustomRoute }
@@snip [CustomRouteBuilder.java](/akka-docs/src/test/java/jdocs/camel/CustomRouteBuilder.java) { #CustomRoute }
@@snip [CustomRouteTestBase.java](/akka-docs/src/test/java/jdocs/camel/CustomRouteTestBase.java) { #CustomRoute }
@java[The `CamelPath.toCamelUri` converts the `ActorRef` to the Camel actor component URI format which points to the actor endpoint as described above.]
When a message is received on the jetty endpoint, it is routed to the `Responder` actor, which in return replies back to the client of
the HTTP request.
<a id="camel-intercepting-route-construction"></a>
### Intercepting route construction
The previous section, [camel components](#camel-components-2), explained how to setup a route to an actor manually.
It was the application's responsibility to define the route and add it to the current CamelContext.
This section explains a more convenient way to define custom routes: akka-camel is still setting up the routes to consumer actors (and adds these routes to the current CamelContext) but applications can define extensions to these routes.
Extensions can be defined with Camel's [Java DSL](http://camel.apache.org/dsl.html) or [Scala DSL](http://camel.apache.org/scala-dsl.html).
For example, an extension could be a custom error handler that redelivers messages from an endpoint to an actor's bounded mailbox when the mailbox was full.
The following examples demonstrate how to extend a route to a consumer actor for
handling exceptions thrown by that actor.
Scala
: @@snip [CustomRoute.scala](/akka-docs/src/test/scala/docs/camel/CustomRoute.scala) { #ErrorThrowingConsumer }
Java
: @@snip [ErrorThrowingConsumer.java](/akka-docs/src/test/java/jdocs/camel/ErrorThrowingConsumer.java) { #ErrorThrowingConsumer }
The above ErrorThrowingConsumer sends the Failure back to the sender in preRestart
because the Exception that is thrown in the actor would
otherwise just crash the actor, by default the actor would be restarted, and the response would never reach the client of the Consumer.
The akka-camel module creates a RouteDefinition instance by calling
from(endpointUri) on a Camel RouteBuilder (where endpointUri is the endpoint URI
of the consumer actor) and passes that instance as argument to the route
definition handler *). The route definition handler then extends the route and
returns a ProcessorDefinition (in the above example, the ProcessorDefinition
returned by the end method. See the [org.apache.camel.model](https://svn.apache.org/repos/asf/camel/tags/camel-2.8.0/camel-core/src/main/java/org/apache/camel/model/) package for
details). After executing the route definition handler, akka-camel finally calls
a to(targetActorUri) on the returned ProcessorDefinition to complete the
route to the consumer actor (where targetActorUri is the actor component URI as described in [Access to actors](#access-to-actors)).
If the actor cannot be found, a `ActorNotRegisteredException` is thrown.
*) Before passing the RouteDefinition instance to the route definition handler,
akka-camel may make some further modifications to it.
## Configuration
There are several configuration properties for the Camel module, please refer
to the @ref:[reference configuration](general/configuration.md#config-akka-camel).
## Additional Resources
For an introduction to akka-camel 2, see also the Peter Gabryanczyk's talk [Migrating akka-camel module to Akka 2.x](http://skillsmatter.com/podcast/scala/akka-2-x).
For an introduction to akka-camel 1, see also the [Appendix E - Akka and Camel](http://www.manning.com/ibsen/appEsample.pdf)
(pdf) of the book [Camel in Action](http://www.manning.com/ibsen/).
Other, more advanced external articles (for version 1) are:
* [Akka Consumer Actors: New Features and Best Practices](http://krasserm.blogspot.com/2011/02/akka-consumer-actors-new-features-and.html)
* [Akka Producer Actors: New Features and Best Practices](http://krasserm.blogspot.com/2011/02/akka-producer-actor-new-features-and.html)
If anyone is interested in setting up akka-camel as a separate community-maintained repository then please get in touch.

View file

@ -429,11 +429,6 @@ Each Akka module has a reference configuration file with the default values.
@@snip [reference.conf](/akka-agent/src/main/resources/reference.conf)
<a id="config-akka-camel"></a>
### akka-camel
@@snip [reference.conf](/akka-camel/src/main/resources/reference.conf)
<a id="config-akka-cluster"></a>
### akka-cluster

View file

@ -11,6 +11,5 @@
* [io-tcp](io-tcp.md)
* [io-udp](io-udp.md)
* [io-dns](io-dns.md)
* [camel](camel.md)
@@@

View file

@ -1,5 +1,14 @@
# Migration Guide 2.5.x to 2.6.x
## akka-camel removed
After being deprecated in 2.5.0, the akka-camel module has been removed in 2.6.
As an alternative we recommend [Alpakka](https://doc.akka.io/docs/alpakka/current/).
This is of course not a drop-in replacement. If there is community interest we
are open to setting up akka-camel as a separate community-maintained
repository.
## Scala 2.11 no longer supported
If you are still using Scala 2.11 then you must upgrade to 2.12 or 2.13

View file

@ -1,57 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #CamelActivation
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import akka.camel.javaapi.UntypedConsumerActor;
import akka.testkit.javadsl.TestKit;
import akka.util.Timeout;
import jdocs.AbstractJavaTest;
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;
import static java.util.concurrent.TimeUnit.SECONDS;
// #CamelActivation
import org.junit.Test;
public class ActivationTestBase extends AbstractJavaTest {
@SuppressWarnings("unused")
@Test
public void testActivation() {
// #CamelActivation
// ..
ActorSystem system = ActorSystem.create("some-system");
Props props = Props.create(MyConsumer.class);
ActorRef producer = system.actorOf(props, "myproducer");
Camel camel = CamelExtension.get(system);
// get a future reference to the activation of the endpoint of the Consumer Actor
Timeout timeout = new Timeout(Duration.create(10, SECONDS));
Future<ActorRef> activationFuture =
camel.activationFutureFor(producer, timeout, system.dispatcher());
// #CamelActivation
// #CamelDeactivation
// ..
system.stop(producer);
// get a future reference to the deactivation of the endpoint of the Consumer Actor
Future<ActorRef> deactivationFuture =
camel.deactivationFutureFor(producer, timeout, system.dispatcher());
// #CamelDeactivation
TestKit.shutdownActorSystem(system);
}
public static class MyConsumer extends UntypedConsumerActor {
public String getEndpointUri() {
return "direct:test";
}
public void onReceive(Object message) {}
}
}

View file

@ -1,38 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
import akka.actor.ActorSystem;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.junit.Test;
public class CamelExtensionTest extends AbstractJavaTest {
@Test
public void getCamelExtension() {
// #CamelExtension
ActorSystem system = ActorSystem.create("some-system");
Camel camel = CamelExtension.get(system);
CamelContext camelContext = camel.context();
ProducerTemplate producerTemplate = camel.template();
// #CamelExtension
TestKit.shutdownActorSystem(system);
}
public void addActiveMQComponent() {
// #CamelExtensionAddComponent
ActorSystem system = ActorSystem.create("some-system");
Camel camel = CamelExtension.get(system);
CamelContext camelContext = camel.context();
// camelContext.addComponent("activemq", ActiveMQComponent.activeMQComponent(
// "vm://localhost?broker.persistent=false"));
// #CamelExtensionAddComponent
TestKit.shutdownActorSystem(system);
}
}

View file

@ -1,27 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #Consumer1
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class Consumer1 extends UntypedConsumerActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public String getEndpointUri() {
return "file:data/input/actor";
}
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
String body = camelMessage.getBodyAs(String.class, getCamelContext());
log.info("Received message: {}", body);
} else unhandled(message);
}
}
// #Consumer1

View file

@ -1,23 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #Consumer2
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
public class Consumer2 extends UntypedConsumerActor {
public String getEndpointUri() {
return "jetty:http://localhost:8877/camel/default";
}
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
String body = camelMessage.getBodyAs(String.class, getCamelContext());
getSender().tell(String.format("Received message: %s", body), getSelf());
} else unhandled(message);
}
}
// #Consumer2

View file

@ -1,34 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #Consumer3
import akka.actor.Status;
import akka.camel.Ack;
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
public class Consumer3 extends UntypedConsumerActor {
@Override
public boolean autoAck() {
return false;
}
public String getEndpointUri() {
return "jms:queue:test";
}
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
getSender().tell(Ack.getInstance(), getSelf());
// on success
// ..
Exception someException = new Exception("e1");
// on failure
getSender().tell(new Status.Failure(someException), getSelf());
} else unhandled(message);
}
}
// #Consumer3

View file

@ -1,34 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #Consumer4
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
import java.util.concurrent.TimeUnit;
public class Consumer4 extends UntypedConsumerActor {
private static final FiniteDuration timeout = Duration.create(500, TimeUnit.MILLISECONDS);
@Override
public FiniteDuration replyTimeout() {
return timeout;
}
public String getEndpointUri() {
return "jetty:http://localhost:8877/camel/default";
}
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
String body = camelMessage.getBodyAs(String.class, getCamelContext());
getSender().tell(String.format("Hello %s", body), getSelf());
} else unhandled(message);
}
}
// #Consumer4

View file

@ -1,22 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #CustomRoute
import akka.actor.ActorRef;
import akka.camel.internal.component.CamelPath;
import org.apache.camel.builder.RouteBuilder;
public class CustomRouteBuilder extends RouteBuilder {
private String uri;
public CustomRouteBuilder(ActorRef responder) {
uri = CamelPath.toUri(responder);
}
public void configure() throws Exception {
from("jetty:http://localhost:8877/camel/custom").to(uri);
}
}
// #CustomRoute

View file

@ -1,27 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import akka.testkit.javadsl.TestKit;
public class CustomRouteTestBase {
public void customRoute() throws Exception {
// #CustomRoute
ActorSystem system = ActorSystem.create("some-system");
try {
Camel camel = CamelExtension.get(system);
ActorRef responder = system.actorOf(Props.create(Responder.class), "TestResponder");
camel.context().addRoutes(new CustomRouteBuilder(responder));
// #CustomRoute
} finally {
TestKit.shutdownActorSystem(system);
}
}
}

View file

@ -1,57 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #ErrorThrowingConsumer
import akka.actor.Status;
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
import akka.dispatch.Mapper;
import org.apache.camel.builder.Builder;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.model.RouteDefinition;
import scala.Option;
public class ErrorThrowingConsumer extends UntypedConsumerActor {
private String uri;
private static Mapper<RouteDefinition, ProcessorDefinition<?>> mapper =
new Mapper<RouteDefinition, ProcessorDefinition<?>>() {
public ProcessorDefinition<?> apply(RouteDefinition rd) {
// Catch any exception and handle it by returning the exception message
// as response
return rd.onException(Exception.class)
.handled(true)
.transform(Builder.exceptionMessage())
.end();
}
};
public ErrorThrowingConsumer(String uri) {
this.uri = uri;
}
public String getEndpointUri() {
return uri;
}
public void onReceive(Object message) throws Exception {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
String body = camelMessage.getBodyAs(String.class, getCamelContext());
throw new Exception(String.format("error: %s", body));
} else unhandled(message);
}
@Override
public Mapper<RouteDefinition, ProcessorDefinition<?>> getRouteDefinitionHandler() {
return mapper;
}
@Override
public void preRestart(Throwable reason, Option<Object> message) {
getSender().tell(new Status.Failure(reason), getSelf());
}
}
// #ErrorThrowingConsumer

View file

@ -1,15 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #Producer1
import akka.camel.javaapi.UntypedProducerActor;
public class FirstProducer extends UntypedProducerActor {
public String getEndpointUri() {
return "http://localhost:8080/news";
}
}
// #Producer1

View file

@ -1,28 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #RouteResponse
import akka.actor.ActorRef;
import akka.camel.javaapi.UntypedProducerActor;
public class Forwarder extends UntypedProducerActor {
private String uri;
private ActorRef target;
public Forwarder(String uri, ActorRef target) {
this.uri = uri;
this.target = target;
}
public String getEndpointUri() {
return uri;
}
@Override
public void onRouteResponse(Object message) {
target.forward(message, getContext());
}
}
// #RouteResponse

View file

@ -1,19 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #ProducerTemplate
import akka.actor.UntypedAbstractActor;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import org.apache.camel.ProducerTemplate;
public class MyActor extends UntypedAbstractActor {
public void onReceive(Object message) {
Camel camel = CamelExtension.get(getContext().getSystem());
ProducerTemplate template = camel.template();
template.sendBody("direct:news", message);
}
}
// #ProducerTemplate

View file

@ -1,34 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #Consumer-mina
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
public class MyEndpoint extends UntypedConsumerActor {
private String uri;
public String getEndpointUri() {
return uri;
}
public void onReceive(Object message) throws Exception {
if (message instanceof CamelMessage) {
/* ... */
} else unhandled(message);
}
// Extra constructor to change the default uri,
// for instance to "jetty:http://localhost:8877/example"
public MyEndpoint(String uri) {
this.uri = uri;
}
public MyEndpoint() {
this.uri = "mina2:tcp://localhost:6200?textline=true";
}
}
// #Consumer-mina

View file

@ -1,27 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
import akka.actor.*;
import akka.testkit.javadsl.TestKit;
public class OnRouteResponseTestBase {
public void onRouteResponse() {
// #RouteResponse
ActorSystem system = ActorSystem.create("some-system");
Props receiverProps = Props.create(ResponseReceiver.class);
final ActorRef receiver = system.actorOf(receiverProps, "responseReceiver");
ActorRef forwardResponse =
system.actorOf(Props.create(Forwarder.class, "http://localhost:8080/news/akka", receiver));
// the Forwarder sends out a request to the web page and forwards the response to
// the ResponseReceiver
forwardResponse.tell("some request", ActorRef.noSender());
// #RouteResponse
system.stop(receiver);
system.stop(forwardResponse);
TestKit.shutdownActorSystem(system);
}
}

View file

@ -1,25 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #Oneway
import akka.camel.javaapi.UntypedProducerActor;
public class OnewaySender extends UntypedProducerActor {
private String uri;
public OnewaySender(String uri) {
this.uri = uri;
}
public String getEndpointUri() {
return uri;
}
@Override
public boolean isOneway() {
return true;
}
}
// #Oneway

View file

@ -1,14 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #Producer
import akka.camel.javaapi.UntypedProducerActor;
public class Orders extends UntypedProducerActor {
public String getEndpointUri() {
return "jms:queue:Orders";
}
}
// #Producer

View file

@ -1,14 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #Producer1
import akka.camel.javaapi.UntypedProducerActor;
public class Producer1 extends UntypedProducerActor {
public String getEndpointUri() {
return "http://localhost:8080/news";
}
}
// #Producer1

View file

@ -1,57 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletionStage;
import akka.pattern.Patterns;
import akka.testkit.javadsl.TestKit;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.camel.CamelMessage;
public class ProducerTestBase {
public void tellJmsProducer() {
// #TellProducer
ActorSystem system = ActorSystem.create("some-system");
Props props = Props.create(Orders.class);
ActorRef producer = system.actorOf(props, "jmsproducer");
producer.tell("<order amount=\"100\" currency=\"PLN\" itemId=\"12345\"/>", ActorRef.noSender());
// #TellProducer
TestKit.shutdownActorSystem(system);
}
@SuppressWarnings("unused")
public void askProducer() {
// #AskProducer
ActorSystem system = ActorSystem.create("some-system");
Props props = Props.create(FirstProducer.class);
ActorRef producer = system.actorOf(props, "myproducer");
CompletionStage<Object> future =
Patterns.ask(producer, "some request", Duration.ofMillis(1000L));
// #AskProducer
system.stop(producer);
TestKit.shutdownActorSystem(system);
}
public void correlate() {
// #Correlate
ActorSystem system = ActorSystem.create("some-system");
Props props = Props.create(Orders.class);
ActorRef producer = system.actorOf(props, "jmsproducer");
Map<String, Object> headers = new HashMap<String, Object>();
headers.put(CamelMessage.MessageExchangeId(), "123");
producer.tell(
new CamelMessage("<order amount=\"100\" currency=\"PLN\" " + "itemId=\"12345\"/>", headers),
ActorRef.noSender());
// #Correlate
system.stop(producer);
TestKit.shutdownActorSystem(system);
}
}

View file

@ -1,25 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #RequestProducerTemplate
import akka.actor.AbstractActor;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import org.apache.camel.ProducerTemplate;
public class RequestBodyActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(
message -> {
Camel camel = CamelExtension.get(getContext().getSystem());
ProducerTemplate template = camel.template();
getSender().tell(template.requestBody("direct:news", message), getSelf());
})
.build();
}
}
// #RequestProducerTemplate

View file

@ -1,30 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #CustomRoute
import akka.actor.UntypedAbstractActor;
import akka.camel.CamelMessage;
import akka.dispatch.Mapper;
public class Responder extends UntypedAbstractActor {
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
getSender().tell(createResponse(camelMessage), getSelf());
} else unhandled(message);
}
private CamelMessage createResponse(CamelMessage msg) {
return msg.mapBody(
new Mapper<String, String>() {
@Override
public String apply(String body) {
return String.format("received %s", body);
}
});
}
}
// #CustomRoute

View file

@ -1,17 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #RouteResponse
import akka.actor.UntypedAbstractActor;
import akka.camel.CamelMessage;
public class ResponseReceiver extends UntypedAbstractActor {
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
// do something with the forwarded response
}
}
}
// #RouteResponse

View file

@ -1,42 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.camel;
// #TransformOutgoingMessage
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedProducerActor;
import akka.dispatch.Mapper;
public class Transformer extends UntypedProducerActor {
private String uri;
public Transformer(String uri) {
this.uri = uri;
}
public String getEndpointUri() {
return uri;
}
private CamelMessage upperCase(CamelMessage msg) {
return msg.mapBody(
new Mapper<String, String>() {
@Override
public String apply(String body) {
return body.toUpperCase();
}
});
}
@Override
public Object onTransformOutgoingMessage(Object message) {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
return upperCase(camelMessage);
} else {
return message;
}
}
}
// #TransformOutgoingMessage

View file

@ -1,73 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package docs.camel
import language.postfixOps
object Consumers {
object Sample1 {
//#Consumer1
import akka.camel.{ CamelMessage, Consumer }
class Consumer1 extends Consumer {
def endpointUri = "file:data/input/actor"
def receive = {
case msg: CamelMessage => println("received %s".format(msg.bodyAs[String]))
}
}
//#Consumer1
}
object Sample2 {
//#Consumer2
import akka.camel.{ CamelMessage, Consumer }
class Consumer2 extends Consumer {
def endpointUri = "jetty:http://localhost:8877/camel/default"
def receive = {
case msg: CamelMessage => sender() ! ("Hello %s".format(msg.bodyAs[String]))
}
}
//#Consumer2
}
object Sample3 {
//#Consumer3
import akka.camel.{ CamelMessage, Consumer }
import akka.camel.Ack
import akka.actor.Status.Failure
class Consumer3 extends Consumer {
override def autoAck = false
def endpointUri = "jms:queue:test"
def receive = {
case msg: CamelMessage =>
sender() ! Ack
// on success
// ..
val someException = new Exception("e1")
// on failure
sender() ! Failure(someException)
}
}
//#Consumer3
}
object Sample4 {
//#Consumer4
import akka.camel.{ CamelMessage, Consumer }
import scala.concurrent.duration._
class Consumer4 extends Consumer {
def endpointUri = "jetty:http://localhost:8877/camel/default"
override def replyTimeout = 500 millis
def receive = {
case msg: CamelMessage => sender() ! ("Hello %s".format(msg.bodyAs[String]))
}
}
//#Consumer4
}
}

View file

@ -1,61 +0,0 @@
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package docs.camel
import akka.camel.CamelMessage
import akka.actor.Status.Failure
import language.existentials
object CustomRoute {
object Sample1 {
//#CustomRoute
import akka.actor.{ Actor, ActorRef, ActorSystem, Props }
import akka.camel.{ CamelExtension, CamelMessage }
import org.apache.camel.builder.RouteBuilder
import akka.camel._
class Responder extends Actor {
def receive = {
case msg: CamelMessage =>
sender() ! (msg.mapBody { body: String =>
"received %s".format(body)
})
}
}
class CustomRouteBuilder(system: ActorSystem, responder: ActorRef) extends RouteBuilder {
def configure: Unit = {
from("jetty:http://localhost:8877/camel/custom").to(responder)
}
}
val system = ActorSystem("some-system")
val camel = CamelExtension(system)
val responder = system.actorOf(Props[Responder], name = "TestResponder")
camel.context.addRoutes(new CustomRouteBuilder(system, responder))
//#CustomRoute
}
object Sample2 {
//#ErrorThrowingConsumer
import akka.camel.Consumer
import org.apache.camel.builder.Builder
import org.apache.camel.model.RouteDefinition
class ErrorThrowingConsumer(override val endpointUri: String) extends Consumer {
def receive = {
case msg: CamelMessage => throw new Exception("error: %s".format(msg.body))
}
override def onRouteDefinition =
(rd) => rd.onException(classOf[Exception]).handled(true).transform(Builder.exceptionMessage).end
final override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
sender() ! Failure(reason)
}
}
//#ErrorThrowingConsumer
}
}

View file

@ -1,111 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package docs.camel
//#imports
import akka.actor.{ ActorSystem, Props }
import akka.camel.CamelExtension
import language.postfixOps
import akka.util.Timeout
//#imports
object Introduction {
def foo(): Unit = {
//#Consumer-mina
import akka.camel.{ CamelMessage, Consumer }
class MyEndpoint extends Consumer {
def endpointUri = "mina2:tcp://localhost:6200?textline=true"
def receive = {
case msg: CamelMessage => { /* ... */ }
case _ => { /* ... */ }
}
}
// start and expose actor via tcp
import akka.actor.{ ActorSystem, Props }
val system = ActorSystem("some-system")
val mina = system.actorOf(Props[MyEndpoint])
//#Consumer-mina
}
def bar(): Unit = {
//#Consumer
import akka.camel.{ CamelMessage, Consumer }
class MyEndpoint extends Consumer {
def endpointUri = "jetty:http://localhost:8877/example"
def receive = {
case msg: CamelMessage => { /* ... */ }
case _ => { /* ... */ }
}
}
//#Consumer
}
def baz(): Unit = {
//#Producer
import akka.actor.Actor
import akka.camel.{ Oneway, Producer }
import akka.actor.{ ActorSystem, Props }
class Orders extends Actor with Producer with Oneway {
def endpointUri = "jms:queue:Orders"
}
val sys = ActorSystem("some-system")
val orders = sys.actorOf(Props[Orders])
orders ! <order amount="100" currency="PLN" itemId="12345"/>
//#Producer
}
{
//#CamelExtension
val system = ActorSystem("some-system")
val camel = CamelExtension(system)
val camelContext = camel.context
val producerTemplate = camel.template
//#CamelExtension
}
{
//#CamelExtensionAddComponent
// import org.apache.activemq.camel.component.ActiveMQComponent
val system = ActorSystem("some-system")
val camel = CamelExtension(system)
val camelContext = camel.context
// camelContext.addComponent("activemq", ActiveMQComponent.activeMQComponent(
// "vm://localhost?broker.persistent=false"))
//#CamelExtensionAddComponent
}
{
//#CamelActivation
import akka.camel.{ CamelMessage, Consumer }
import scala.concurrent.duration._
class MyEndpoint extends Consumer {
def endpointUri = "mina2:tcp://localhost:6200?textline=true"
def receive = {
case msg: CamelMessage => { /* ... */ }
case _ => { /* ... */ }
}
}
val system = ActorSystem("some-system")
val camel = CamelExtension(system)
val actorRef = system.actorOf(Props[MyEndpoint])
// get a future reference to the activation of the endpoint of the Consumer Actor
val activationFuture = camel.activationFutureFor(actorRef)(timeout = 10 seconds, executor = system.dispatcher)
//#CamelActivation
//#CamelDeactivation
system.stop(actorRef)
// get a future reference to the deactivation of the endpoint of the Consumer Actor
val deactivationFuture = camel.deactivationFutureFor(actorRef)(timeout = 10 seconds, executor = system.dispatcher)
//#CamelDeactivation
}
}

View file

@ -1,132 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package docs.camel
import akka.camel.CamelExtension
import language.postfixOps
object Producers {
object Sample1 {
//#Producer1
import akka.actor.Actor
import akka.actor.{ ActorSystem, Props }
import akka.camel.{ CamelMessage, Producer }
import akka.util.Timeout
class Producer1 extends Actor with Producer {
def endpointUri = "http://localhost:8080/news"
}
//#Producer1
//#AskProducer
import akka.pattern.ask
import scala.concurrent.duration._
implicit val timeout = Timeout(10 seconds)
val system = ActorSystem("some-system")
val producer = system.actorOf(Props[Producer1])
val future = producer.ask("some request").mapTo[CamelMessage]
//#AskProducer
}
object Sample2 {
//#RouteResponse
import akka.actor.{ Actor, ActorRef }
import akka.camel.{ CamelMessage, Producer }
import akka.actor.{ ActorSystem, Props }
class ResponseReceiver extends Actor {
def receive = {
case msg: CamelMessage =>
// do something with the forwarded response
}
}
class Forwarder(uri: String, target: ActorRef) extends Actor with Producer {
def endpointUri = uri
override def routeResponse(msg: Any): Unit = { target.forward(msg) }
}
val system = ActorSystem("some-system")
val receiver = system.actorOf(Props[ResponseReceiver])
val forwardResponse = system.actorOf(Props(classOf[Forwarder], this, "http://localhost:8080/news/akka", receiver))
// the Forwarder sends out a request to the web page and forwards the response to
// the ResponseReceiver
forwardResponse ! "some request"
//#RouteResponse
}
object Sample3 {
//#TransformOutgoingMessage
import akka.actor.Actor
import akka.camel.{ CamelMessage, Producer }
class Transformer(uri: String) extends Actor with Producer {
def endpointUri = uri
def upperCase(msg: CamelMessage) = msg.mapBody { body: String =>
body.toUpperCase
}
override def transformOutgoingMessage(msg: Any) = msg match {
case msg: CamelMessage => upperCase(msg)
}
}
//#TransformOutgoingMessage
}
object Sample4 {
//#Oneway
import akka.actor.{ Actor, ActorSystem, Props }
import akka.camel.Producer
class OnewaySender(uri: String) extends Actor with Producer {
def endpointUri = uri
override def oneway: Boolean = true
}
val system = ActorSystem("some-system")
val producer = system.actorOf(Props(classOf[OnewaySender], this, "activemq:FOO.BAR"))
producer ! "Some message"
//#Oneway
}
object Sample5 {
//#Correlate
import akka.camel.{ CamelMessage, Producer }
import akka.actor.Actor
import akka.actor.{ ActorSystem, Props }
class Producer2 extends Actor with Producer {
def endpointUri = "activemq:FOO.BAR"
}
val system = ActorSystem("some-system")
val producer = system.actorOf(Props[Producer2])
producer ! CamelMessage("bar", Map(CamelMessage.MessageExchangeId -> "123"))
//#Correlate
}
object Sample6 {
//#ProducerTemplate
import akka.actor.Actor
class MyActor extends Actor {
def receive = {
case msg =>
val template = CamelExtension(context.system).template
template.sendBody("direct:news", msg)
}
}
//#ProducerTemplate
}
object Sample7 {
//#RequestProducerTemplate
import akka.actor.Actor
class MyActor extends Actor {
def receive = {
case msg =>
val template = CamelExtension(context.system).template
sender() ! template.requestBody("direct:news", msg)
}
}
//#RequestProducerTemplate
}
}

View file

@ -1,49 +0,0 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package docs.camel
object PublishSubscribe {
//#PubSub
import akka.actor.{ Actor, ActorRef, ActorSystem, Props }
import akka.camel.{ CamelMessage, Consumer, Producer }
class Subscriber(name: String, uri: String) extends Actor with Consumer {
def endpointUri = uri
def receive = {
case msg: CamelMessage => println("%s received: %s".format(name, msg.body))
}
}
class Publisher(name: String, uri: String) extends Actor with Producer {
def endpointUri = uri
// one-way communication with JMS
override def oneway = true
}
class PublisherBridge(uri: String, publisher: ActorRef) extends Actor with Consumer {
def endpointUri = uri
def receive = {
case msg: CamelMessage => {
publisher ! msg.bodyAs[String]
sender() ! ("message published")
}
}
}
// Add below to a Boot class
// Setup publish/subscribe example
val system = ActorSystem("some-system")
val jmsUri = "jms:topic:test"
val jmsSubscriber1 = system.actorOf(Props(classOf[Subscriber], "jms-subscriber-1", jmsUri))
val jmsSubscriber2 = system.actorOf(Props(classOf[Subscriber], "jms-subscriber-2", jmsUri))
val jmsPublisher = system.actorOf(Props(classOf[Publisher], "jms-publisher", jmsUri))
val jmsPublisherBridge =
system.actorOf(Props(classOf[PublisherBridge], "jetty:http://0.0.0.0:8877/camel/pub/jms", jmsPublisher))
//#PubSub
}

View file

@ -43,7 +43,6 @@ lazy val aggregatedProjects: Seq[ProjectReference] = List[ProjectReference](
agent,
benchJmh,
benchJmhTyped,
camel,
cluster,
clusterMetrics,
clusterSharding,
@ -125,12 +124,6 @@ lazy val benchJmhTyped = akkaModule("akka-bench-jmh-typed")
.enablePlugins(JmhPlugin, ScaladocNoVerificationOfDiagrams, NoPublish, CopyrightHeader)
.disablePlugins(MimaPlugin, WhiteSourcePlugin, ValidatePullRequest, CopyrightHeaderInPr)
lazy val camel = akkaModule("akka-camel")
.dependsOn(actor, slf4j, testkit % "test->test")
.settings(Dependencies.camel)
.settings(AutomaticModuleName.settings("akka.camel"))
.settings(OSGi.camel)
lazy val cluster = akkaModule("akka-cluster")
.dependsOn(remote, remoteTests % "test->test", testkit % "test->test")
.settings(Dependencies.cluster)
@ -220,7 +213,6 @@ lazy val docs = akkaModule("akka-docs")
distributedData,
stream,
actorTyped,
camel % "compile->compile;test->test",
clusterTools % "compile->compile;test->test",
clusterSharding % "compile->compile;test->test",
testkit % "compile->compile;test->test",

View file

@ -36,8 +36,6 @@ object Dependencies {
object Compile {
// Compile
val camelCore = ("org.apache.camel" % "camel-core" % "2.17.7").exclude("org.slf4j", "slf4j-api") // ApacheV2
// when updating config version, update links ActorSystem ScalaDoc to link to the updated version
val config = "com.typesafe" % "config" % "1.3.3" // ApacheV2
val netty = "io.netty" % "netty" % "3.10.6.Final" // ApacheV2
@ -118,8 +116,6 @@ object Dependencies {
// If changed, update akka-docs/build.sbt as well
val sigarLoader = "io.kamon" % "sigar-loader" % "1.6.6-rev002" % "optional;provided;test" // ApacheV2
// Non-default module in Java9, removed in Java11. For Camel.
val jaxb = "javax.xml.bind" % "jaxb-api" % "2.3.0" % "provided;test"
val activation = "com.sun.activation" % "javax.activation" % "1.2.0" % "provided;test"
val levelDB = "org.iq80.leveldb" % "leveldb" % "0.10" % "optional;provided" // ApacheV2
@ -207,16 +203,6 @@ object Dependencies {
val persistenceShared = l ++= Seq(Provided.levelDB, Provided.levelDBNative)
val camel = l ++= Seq(
camelCore,
Provided.jaxb,
Provided.activation,
Test.scalatest.value,
Test.junit,
Test.mockito,
Test.logback,
Test.commonsIo)
val osgi = l ++= Seq(
osgiCore,
osgiCompendium,

View file

@ -48,8 +48,6 @@ object OSGi {
val agent = exports(Seq("akka.agent.*"))
val camel = exports(Seq("akka.camel.*"))
val cluster = exports(Seq("akka.cluster.*"))
val clusterTools = exports(Seq("akka.cluster.singleton.*", "akka.cluster.client.*", "akka.cluster.pubsub.*"))