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:
parent
39b344c508
commit
96c8ef4257
80 changed files with 12 additions and 6538 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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]
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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()
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
akka{
|
||||
loglevel = "ERROR"
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,5 @@
|
|||
* [io-tcp](io-tcp.md)
|
||||
* [io-udp](io-udp.md)
|
||||
* [io-dns](io-dns.md)
|
||||
* [camel](camel.md)
|
||||
|
||||
@@@
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.*"))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue