Separated core from modules, moved modules to akka-modules repository
This commit is contained in:
parent
7f0358277b
commit
f97d04f1b7
306 changed files with 5 additions and 25602 deletions
|
|
@ -1,34 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package akka.camel;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation used by implementations of {@link akka.actor.TypedActor}
|
||||
* (on method-level) to define consumer endpoints.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface consume {
|
||||
|
||||
/**
|
||||
* Consumer endpoint URI
|
||||
*/
|
||||
public abstract String value();
|
||||
|
||||
/**
|
||||
* Route definition handler class for customizing route to annotated method.
|
||||
* The handler class must have a default constructor.
|
||||
*/
|
||||
public abstract Class<? extends RouteDefinitionHandler> routeDefinitionHandler()
|
||||
default RouteDefinitionIdentity.class;
|
||||
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
class=akka.camel.component.ActorComponent
|
||||
|
|
@ -1 +0,0 @@
|
|||
class=akka.camel.component.TypedActorComponent
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package akka.camel
|
||||
|
||||
import java.util.Map
|
||||
|
||||
import org.apache.camel.{ProducerTemplate, CamelContext}
|
||||
import org.apache.camel.impl.DefaultCamelContext
|
||||
|
||||
import akka.camel.component.TypedActorComponent
|
||||
import akka.japi.{Option => JOption}
|
||||
import akka.util.Logging
|
||||
|
||||
/**
|
||||
* Manages the lifecycle of a CamelContext. Allowed transitions are
|
||||
* init -> start -> stop -> init -> ... etc.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
trait CamelContextLifecycle extends Logging {
|
||||
// TODO: enforce correct state transitions
|
||||
// valid: init -> start -> stop -> init ...
|
||||
|
||||
private var _context: Option[CamelContext] = None
|
||||
private var _template: Option[ProducerTemplate] = None
|
||||
|
||||
private var _initialized = false
|
||||
private var _started = false
|
||||
|
||||
/**
|
||||
* Camel component for accessing typed actors.
|
||||
*/
|
||||
private[camel] var typedActorComponent: TypedActorComponent = _
|
||||
|
||||
/**
|
||||
* Registry in which typed actors are TEMPORARILY registered during
|
||||
* creation of Camel routes to these actors.
|
||||
*/
|
||||
private[camel] var typedActorRegistry: Map[String, AnyRef] = _
|
||||
|
||||
/**
|
||||
* Returns <code>Some(CamelContext)</code> (containing the current CamelContext)
|
||||
* if <code>CamelContextLifecycle</code> has been initialized, otherwise <code>None</code>.
|
||||
*/
|
||||
def context: Option[CamelContext] = _context
|
||||
|
||||
/**
|
||||
* Returns <code>Some(ProducerTemplate)</code> (containing the current ProducerTemplate)
|
||||
* if <code>CamelContextLifecycle</code> has been initialized, otherwise <code>None</code>.
|
||||
*/
|
||||
def template: Option[ProducerTemplate] = _template
|
||||
|
||||
/**
|
||||
* Returns <code>Some(CamelContext)</code> (containing the current CamelContext)
|
||||
* if <code>CamelContextLifecycle</code> has been initialized, otherwise <code>None</code>.
|
||||
* <p>
|
||||
* Java API.
|
||||
*/
|
||||
def getContext: JOption[CamelContext] = context
|
||||
|
||||
/**
|
||||
* Returns <code>Some(ProducerTemplate)</code> (containing the current ProducerTemplate)
|
||||
* if <code>CamelContextLifecycle</code> has been initialized, otherwise <code>None</code>.
|
||||
* <p>
|
||||
* Java API.
|
||||
*/
|
||||
def getTemplate: JOption[ProducerTemplate] = template
|
||||
|
||||
/**
|
||||
* Returns the current <code>CamelContext</code> if this <code>CamelContextLifecycle</code>
|
||||
* has been initialized, otherwise throws an <code>IllegalStateException</code>.
|
||||
*/
|
||||
def mandatoryContext =
|
||||
if (context.isDefined) context.get
|
||||
else throw new IllegalStateException("no current CamelContext")
|
||||
|
||||
/**
|
||||
* Returns the current <code>ProducerTemplate</code> if this <code>CamelContextLifecycle</code>
|
||||
* has been initialized, otherwise throws an <code>IllegalStateException</code>.
|
||||
*/
|
||||
def mandatoryTemplate =
|
||||
if (template.isDefined) template.get
|
||||
else throw new IllegalStateException("no current ProducerTemplate")
|
||||
|
||||
/**
|
||||
* Returns the current <code>CamelContext</code> if this <code>CamelContextLifecycle</code>
|
||||
* has been initialized, otherwise throws an <code>IllegalStateException</code>.
|
||||
* <p>
|
||||
* Java API.
|
||||
*/
|
||||
def getMandatoryContext = mandatoryContext
|
||||
|
||||
/**
|
||||
* Returns the current <code>ProducerTemplate</code> if this <code>CamelContextLifecycle</code>
|
||||
* has been initialized, otherwise throws an <code>IllegalStateException</code>.
|
||||
* <p>
|
||||
* Java API.
|
||||
*/
|
||||
def getMandatoryTemplate = mandatoryTemplate
|
||||
|
||||
def initialized = _initialized
|
||||
def started = _started
|
||||
|
||||
/**
|
||||
* Starts the CamelContext and an associated ProducerTemplate.
|
||||
*/
|
||||
def start = {
|
||||
for {
|
||||
c <- context
|
||||
t <- template
|
||||
} {
|
||||
c.start
|
||||
t.start
|
||||
_started = true
|
||||
log.info("Camel context started")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the CamelContext and the associated ProducerTemplate.
|
||||
*/
|
||||
def stop = {
|
||||
for {
|
||||
t <- template
|
||||
c <- context
|
||||
} {
|
||||
t.stop
|
||||
c.stop
|
||||
_started = false
|
||||
_initialized = false
|
||||
log.info("Camel context stopped")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this lifecycle object with the a DefaultCamelContext.
|
||||
*/
|
||||
def init(): Unit = init(new DefaultCamelContext)
|
||||
|
||||
/**
|
||||
* Initializes this lifecycle object with the given CamelContext. For the passed
|
||||
* CamelContext, stream-caching is enabled. If applications want to disable stream-
|
||||
* caching they can do so after this method returned and prior to calling start.
|
||||
* This method also registers a new TypedActorComponent at the passes CamelContext
|
||||
* under a name defined by TypedActorComponent.InternalSchema.
|
||||
*/
|
||||
def init(context: CamelContext) {
|
||||
this.typedActorComponent = new TypedActorComponent
|
||||
this.typedActorRegistry = typedActorComponent.typedActorRegistry
|
||||
|
||||
context.setStreamCaching(true)
|
||||
context.addComponent(TypedActorComponent.InternalSchema, typedActorComponent)
|
||||
|
||||
this._context = Some(context)
|
||||
this._template = Some(context.createProducerTemplate)
|
||||
|
||||
_initialized = true
|
||||
log.info("Camel context initialized")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages a global CamelContext and an associated ProducerTemplate.
|
||||
*/
|
||||
object CamelContextManager extends CamelContextLifecycle {
|
||||
|
||||
// -----------------------------------------------------
|
||||
// The inherited getters aren't statically accessible
|
||||
// from Java. Therefore, they are redefined here.
|
||||
// TODO: investigate if this is a Scala bug.
|
||||
// -----------------------------------------------------
|
||||
|
||||
/**
|
||||
* see CamelContextLifecycle.getContext
|
||||
* <p>
|
||||
* Java API.
|
||||
*/
|
||||
override def getContext: JOption[CamelContext] = super.getContext
|
||||
|
||||
/**
|
||||
* see CamelContextLifecycle.getTemplate
|
||||
* <p>
|
||||
* Java API.
|
||||
*/
|
||||
override def getTemplate: JOption[ProducerTemplate] = super.getTemplate
|
||||
|
||||
/**
|
||||
* see CamelContextLifecycle.getMandatoryContext
|
||||
* <p>
|
||||
* Java API.
|
||||
*/
|
||||
override def getMandatoryContext = super.getMandatoryContext
|
||||
|
||||
/**
|
||||
* see CamelContextLifecycle.getMandatoryTemplate
|
||||
* <p>
|
||||
* Java API.
|
||||
*/
|
||||
override def getMandatoryTemplate = super.getMandatoryTemplate
|
||||
}
|
||||
|
|
@ -1,275 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
package akka.camel
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import org.apache.camel.CamelContext
|
||||
|
||||
import akka.actor.Actor._
|
||||
import akka.actor.{AspectInitRegistry, ActorRegistry}
|
||||
import akka.config.Config._
|
||||
import akka.japi.{SideEffect, Option => JOption}
|
||||
import akka.util.{Logging, Bootable}
|
||||
|
||||
/**
|
||||
* Publishes (untyped) consumer actors and typed consumer actors via Camel endpoints. Actors
|
||||
* are published (asynchronously) when they are started and unpublished (asynchronously) when
|
||||
* they are stopped. The CamelService is notified about actor start- and stop-events by
|
||||
* registering listeners at ActorRegistry and AspectInitRegistry.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
trait CamelService extends Bootable with Logging {
|
||||
private[camel] val consumerPublisher = actorOf[ConsumerPublisher]
|
||||
private[camel] val publishRequestor = actorOf[PublishRequestor]
|
||||
|
||||
private val serviceEnabled = config.getList("akka.enabled-modules").exists(_ == "camel")
|
||||
|
||||
/**
|
||||
* Starts this CamelService unless <code>akka.camel.service</code> is set to <code>false</code>.
|
||||
*/
|
||||
abstract override def onLoad = {
|
||||
if (serviceEnabled) registerPublishRequestor
|
||||
super.onLoad
|
||||
if (serviceEnabled) start
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops this CamelService unless <code>akka.camel.service</code> is set to <code>false</code>.
|
||||
*/
|
||||
abstract override def onUnload = {
|
||||
if (serviceEnabled) stop
|
||||
super.onUnload
|
||||
}
|
||||
|
||||
@deprecated("use start() instead")
|
||||
def load = start
|
||||
|
||||
@deprecated("use stop() instead")
|
||||
def unload = stop
|
||||
|
||||
/**
|
||||
* Starts this CamelService. Any started actor that is a consumer actor will be (asynchronously)
|
||||
* published as Camel endpoint. Consumer actors that are started after this method returned will
|
||||
* be published as well. Actor publishing is done asynchronously. A started (loaded) CamelService
|
||||
* also publishes <code>@consume</code> annotated methods of typed actors that have been created
|
||||
* with <code>TypedActor.newInstance(..)</code> (and <code>TypedActor.newRemoteInstance(..)</code>
|
||||
* on a remote node).
|
||||
*/
|
||||
def start: CamelService = {
|
||||
if (!publishRequestorRegistered) registerPublishRequestor
|
||||
|
||||
// Only init and start if not already done by application
|
||||
if (!CamelContextManager.initialized) CamelContextManager.init
|
||||
if (!CamelContextManager.started) CamelContextManager.start
|
||||
|
||||
// start actor that exposes consumer actors and typed actors via Camel endpoints
|
||||
consumerPublisher.start
|
||||
|
||||
// init publishRequestor so that buffered and future events are delivered to consumerPublisher
|
||||
publishRequestor ! PublishRequestorInit(consumerPublisher)
|
||||
|
||||
// Register this instance as current CamelService and return it
|
||||
CamelServiceManager.register(this)
|
||||
CamelServiceManager.mandatoryService
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops this CamelService. All published consumer actors and typed consumer actor methods will be
|
||||
* unpublished asynchronously.
|
||||
*/
|
||||
def stop = {
|
||||
// Unregister this instance as current CamelService
|
||||
CamelServiceManager.unregister(this)
|
||||
|
||||
// Remove related listeners from registry
|
||||
unregisterPublishRequestor
|
||||
|
||||
// Stop related services
|
||||
consumerPublisher.stop
|
||||
CamelContextManager.stop
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for an expected number (<code>count</code>) of endpoints to be activated
|
||||
* during execution of <code>f</code>. The wait-timeout is by default 10 seconds.
|
||||
* Other timeout values can be set via the <code>timeout</code> and <code>timeUnit</code>
|
||||
* parameters.
|
||||
*/
|
||||
def awaitEndpointActivation(count: Int, timeout: Long = 10, timeUnit: TimeUnit = TimeUnit.SECONDS)(f: => Unit): Boolean = {
|
||||
val activation = expectEndpointActivationCount(count)
|
||||
f; activation.await(timeout, timeUnit)
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for an expected number (<code>count</code>) of endpoints to be de-activated
|
||||
* during execution of <code>f</code>. The wait-timeout is by default 10 seconds.
|
||||
* Other timeout values can be set via the <code>timeout</code> and <code>timeUnit</code>
|
||||
* parameters.
|
||||
*/
|
||||
def awaitEndpointDeactivation(count: Int, timeout: Long = 10, timeUnit: TimeUnit = TimeUnit.SECONDS)(f: => Unit): Boolean = {
|
||||
val activation = expectEndpointDeactivationCount(count)
|
||||
f; activation.await(timeout, timeUnit)
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for an expected number (<code>count</code>) of endpoints to be activated
|
||||
* during execution of <code>p</code>. The wait timeout is 10 seconds.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def awaitEndpointActivation(count: Int, p: SideEffect): Boolean = {
|
||||
awaitEndpointActivation(count, 10, TimeUnit.SECONDS, p)
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for an expected number (<code>count</code>) of endpoints to be activated
|
||||
* during execution of <code>p</code>. Timeout values can be set via the
|
||||
* <code>timeout</code> and <code>timeUnit</code> parameters.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def awaitEndpointActivation(count: Int, timeout: Long, timeUnit: TimeUnit, p: SideEffect): Boolean = {
|
||||
awaitEndpointActivation(count, timeout, timeUnit) { p.apply }
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for an expected number (<code>count</code>) of endpoints to be de-activated
|
||||
* during execution of <code>p</code>. The wait timeout is 10 seconds.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def awaitEndpointDeactivation(count: Int, p: SideEffect): Boolean = {
|
||||
awaitEndpointDeactivation(count, 10, TimeUnit.SECONDS, p)
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for an expected number (<code>count</code>) of endpoints to be de-activated
|
||||
* during execution of <code>p</code>. Timeout values can be set via the
|
||||
* <code>timeout</code> and <code>timeUnit</code> parameters.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def awaitEndpointDeactivation(count: Int, timeout: Long, timeUnit: TimeUnit, p: SideEffect): Boolean = {
|
||||
awaitEndpointDeactivation(count, timeout, timeUnit) { p.apply }
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an expectation on the number of upcoming endpoint activations and returns
|
||||
* a CountDownLatch that can be used to wait for the activations to occur. Endpoint
|
||||
* activations that occurred in the past are not considered.
|
||||
*/
|
||||
private def expectEndpointActivationCount(count: Int): CountDownLatch =
|
||||
(consumerPublisher !! SetExpectedRegistrationCount(count)).as[CountDownLatch].get
|
||||
|
||||
/**
|
||||
* Sets an expectation on the number of upcoming endpoint de-activations and returns
|
||||
* a CountDownLatch that can be used to wait for the de-activations to occur. Endpoint
|
||||
* de-activations that occurred in the past are not considered.
|
||||
*/
|
||||
private def expectEndpointDeactivationCount(count: Int): CountDownLatch =
|
||||
(consumerPublisher !! SetExpectedUnregistrationCount(count)).as[CountDownLatch].get
|
||||
|
||||
private[camel] def publishRequestorRegistered: Boolean = {
|
||||
ActorRegistry.hasListener(publishRequestor) ||
|
||||
AspectInitRegistry.hasListener(publishRequestor)
|
||||
}
|
||||
|
||||
private[camel] def registerPublishRequestor: Unit = {
|
||||
ActorRegistry.addListener(publishRequestor)
|
||||
AspectInitRegistry.addListener(publishRequestor)
|
||||
}
|
||||
|
||||
private[camel] def unregisterPublishRequestor: Unit = {
|
||||
ActorRegistry.removeListener(publishRequestor)
|
||||
AspectInitRegistry.removeListener(publishRequestor)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages a global CamelService (the 'current' CamelService).
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
object CamelServiceManager {
|
||||
|
||||
/**
|
||||
* The current (optional) CamelService. Is defined when a CamelService has been started.
|
||||
*/
|
||||
private var _current: Option[CamelService] = None
|
||||
|
||||
/**
|
||||
* Starts a new CamelService and makes it the current CamelService.
|
||||
*
|
||||
* @see CamelService#start
|
||||
* @see CamelService#onLoad
|
||||
*/
|
||||
def startCamelService = CamelServiceFactory.createCamelService.start
|
||||
|
||||
/**
|
||||
* Stops the current CamelService.
|
||||
*
|
||||
* @see CamelService#stop
|
||||
* @see CamelService#onUnload
|
||||
*/
|
||||
def stopCamelService = for (s <- service) s.stop
|
||||
|
||||
/**
|
||||
* Returns <code>Some(CamelService)</code> if this <code>CamelService</code>
|
||||
* has been started, <code>None</code> otherwise.
|
||||
*/
|
||||
def service = _current
|
||||
|
||||
/**
|
||||
* Returns the current <code>CamelService</code> if <code>CamelService</code>
|
||||
* has been started, otherwise throws an <code>IllegalStateException</code>.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def getService: JOption[CamelService] = CamelServiceManager.service
|
||||
|
||||
/**
|
||||
* Returns <code>Some(CamelService)</code> (containing the current CamelService)
|
||||
* if this <code>CamelService</code>has been started, <code>None</code> otherwise.
|
||||
*/
|
||||
def mandatoryService =
|
||||
if (_current.isDefined) _current.get
|
||||
else throw new IllegalStateException("co current CamelService")
|
||||
|
||||
/**
|
||||
* Returns <code>Some(CamelService)</code> (containing the current CamelService)
|
||||
* if this <code>CamelService</code>has been started, <code>None</code> otherwise.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def getMandatoryService = mandatoryService
|
||||
|
||||
private[camel] def register(service: CamelService) =
|
||||
if (_current.isDefined) throw new IllegalStateException("current CamelService already registered")
|
||||
else _current = Some(service)
|
||||
|
||||
private[camel] def unregister(service: CamelService) =
|
||||
if (_current == Some(service)) _current = None
|
||||
else throw new IllegalStateException("only current CamelService can be unregistered")
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
object CamelServiceFactory {
|
||||
/**
|
||||
* Creates a new CamelService instance.
|
||||
*/
|
||||
def createCamelService: CamelService = new CamelService { }
|
||||
|
||||
/**
|
||||
* Creates a new CamelService instance and initializes it with the given CamelContext.
|
||||
*/
|
||||
def createCamelService(camelContext: CamelContext): CamelService = {
|
||||
CamelContextManager.init(camelContext)
|
||||
createCamelService
|
||||
}
|
||||
}
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package akka.camel
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
import org.apache.camel.{Exchange, Processor}
|
||||
import org.apache.camel.model.{RouteDefinition, ProcessorDefinition}
|
||||
|
||||
import akka.actor._
|
||||
import akka.japi.{Function => JFunction}
|
||||
|
||||
/**
|
||||
* Mixed in by Actor implementations that consume message from Camel endpoints.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
trait Consumer { self: Actor =>
|
||||
import RouteDefinitionHandler._
|
||||
|
||||
/**
|
||||
* The default route definition handler is the identity function
|
||||
*/
|
||||
private[camel] var routeDefinitionHandler: RouteDefinitionHandler = identity
|
||||
|
||||
/**
|
||||
* Returns the Camel endpoint URI to consume messages from.
|
||||
*/
|
||||
def endpointUri: String
|
||||
|
||||
/**
|
||||
* Determines whether two-way communications between an endpoint and this consumer actor
|
||||
* should be done in blocking or non-blocking mode (default is non-blocking). This method
|
||||
* doesn't have any effect on one-way communications (they'll never block).
|
||||
*/
|
||||
def blocking = false
|
||||
|
||||
/**
|
||||
* Sets the route definition handler for creating a custom route to this consumer instance.
|
||||
*/
|
||||
def onRouteDefinition(h: RouteDefinition => ProcessorDefinition[_]): Unit = onRouteDefinition(from(h))
|
||||
|
||||
/**
|
||||
* Sets the route definition handler for creating a custom route to this consumer instance.
|
||||
* <p>
|
||||
* Java API.
|
||||
*/
|
||||
def onRouteDefinition(h: RouteDefinitionHandler): Unit = routeDefinitionHandler = h
|
||||
}
|
||||
|
||||
/**
|
||||
* Java-friendly Consumer.
|
||||
*
|
||||
* @see UntypedConsumerActor
|
||||
* @see RemoteUntypedConsumerActor
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
trait UntypedConsumer extends Consumer { self: UntypedActor =>
|
||||
final override def endpointUri = getEndpointUri
|
||||
final override def blocking = isBlocking
|
||||
|
||||
/**
|
||||
* Returns the Camel endpoint URI to consume messages from.
|
||||
*/
|
||||
def getEndpointUri(): String
|
||||
|
||||
/**
|
||||
* Determines whether two-way communications between an endpoint and this consumer actor
|
||||
* should be done in blocking or non-blocking mode (default is non-blocking). This method
|
||||
* doesn't have any effect on one-way communications (they'll never block).
|
||||
*/
|
||||
def isBlocking() = super.blocking
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass this abstract class to create an MDB-style untyped consumer actor. This
|
||||
* class is meant to be used from Java.
|
||||
*/
|
||||
abstract class UntypedConsumerActor extends UntypedActor with UntypedConsumer
|
||||
|
||||
/**
|
||||
* Subclass this abstract class to create an MDB-style remote untyped consumer
|
||||
* actor. This class is meant to be used from Java.
|
||||
*/
|
||||
abstract class RemoteUntypedConsumerActor(address: InetSocketAddress) extends RemoteUntypedActor(address) with UntypedConsumer {
|
||||
def this(host: String, port: Int) = this(new InetSocketAddress(host, port))
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback handler for route definitions to consumer actors.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
trait RouteDefinitionHandler {
|
||||
def onRouteDefinition(rd: RouteDefinition): ProcessorDefinition[_]
|
||||
}
|
||||
|
||||
/**
|
||||
* The identity route definition handler.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*
|
||||
*/
|
||||
class RouteDefinitionIdentity extends RouteDefinitionHandler {
|
||||
def onRouteDefinition(rd: RouteDefinition) = rd
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
object RouteDefinitionHandler {
|
||||
/**
|
||||
* Returns the identity route definition handler
|
||||
*/
|
||||
val identity = new RouteDefinitionIdentity
|
||||
|
||||
/**
|
||||
* Created a route definition handler from the given function.
|
||||
*/
|
||||
def from(f: RouteDefinition => ProcessorDefinition[_]) = new RouteDefinitionHandler {
|
||||
def onRouteDefinition(rd: RouteDefinition) = f(rd)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] object Consumer {
|
||||
/**
|
||||
* Applies a function <code>f</code> to <code>actorRef</code> if <code>actorRef</code>
|
||||
* references a consumer actor. A valid reference to a consumer actor is a local actor
|
||||
* reference with a target actor that implements the <code>Consumer</code> trait. The
|
||||
* target <code>Consumer</code> object is passed as argument to <code>f</code>. This
|
||||
* method returns <code>None</code> if <code>actorRef</code> is not a valid reference
|
||||
* to a consumer actor, <code>Some</code> consumer actor otherwise.
|
||||
*/
|
||||
def forConsumer[T](actorRef: ActorRef)(f: Consumer => T): Option[T] = {
|
||||
if (!actorRef.actor.isInstanceOf[Consumer]) None
|
||||
else if (actorRef.remoteAddress.isDefined) None
|
||||
else Some(f(actorRef.actor.asInstanceOf[Consumer]))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,351 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
package akka.camel
|
||||
|
||||
import collection.mutable.ListBuffer
|
||||
|
||||
import java.io.InputStream
|
||||
import java.lang.reflect.Method
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
import org.apache.camel.builder.RouteBuilder
|
||||
import org.apache.camel.model.{ProcessorDefinition, RouteDefinition}
|
||||
|
||||
import akka.actor._
|
||||
import akka.camel.component.TypedActorComponent
|
||||
import akka.util.Logging
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] object ConsumerPublisher extends Logging {
|
||||
/**
|
||||
* Creates a route to the registered consumer actor.
|
||||
*/
|
||||
def handleConsumerActorRegistered(event: ConsumerActorRegistered) {
|
||||
CamelContextManager.mandatoryContext.addRoutes(new ConsumerActorRouteBuilder(event))
|
||||
log.info("published actor %s at endpoint %s" format (event.actorRef, event.endpointUri))
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the route to the already un-registered consumer actor.
|
||||
*/
|
||||
def handleConsumerActorUnregistered(event: ConsumerActorUnregistered) {
|
||||
CamelContextManager.mandatoryContext.stopRoute(event.uuid)
|
||||
log.info("unpublished actor %s from endpoint %s" format (event.actorRef, event.endpointUri))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a route to an typed actor method.
|
||||
*/
|
||||
def handleConsumerMethodRegistered(event: ConsumerMethodRegistered) {
|
||||
CamelContextManager.typedActorRegistry.put(event.methodUuid, event.typedActor)
|
||||
CamelContextManager.mandatoryContext.addRoutes(new ConsumerMethodRouteBuilder(event))
|
||||
log.info("published method %s of %s at endpoint %s" format (event.methodName, event.typedActor, event.endpointUri))
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the route to the already un-registered consumer actor method.
|
||||
*/
|
||||
def handleConsumerMethodUnregistered(event: ConsumerMethodUnregistered) {
|
||||
CamelContextManager.typedActorRegistry.remove(event.methodUuid)
|
||||
CamelContextManager.mandatoryContext.stopRoute(event.methodUuid)
|
||||
log.info("unpublished method %s of %s from endpoint %s" format (event.methodName, event.typedActor, event.endpointUri))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actor that publishes consumer actors and typed actor methods at Camel endpoints.
|
||||
* The Camel context used for publishing is obtained via CamelContextManager.context.
|
||||
* This actor accepts messages of type
|
||||
* akka.camel.ConsumerActorRegistered,
|
||||
* akka.camel.ConsumerActorUnregistered,
|
||||
* akka.camel.ConsumerMethodRegistered and
|
||||
* akka.camel.ConsumerMethodUnregistered.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] class ConsumerPublisher extends Actor {
|
||||
import ConsumerPublisher._
|
||||
|
||||
@volatile private var registrationLatch = new CountDownLatch(0)
|
||||
@volatile private var unregistrationLatch = new CountDownLatch(0)
|
||||
|
||||
protected def receive = {
|
||||
case r: ConsumerActorRegistered => {
|
||||
handleConsumerActorRegistered(r)
|
||||
registrationLatch.countDown
|
||||
}
|
||||
case u: ConsumerActorUnregistered => {
|
||||
handleConsumerActorUnregistered(u)
|
||||
unregistrationLatch.countDown
|
||||
}
|
||||
case mr: ConsumerMethodRegistered => {
|
||||
handleConsumerMethodRegistered(mr)
|
||||
registrationLatch.countDown
|
||||
}
|
||||
case mu: ConsumerMethodUnregistered => {
|
||||
handleConsumerMethodUnregistered(mu)
|
||||
unregistrationLatch.countDown
|
||||
}
|
||||
case SetExpectedRegistrationCount(num) => {
|
||||
registrationLatch = new CountDownLatch(num)
|
||||
self.reply(registrationLatch)
|
||||
}
|
||||
case SetExpectedUnregistrationCount(num) => {
|
||||
unregistrationLatch = new CountDownLatch(num)
|
||||
self.reply(unregistrationLatch)
|
||||
}
|
||||
case _ => { /* ignore */}
|
||||
}
|
||||
}
|
||||
|
||||
private[camel] case class SetExpectedRegistrationCount(num: Int)
|
||||
private[camel] case class SetExpectedUnregistrationCount(num: Int)
|
||||
|
||||
/**
|
||||
* Abstract route to a target which is either an actor or an typed actor method.
|
||||
*
|
||||
* @param endpointUri endpoint URI of the consumer actor or typed actor method.
|
||||
* @param id actor identifier or typed actor identifier (registry key).
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] abstract class ConsumerRouteBuilder(endpointUri: String, id: String) extends RouteBuilder {
|
||||
// TODO: make conversions configurable
|
||||
private val bodyConversions = Map(
|
||||
"file" -> classOf[InputStream]
|
||||
)
|
||||
|
||||
def configure = {
|
||||
val schema = endpointUri take endpointUri.indexOf(":") // e.g. "http" from "http://whatever/..."
|
||||
val cnvopt = bodyConversions.get(schema)
|
||||
|
||||
onRouteDefinition(startRouteDefinition(cnvopt)).to(targetUri)
|
||||
}
|
||||
|
||||
protected def routeDefinitionHandler: RouteDefinitionHandler
|
||||
protected def targetUri: String
|
||||
|
||||
private def onRouteDefinition(rd: RouteDefinition) = routeDefinitionHandler.onRouteDefinition(rd)
|
||||
private def startRouteDefinition(bodyConversion: Option[Class[_]]): RouteDefinition = bodyConversion match {
|
||||
case Some(clazz) => from(endpointUri).routeId(id).convertBodyTo(clazz)
|
||||
case None => from(endpointUri).routeId(id)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the route to a (untyped) consumer actor.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] class ConsumerActorRouteBuilder(event: ConsumerActorRegistered) extends ConsumerRouteBuilder(event.endpointUri, event.uuid) {
|
||||
protected def routeDefinitionHandler: RouteDefinitionHandler = event.routeDefinitionHandler
|
||||
protected def targetUri = "actor:uuid:%s?blocking=%s" format (event.uuid, event.blocking)
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the route to a typed actor method.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] class ConsumerMethodRouteBuilder(event: ConsumerMethodRegistered) extends ConsumerRouteBuilder(event.endpointUri, event.methodUuid) {
|
||||
protected def routeDefinitionHandler: RouteDefinitionHandler = event.routeDefinitionHandler
|
||||
protected def targetUri = "%s:%s?method=%s" format (TypedActorComponent.InternalSchema, event.methodUuid, event.methodName)
|
||||
}
|
||||
|
||||
/**
|
||||
* A registration listener that triggers publication of consumer actors and typed actor
|
||||
* methods as well as un-publication of consumer actors and typed actor methods. This actor
|
||||
* needs to be initialized with a <code>PublishRequestorInit</code> command message for
|
||||
* obtaining a reference to a <code>publisher</code> actor. Before initialization it buffers
|
||||
* all outbound messages and delivers them to the <code>publisher</code> when receiving a
|
||||
* <code>PublishRequestorInit</code> message. After initialization, outbound messages are
|
||||
* delivered directly without buffering.
|
||||
*
|
||||
* @see PublishRequestorInit
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] class PublishRequestor extends Actor {
|
||||
private val events = ListBuffer[ConsumerEvent]()
|
||||
private var publisher: Option[ActorRef] = None
|
||||
|
||||
protected def receive = {
|
||||
case ActorRegistered(actor) =>
|
||||
for (event <- ConsumerActorRegistered.forConsumer(actor)) deliverCurrentEvent(event)
|
||||
case ActorUnregistered(actor) =>
|
||||
for (event <- ConsumerActorUnregistered.forConsumer(actor)) deliverCurrentEvent(event)
|
||||
case AspectInitRegistered(proxy, init) =>
|
||||
for (event <- ConsumerMethodRegistered.forConsumer(proxy, init)) deliverCurrentEvent(event)
|
||||
case AspectInitUnregistered(proxy, init) =>
|
||||
for (event <- ConsumerMethodUnregistered.forConsumer(proxy, init)) deliverCurrentEvent(event)
|
||||
case PublishRequestorInit(pub) => {
|
||||
publisher = Some(pub)
|
||||
deliverBufferedEvents
|
||||
}
|
||||
case _ => { /* ignore */ }
|
||||
}
|
||||
|
||||
private def deliverCurrentEvent(event: ConsumerEvent) = {
|
||||
publisher match {
|
||||
case Some(pub) => pub ! event
|
||||
case None => events += event
|
||||
}
|
||||
}
|
||||
|
||||
private def deliverBufferedEvents = {
|
||||
for (event <- events) deliverCurrentEvent(event)
|
||||
events.clear
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Command message to initialize a PublishRequestor to use <code>consumerPublisher</code>
|
||||
* for publishing actors or typed actor methods.
|
||||
*/
|
||||
private[camel] case class PublishRequestorInit(consumerPublisher: ActorRef)
|
||||
|
||||
/**
|
||||
* A consumer (un)registration event.
|
||||
*/
|
||||
private[camel] sealed trait ConsumerEvent
|
||||
|
||||
/**
|
||||
* A consumer actor (un)registration event.
|
||||
*/
|
||||
private[camel] trait ConsumerActorEvent extends ConsumerEvent {
|
||||
val actorRef: ActorRef
|
||||
val actor: Consumer
|
||||
|
||||
val uuid = actorRef.uuid.toString
|
||||
val endpointUri = actor.endpointUri
|
||||
val blocking = actor.blocking
|
||||
val routeDefinitionHandler = actor.routeDefinitionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* A consumer method (un)registration event.
|
||||
*/
|
||||
private[camel] trait ConsumerMethodEvent extends ConsumerEvent {
|
||||
val typedActor: AnyRef
|
||||
val init: AspectInit
|
||||
val method: Method
|
||||
|
||||
val uuid = init.actorRef.uuid.toString
|
||||
val methodName = method.getName
|
||||
val methodUuid = "%s_%s" format (uuid, methodName)
|
||||
|
||||
lazy val routeDefinitionHandler = consumeAnnotation.routeDefinitionHandler.newInstance
|
||||
lazy val consumeAnnotation = method.getAnnotation(classOf[consume])
|
||||
lazy val endpointUri = consumeAnnotation.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Event indicating that a consumer actor has been registered at the actor registry.
|
||||
*/
|
||||
private[camel] case class ConsumerActorRegistered(actorRef: ActorRef, actor: Consumer) extends ConsumerActorEvent
|
||||
|
||||
/**
|
||||
* Event indicating that a consumer actor has been unregistered from the actor registry.
|
||||
*/
|
||||
private[camel] case class ConsumerActorUnregistered(actorRef: ActorRef, actor: Consumer) extends ConsumerActorEvent
|
||||
|
||||
/**
|
||||
* Event indicating that an typed actor proxy has been created for a typed actor. For each <code>@consume</code>
|
||||
* annotated typed actor method a separate instance of this class is created.
|
||||
*/
|
||||
private[camel] case class ConsumerMethodRegistered(typedActor: AnyRef, init: AspectInit, method: Method) extends ConsumerMethodEvent
|
||||
|
||||
/**
|
||||
* Event indicating that an typed actor has been stopped. For each <code>@consume</code>
|
||||
* annotated typed object method a separate instance of this class is created.
|
||||
*/
|
||||
private[camel] case class ConsumerMethodUnregistered(typedActor: AnyRef, init: AspectInit, method: Method) extends ConsumerMethodEvent
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] object ConsumerActorRegistered {
|
||||
/**
|
||||
* Creates an ConsumerActorRegistered event message for a consumer actor or None if
|
||||
* <code>actorRef</code> is not a consumer actor.
|
||||
*/
|
||||
def forConsumer(actorRef: ActorRef): Option[ConsumerActorRegistered] = {
|
||||
Consumer.forConsumer[ConsumerActorRegistered](actorRef) {
|
||||
actor => ConsumerActorRegistered(actorRef, actor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] object ConsumerActorUnregistered {
|
||||
/**
|
||||
* Creates an ConsumerActorUnregistered event message for a consumer actor or None if
|
||||
* <code>actorRef</code> is not a consumer actor.
|
||||
*/
|
||||
def forConsumer(actorRef: ActorRef): Option[ConsumerActorUnregistered] = {
|
||||
Consumer.forConsumer[ConsumerActorUnregistered](actorRef) {
|
||||
actor => ConsumerActorUnregistered(actorRef, actor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] object ConsumerMethod {
|
||||
/**
|
||||
* Applies a function <code>f</code> to each consumer method of <code>TypedActor</code> and
|
||||
* returns the function results as a list. A consumer method is one that is annotated with
|
||||
* <code>@consume</code>. If <code>typedActor</code> is a proxy for a remote typed actor
|
||||
* <code>f</code> is never called and <code>Nil</code> is returned.
|
||||
*/
|
||||
def forConsumer[T](typedActor: AnyRef, init: AspectInit)(f: Method => T): List[T] = {
|
||||
if (init.remoteAddress.isDefined) Nil // let remote node publish typed actor methods on endpoints
|
||||
else {
|
||||
// TODO: support consumer annotation inheritance
|
||||
// - visit overridden methods in superclasses
|
||||
// - visit implemented method declarations in interfaces
|
||||
val intfClass = typedActor.getClass
|
||||
val implClass = init.targetInstance.getClass
|
||||
(for (m <- intfClass.getMethods.toList; if (m.isAnnotationPresent(classOf[consume]))) yield f(m)) ++
|
||||
(for (m <- implClass.getMethods.toList; if (m.isAnnotationPresent(classOf[consume]))) yield f(m))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] object ConsumerMethodRegistered {
|
||||
/**
|
||||
* Creates a list of ConsumerMethodRegistered event messages for a typed actor or an empty
|
||||
* list if the typed actor is a proxy for a remote typed actor or the typed actor doesn't
|
||||
* have any <code>@consume</code> annotated methods.
|
||||
*/
|
||||
def forConsumer(typedActor: AnyRef, init: AspectInit): List[ConsumerMethodRegistered] = {
|
||||
ConsumerMethod.forConsumer(typedActor, init) {
|
||||
m => ConsumerMethodRegistered(typedActor, init, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] object ConsumerMethodUnregistered {
|
||||
/**
|
||||
* Creates a list of ConsumerMethodUnregistered event messages for a typed actor or an empty
|
||||
* list if the typed actor is a proxy for a remote typed actor or the typed actor doesn't
|
||||
* have any <code>@consume</code> annotated methods.
|
||||
*/
|
||||
def forConsumer(typedActor: AnyRef, init: AspectInit): List[ConsumerMethodUnregistered] = {
|
||||
ConsumerMethod.forConsumer(typedActor, init) {
|
||||
m => ConsumerMethodUnregistered(typedActor, init, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,380 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
package akka.camel
|
||||
|
||||
import java.util.{Map => JMap, Set => JSet}
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
import org.apache.camel.{Exchange, Message => CamelMessage}
|
||||
import org.apache.camel.util.ExchangeHelper
|
||||
|
||||
import akka.japi.{Function => JFunction}
|
||||
|
||||
/**
|
||||
* An immutable representation of a Camel message.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
case class Message(val body: Any, val headers: Map[String, Any] = Map.empty) {
|
||||
|
||||
/**
|
||||
* Creates a Message with given body and empty headers map.
|
||||
*/
|
||||
def this(body: Any) = this(body, Map.empty[String, Any])
|
||||
|
||||
/**
|
||||
* Creates a Message with given body and headers map. A copy of the headers map is made.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def this(body: Any, headers: JMap[String, Any]) = this(body, headers.toMap)
|
||||
|
||||
/**
|
||||
* 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 managed
|
||||
* by CamelContextManager. Applications have to ensure proper initialization of
|
||||
* CamelContextManager.
|
||||
*
|
||||
* @see CamelContextManager.
|
||||
*/
|
||||
def bodyAs[T](implicit m: Manifest[T]): T = getBodyAs(m.erasure.asInstanceOf[Class[T]])
|
||||
|
||||
/**
|
||||
* 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 managed by CamelContextManager. Applications have to ensure proper
|
||||
* initialization of CamelContextManager.
|
||||
* <p>
|
||||
* Java API
|
||||
*
|
||||
* @see CamelContextManager.
|
||||
*/
|
||||
def getBodyAs[T](clazz: Class[T]): T =
|
||||
CamelContextManager.mandatoryContext.getTypeConverter.mandatoryConvertTo[T](clazz, body)
|
||||
|
||||
/**
|
||||
* Returns those headers from this message whose name is contained in <code>names</code>.
|
||||
*/
|
||||
def headers(names: Set[String]): Map[String, Any] = headers.filter(names contains _._1)
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def getHeaders(names: JSet[String]): JMap[String, Any] = headers.filter(names contains _._1)
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def getHeaders: JMap[String, Any] = headers
|
||||
|
||||
/**
|
||||
* Returns the header with given <code>name</code>. Throws <code>NoSuchElementException</code>
|
||||
* if the header doesn't exist.
|
||||
*/
|
||||
def header(name: String): Any = headers(name)
|
||||
|
||||
/**
|
||||
* Returns the header with given <code>name</code>. Throws <code>NoSuchElementException</code>
|
||||
* if the header doesn't exist.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def getHeader(name: String): Any = header(name)
|
||||
|
||||
/**
|
||||
* Returns the header with given <code>name</code> converted to type <code>T</code>. Throws
|
||||
* <code>NoSuchElementException</code> if the header doesn't exist.
|
||||
*/
|
||||
def headerAs[T](name: String)(implicit m: Manifest[T]): T =
|
||||
getHeaderAs(name, m.erasure.asInstanceOf[Class[T]])
|
||||
|
||||
/**
|
||||
* Returns the header with given <code>name</code> converted to type as given by the <code>clazz</code>
|
||||
* parameter. Throws <code>NoSuchElementException</code> if the header doesn't exist.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def getHeaderAs[T](name: String, clazz: Class[T]): T =
|
||||
CamelContextManager.mandatoryContext.getTypeConverter.mandatoryConvertTo[T](clazz, header(name))
|
||||
|
||||
/**
|
||||
* Creates a Message with a transformed body using a <code>transformer</code> function.
|
||||
*/
|
||||
def transformBody[A](transformer: A => Any): Message = setBody(transformer(body.asInstanceOf[A]))
|
||||
|
||||
/**
|
||||
* Creates a Message with a transformed body using a <code>transformer</code> function.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def transformBody[A](transformer: JFunction[A, Any]): Message = setBody(transformer(body.asInstanceOf[A]))
|
||||
|
||||
/**
|
||||
* Creates a Message with current <code>body</code> converted to type <code>T</code>.
|
||||
*/
|
||||
def setBodyAs[T](implicit m: Manifest[T]): Message = setBodyAs(m.erasure.asInstanceOf[Class[T]])
|
||||
|
||||
/**
|
||||
* Creates a Message with current <code>body</code> converted to type <code>clazz</code>.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def setBodyAs[T](clazz: Class[T]): Message = setBody(getBodyAs(clazz))
|
||||
|
||||
/**
|
||||
* Creates a Message with a given <code>body</code>.
|
||||
*/
|
||||
def setBody(body: Any) = new Message(body, this.headers)
|
||||
|
||||
/**
|
||||
* Creates a new Message with given <code>headers</code>.
|
||||
*/
|
||||
def setHeaders(headers: Map[String, Any]): Message = copy(this.body, headers)
|
||||
|
||||
/**
|
||||
* Creates a new Message with given <code>headers</code>. A copy of the headers map is made.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def setHeaders(headers: JMap[String, Any]): Message = setHeaders(headers.toMap)
|
||||
|
||||
/**
|
||||
* Creates a new Message with given <code>headers</code> added to the current headers.
|
||||
*/
|
||||
def addHeaders(headers: Map[String, Any]): Message = copy(this.body, this.headers ++ headers)
|
||||
|
||||
/**
|
||||
* Creates a new Message with given <code>headers</code> added to the current headers.
|
||||
* A copy of the headers map is made.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def addHeaders(headers: JMap[String, Any]): Message = addHeaders(headers.toMap)
|
||||
|
||||
/**
|
||||
* Creates a new Message with the given <code>header</code> added to the current headers.
|
||||
*/
|
||||
def addHeader(header: (String, Any)): Message = copy(this.body, this.headers + header)
|
||||
|
||||
/**
|
||||
* Creates a new Message with the given header, represented by <code>name</code> and
|
||||
* <code>value</code> added to the existing headers.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def addHeader(name: String, value: Any): Message = addHeader((name, value))
|
||||
|
||||
/**
|
||||
* Creates a new Message where the header with given <code>headerName</code> is removed from
|
||||
* the existing headers.
|
||||
*/
|
||||
def removeHeader(headerName: String) = copy(this.body, this.headers - headerName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Companion object of Message class.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
object Message {
|
||||
|
||||
/**
|
||||
* Message 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".intern
|
||||
|
||||
/**
|
||||
* Creates a new Message with <code>body</code> as message body and an empty header map.
|
||||
*/
|
||||
//def apply(body: Any) = new Message(body)
|
||||
|
||||
/**
|
||||
* Creates a canonical form of the given message <code>msg</code>. If <code>msg</code> of type
|
||||
* Message then <code>msg</code> is returned, otherwise <code>msg</code> is set as body of a
|
||||
* newly created Message object.
|
||||
*/
|
||||
def canonicalize(msg: Any) = msg match {
|
||||
case mobj: Message => mobj
|
||||
case body => new Message(body)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An immutable representation of a failed Camel exchange. 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.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
case class Failure(val cause: Exception, val headers: Map[String, Any] = Map.empty) {
|
||||
|
||||
/**
|
||||
* Creates a Failure with cause body and empty headers map.
|
||||
*/
|
||||
def this(cause: Exception) = this(cause, Map.empty[String, Any])
|
||||
|
||||
/**
|
||||
* Creates a Failure with given cause and headers map. A copy of the headers map is made.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def this(cause: Exception, headers: JMap[String, Any]) = this(cause, headers.toMap)
|
||||
|
||||
/**
|
||||
* Returns the cause of this Failure.
|
||||
* <p>
|
||||
* Java API.
|
||||
*/
|
||||
def getCause = cause
|
||||
|
||||
/**
|
||||
* Returns all headers from this failure 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.
|
||||
* <p>
|
||||
* Java API
|
||||
*/
|
||||
def getHeaders: JMap[String, Any] = headers
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter for converting an org.apache.camel.Exchange to and from Message and Failure objects.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class CamelExchangeAdapter(exchange: Exchange) {
|
||||
import CamelMessageConversion.toMessageAdapter
|
||||
|
||||
/**
|
||||
* Sets Exchange.getIn from the given Message object.
|
||||
*/
|
||||
def fromRequestMessage(msg: Message): Exchange = { requestMessage.fromMessage(msg); exchange }
|
||||
|
||||
/**
|
||||
* Depending on the exchange pattern, sets Exchange.getIn or Exchange.getOut from the given
|
||||
* Message object. If the exchange is out-capable then the Exchange.getOut is set, otherwise
|
||||
* Exchange.getIn.
|
||||
*/
|
||||
def fromResponseMessage(msg: Message): Exchange = { responseMessage.fromMessage(msg); exchange }
|
||||
|
||||
/**
|
||||
* Sets Exchange.getException from the given Failure message. Headers of the Failure message
|
||||
* are ignored.
|
||||
*/
|
||||
def fromFailureMessage(msg: Failure): Exchange = { exchange.setException(msg.cause); exchange }
|
||||
|
||||
/**
|
||||
* Creates a Message object from Exchange.getIn.
|
||||
*/
|
||||
def toRequestMessage: Message = toRequestMessage(Map.empty)
|
||||
|
||||
/**
|
||||
* Depending on the exchange pattern, creates a Message object from Exchange.getIn or Exchange.getOut.
|
||||
* If the exchange is out-capable then the Exchange.getOut is set, otherwise Exchange.getIn.
|
||||
*/
|
||||
def toResponseMessage: Message = toResponseMessage(Map.empty)
|
||||
|
||||
/**
|
||||
* Creates a Failure object from the adapted Exchange.
|
||||
*
|
||||
* @see Failure
|
||||
*/
|
||||
def toFailureMessage: Failure = toFailureMessage(Map.empty)
|
||||
|
||||
/**
|
||||
* Creates a Message object from Exchange.getIn.
|
||||
*
|
||||
* @param headers additional headers to set on the created Message in addition to those
|
||||
* in the Camel message.
|
||||
*/
|
||||
def toRequestMessage(headers: Map[String, Any]): Message = requestMessage.toMessage(headers)
|
||||
|
||||
/**
|
||||
* Depending on the exchange pattern, creates a Message object from Exchange.getIn or Exchange.getOut.
|
||||
* If the exchange is out-capable then the Exchange.getOut is set, otherwise Exchange.getIn.
|
||||
*
|
||||
* @param headers additional headers to set on the created Message in addition to those
|
||||
* in the Camel message.
|
||||
*/
|
||||
def toResponseMessage(headers: Map[String, Any]): Message = responseMessage.toMessage(headers)
|
||||
|
||||
/**
|
||||
* Creates a Failure object from the adapted Exchange.
|
||||
*
|
||||
* @param headers additional headers to set on the created Message in addition to those
|
||||
* in the Camel message.
|
||||
*
|
||||
* @see Failure
|
||||
*/
|
||||
def toFailureMessage(headers: Map[String, Any]): Failure =
|
||||
Failure(exchange.getException, headers ++ responseMessage.toMessage.headers)
|
||||
|
||||
private def requestMessage = exchange.getIn
|
||||
|
||||
private def responseMessage = ExchangeHelper.getResultMessage(exchange)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter for converting an org.apache.camel.Message to and from Message objects.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class CamelMessageAdapter(val cm: CamelMessage) {
|
||||
/**
|
||||
* Set the adapted Camel message from the given Message object.
|
||||
*/
|
||||
def fromMessage(m: Message): CamelMessage = {
|
||||
cm.setBody(m.body)
|
||||
for (h <- m.headers) cm.getHeaders.put(h._1, h._2.asInstanceOf[AnyRef])
|
||||
cm
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Message object from the adapted Camel message.
|
||||
*/
|
||||
def toMessage: Message = toMessage(Map.empty)
|
||||
|
||||
/**
|
||||
* Creates a new Message object from the adapted Camel message.
|
||||
*
|
||||
* @param headers additional headers to set on the created Message in addition to those
|
||||
* in the Camel message.
|
||||
*/
|
||||
def toMessage(headers: Map[String, Any]): Message = Message(cm.getBody, cmHeaders(headers, cm))
|
||||
|
||||
private def cmHeaders(headers: Map[String, Any], cm: CamelMessage) = headers ++ cm.getHeaders
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines conversion methods to CamelExchangeAdapter and CamelMessageAdapter.
|
||||
* Imported by applications that implicitly want to use conversion methods of
|
||||
* CamelExchangeAdapter and CamelMessageAdapter.
|
||||
*/
|
||||
object CamelMessageConversion {
|
||||
|
||||
/**
|
||||
* Creates an CamelExchangeAdapter for the given Camel exchange.
|
||||
*/
|
||||
implicit def toExchangeAdapter(ce: Exchange): CamelExchangeAdapter =
|
||||
new CamelExchangeAdapter(ce)
|
||||
|
||||
/**
|
||||
* Creates an CamelMessageAdapter for the given Camel message.
|
||||
*/
|
||||
implicit def toMessageAdapter(cm: CamelMessage): CamelMessageAdapter =
|
||||
new CamelMessageAdapter(cm)
|
||||
}
|
||||
|
|
@ -1,256 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package akka.camel
|
||||
|
||||
import CamelMessageConversion.toExchangeAdapter
|
||||
|
||||
import org.apache.camel._
|
||||
import org.apache.camel.processor.SendProcessor
|
||||
|
||||
import akka.actor.{Actor, ActorRef, UntypedActor}
|
||||
|
||||
/**
|
||||
* Support trait for producing messages to Camel endpoints.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
trait ProducerSupport { this: Actor =>
|
||||
|
||||
/**
|
||||
* Message headers to copy by default from request message to response-message.
|
||||
*/
|
||||
private val headersToCopyDefault = Set(Message.MessageExchangeId)
|
||||
|
||||
/**
|
||||
* <code>Endpoint</code> object resolved from the current CamelContext with
|
||||
* <code>endpointUri</code>.
|
||||
*/
|
||||
private lazy val endpoint = CamelContextManager.mandatoryContext.getEndpoint(endpointUri)
|
||||
|
||||
/**
|
||||
* <code>SendProcessor</code> for producing messages to <code>endpoint</code>.
|
||||
*/
|
||||
private lazy val processor = createSendProcessor
|
||||
|
||||
/**
|
||||
* 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 Message.MessageExchangeId is copied. Applications may override this to
|
||||
* define an application-specific set of message headers to copy.
|
||||
*/
|
||||
def headersToCopy: Set[String] = headersToCopyDefault
|
||||
|
||||
/**
|
||||
* Default implementation of <code>Actor.postStop</code> for freeing resources needed
|
||||
* to actually send messages to <code>endpointUri</code>.
|
||||
*/
|
||||
override def postStop {
|
||||
processor.stop
|
||||
}
|
||||
|
||||
/**
|
||||
* 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. This is done by wrapping the response, adding it to this producers
|
||||
* mailbox, unwrapping it and calling <code>receiveAfterProduce</code>. The original
|
||||
* sender and senderFuture are thereby preserved.
|
||||
*
|
||||
* @see Message#canonicalize(Any)
|
||||
*
|
||||
* @param msg message to produce
|
||||
* @param pattern exchange pattern
|
||||
*/
|
||||
protected def produce(msg: Any, pattern: ExchangePattern): Unit = {
|
||||
val cmsg = Message.canonicalize(msg)
|
||||
val exchange = createExchange(pattern).fromRequestMessage(cmsg)
|
||||
processor.process(exchange, new AsyncCallback {
|
||||
val producer = self
|
||||
// Need copies of sender and senderFuture references here
|
||||
// since the callback could be done later by another thread.
|
||||
val sender = self.sender
|
||||
val senderFuture = self.senderFuture
|
||||
|
||||
def done(doneSync: Boolean): Unit = {
|
||||
(doneSync, exchange.isFailed) match {
|
||||
case (true, true) => dispatchSync(exchange.toFailureMessage(cmsg.headers(headersToCopy)))
|
||||
case (true, false) => dispatchSync(exchange.toResponseMessage(cmsg.headers(headersToCopy)))
|
||||
case (false, true) => dispatchAsync(FailureResult(exchange.toFailureMessage(cmsg.headers(headersToCopy))))
|
||||
case (false, false) => dispatchAsync(MessageResult(exchange.toResponseMessage(cmsg.headers(headersToCopy))))
|
||||
}
|
||||
}
|
||||
|
||||
private def dispatchSync(result: Any) =
|
||||
receiveAfterProduce(result)
|
||||
|
||||
private def dispatchAsync(result: Any) = {
|
||||
if (senderFuture.isDefined)
|
||||
producer.postMessageToMailboxAndCreateFutureResultWithTimeout(result, producer.timeout, sender, senderFuture)
|
||||
else
|
||||
producer.postMessageToMailbox(result, sender)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>receiveBeforeProduce</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(Any, ExchangePattern)
|
||||
*/
|
||||
protected def produce: Receive = {
|
||||
case res: MessageResult => receiveAfterProduce(res.message)
|
||||
case res: FailureResult => receiveAfterProduce(res.failure)
|
||||
case msg => {
|
||||
if (oneway)
|
||||
produce(receiveBeforeProduce(msg), ExchangePattern.InOnly)
|
||||
else
|
||||
produce(receiveBeforeProduce(msg), ExchangePattern.InOut)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 receiveBeforeProduce: PartialFunction[Any, Any] = {
|
||||
case msg => 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 receiveAfterProduce: Receive = {
|
||||
case msg => if (!oneway) self.reply(msg)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Exchange of given <code>pattern</code> from the endpoint specified by
|
||||
* <code>endpointUri</code>.
|
||||
*/
|
||||
private def createExchange(pattern: ExchangePattern): Exchange = endpoint.createExchange(pattern)
|
||||
|
||||
/**
|
||||
* Creates a new <code>SendProcessor</code> for <code>endpoint</code>.
|
||||
*/
|
||||
private def createSendProcessor = {
|
||||
val sendProcessor = new SendProcessor(endpoint)
|
||||
sendProcessor.start
|
||||
sendProcessor
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixed in by Actor implementations to produce messages to Camel endpoints.
|
||||
*/
|
||||
trait Producer extends ProducerSupport { this: Actor =>
|
||||
|
||||
/**
|
||||
* Default implementation of Actor.receive. Any messages received by this actors
|
||||
* will be produced to the endpoint specified by <code>endpointUri</code>.
|
||||
*/
|
||||
protected def receive = produce
|
||||
}
|
||||
|
||||
/**
|
||||
* Java-friendly ProducerSupport.
|
||||
*
|
||||
* @see UntypedProducerActor
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
trait UntypedProducer extends ProducerSupport { this: UntypedActor =>
|
||||
final override def endpointUri = getEndpointUri
|
||||
final override def oneway = isOneway
|
||||
|
||||
final override def receiveBeforeProduce = {
|
||||
case msg => onReceiveBeforeProduce(msg)
|
||||
}
|
||||
|
||||
final override def receiveAfterProduce = {
|
||||
case msg => onReceiveAfterProduce(msg)
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of UntypedActor.onReceive
|
||||
*/
|
||||
def onReceive(message: Any) = 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() = super.oneway
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@throws(classOf[Exception])
|
||||
def onReceiveBeforeProduce(message: Any): Any = super.receiveBeforeProduce(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).
|
||||
*/
|
||||
@throws(classOf[Exception])
|
||||
def onReceiveAfterProduce(message: Any): Unit = super.receiveAfterProduce(message)
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass this abstract class to create an untyped producer actor. This class is meant to be used from Java.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
abstract class UntypedProducerActor extends UntypedActor with UntypedProducer
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] case class MessageResult(message: Message)
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] case class FailureResult(failure: Failure)
|
||||
|
||||
/**
|
||||
* A one-way producer.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
trait Oneway extends Producer { this: Actor =>
|
||||
override def oneway = true
|
||||
}
|
||||
|
||||
|
|
@ -1,305 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package akka.camel.component
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import java.util.{Map => JMap}
|
||||
import java.util.concurrent.TimeoutException
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
import org.apache.camel._
|
||||
import org.apache.camel.impl.{DefaultProducer, DefaultEndpoint, DefaultComponent}
|
||||
|
||||
import akka.actor._
|
||||
import akka.camel.{Failure, Message}
|
||||
import akka.camel.CamelMessageConversion.toExchangeAdapter
|
||||
import akka.dispatch.{CompletableFuture, MessageInvocation, MessageDispatcher}
|
||||
|
||||
import scala.reflect.BeanProperty
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
object ActorComponent {
|
||||
/**
|
||||
* Name of the message header containing the actor id or uuid.
|
||||
*/
|
||||
val ActorIdentifier = "CamelActorIdentifier"
|
||||
}
|
||||
|
||||
/**
|
||||
* Camel component for sending messages to and receiving replies from (untyped) actors.
|
||||
*
|
||||
* @see akka.camel.component.ActorEndpoint
|
||||
* @see akka.camel.component.ActorProducer
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class ActorComponent extends DefaultComponent {
|
||||
def createEndpoint(uri: String, remaining: String, parameters: JMap[String, Object]): ActorEndpoint = {
|
||||
val (idType, idValue) = parsePath(remaining)
|
||||
new ActorEndpoint(uri, this, idType, idValue)
|
||||
}
|
||||
|
||||
private def parsePath(remaining: String): Tuple2[String, Option[String]] = remaining match {
|
||||
case null | "" => throw new IllegalArgumentException("invalid path: [%s] - should be <actorid> or id:<actorid> or uuid:<actoruuid>" format remaining)
|
||||
case id if id startsWith "id:" => ("id", parseIdentifier(id substring 3))
|
||||
case uuid if uuid startsWith "uuid:" => ("uuid", parseIdentifier(uuid substring 5))
|
||||
case id => ("id", parseIdentifier(id))
|
||||
}
|
||||
|
||||
private def parseIdentifier(identifier: String): Option[String] =
|
||||
if (identifier.length > 0) Some(identifier) else None
|
||||
}
|
||||
|
||||
/**
|
||||
* Camel endpoint for sending messages to and receiving replies from (untyped) actors. Actors
|
||||
* are referenced using <code>actor</code> endpoint URIs of the following format:
|
||||
* <code>actor:<actor-id></code>,
|
||||
* <code>actor:id:[<actor-id>]</code> and
|
||||
* <code>actor:uuid:[<actor-uuid>]</code>,
|
||||
* where <code><actor-id></code> refers to <code>ActorRef.id</code> and <code><actor-uuid></code>
|
||||
* refers to the String-representation od <code>ActorRef.uuid</code>. In URIs that contain
|
||||
* <code>id:</code> or <code>uuid:</code>, an actor identifier (id or uuid) is optional. In this
|
||||
* case, the in-message of an exchange produced to this endpoint must contain a message header
|
||||
* with name <code>CamelActorIdentifier</code> and a value that is the target actor's identifier.
|
||||
* If the URI contains an actor identifier, a message with a <code>CamelActorIdentifier</code>
|
||||
* header overrides the identifier in the endpoint URI.
|
||||
*
|
||||
* @see akka.camel.component.ActorComponent
|
||||
* @see akka.camel.component.ActorProducer
|
||||
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class ActorEndpoint(uri: String,
|
||||
comp: ActorComponent,
|
||||
val idType: String,
|
||||
val idValue: Option[String]) extends DefaultEndpoint(uri, comp) {
|
||||
|
||||
/**
|
||||
* Whether to block caller thread during two-way message exchanges with (untyped) actors. This is
|
||||
* set via the <code>blocking=true|false</code> endpoint URI parameter. Default value is
|
||||
* <code>false</code>.
|
||||
*/
|
||||
@BeanProperty var blocking: Boolean = false
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
def createConsumer(processor: Processor): Consumer =
|
||||
throw new UnsupportedOperationException("actor consumer not supported yet")
|
||||
|
||||
/**
|
||||
* Creates a new ActorProducer instance initialized with this endpoint.
|
||||
*/
|
||||
def createProducer: ActorProducer = new ActorProducer(this)
|
||||
|
||||
/**
|
||||
* Returns true.
|
||||
*/
|
||||
def isSingleton: Boolean = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the in-message of an exchange to an (untyped) actor, identified by an
|
||||
* actor endpoint URI or by a <code>CamelActorIdentifier</code> message header.
|
||||
* <ul>
|
||||
* <li>If the exchange pattern is out-capable and <code>blocking</code> is set to
|
||||
* <code>true</code> then the producer waits for a reply, using the !! operator.</li>
|
||||
* <li>If the exchange pattern is out-capable and <code>blocking</code> is set to
|
||||
* <code>false</code> then the producer sends the message using the ! operator, together
|
||||
* with a callback handler. The callback handler is an <code>ActorRef</code> that can be
|
||||
* used by the receiving actor to asynchronously reply to the route that is sending the
|
||||
* message.</li>
|
||||
* <li>If the exchange pattern is in-only then the producer sends the message using the
|
||||
* ! operator.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see akka.camel.component.ActorComponent
|
||||
* @see akka.camel.component.ActorEndpoint
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) with AsyncProcessor {
|
||||
import ActorProducer._
|
||||
|
||||
private lazy val uuid = uuidFrom(ep.idValue.getOrElse(throw new ActorIdentifierNotSetException))
|
||||
|
||||
def process(exchange: Exchange) =
|
||||
if (exchange.getPattern.isOutCapable) sendSync(exchange) else sendAsync(exchange)
|
||||
|
||||
def process(exchange: Exchange, callback: AsyncCallback): Boolean = {
|
||||
(exchange.getPattern.isOutCapable, ep.blocking) match {
|
||||
case (true, true) => {
|
||||
sendSync(exchange)
|
||||
callback.done(true)
|
||||
true
|
||||
}
|
||||
case (true, false) => {
|
||||
sendAsync(exchange, Some(AsyncCallbackAdapter(exchange, callback)))
|
||||
false
|
||||
}
|
||||
case (false, _) => {
|
||||
sendAsync(exchange)
|
||||
callback.done(true)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def sendSync(exchange: Exchange) = {
|
||||
val actor = target(exchange)
|
||||
val result: Any = actor !! requestFor(exchange)
|
||||
|
||||
result match {
|
||||
case Some(msg: Failure) => exchange.fromFailureMessage(msg)
|
||||
case Some(msg) => exchange.fromResponseMessage(Message.canonicalize(msg))
|
||||
case None => throw new TimeoutException("timeout (%d ms) while waiting response from %s"
|
||||
format (actor.timeout, ep.getEndpointUri))
|
||||
}
|
||||
}
|
||||
|
||||
private def sendAsync(exchange: Exchange, sender: Option[ActorRef] = None) =
|
||||
target(exchange).!(requestFor(exchange))(sender)
|
||||
|
||||
private def target(exchange: Exchange) =
|
||||
targetOption(exchange) getOrElse (throw new ActorNotRegisteredException(ep.getEndpointUri))
|
||||
|
||||
private def targetOption(exchange: Exchange): Option[ActorRef] = ep.idType match {
|
||||
case "id" => targetById(targetId(exchange))
|
||||
case "uuid" => targetByUuid(targetUuid(exchange))
|
||||
}
|
||||
|
||||
private def targetId(exchange: Exchange) = exchange.getIn.getHeader(ActorComponent.ActorIdentifier) match {
|
||||
case id: String => id
|
||||
case null => ep.idValue.getOrElse(throw new ActorIdentifierNotSetException)
|
||||
}
|
||||
|
||||
private def targetUuid(exchange: Exchange) = exchange.getIn.getHeader(ActorComponent.ActorIdentifier) match {
|
||||
case uuid: Uuid => uuid
|
||||
case uuid: String => uuidFrom(uuid)
|
||||
case null => uuid
|
||||
}
|
||||
|
||||
private def targetById(id: String) = ActorRegistry.actorsFor(id) match {
|
||||
case actors if actors.length == 0 => None
|
||||
case actors => Some(actors(0))
|
||||
}
|
||||
|
||||
private def targetByUuid(uuid: Uuid) = ActorRegistry.actorFor(uuid)
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] object ActorProducer {
|
||||
def requestFor(exchange: Exchange) =
|
||||
exchange.toRequestMessage(Map(Message.MessageExchangeId -> exchange.getExchangeId))
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown to indicate that an actor referenced by an endpoint URI cannot be
|
||||
* found in the ActorRegistry.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class ActorNotRegisteredException(uri: String) extends RuntimeException {
|
||||
override def getMessage = "%s not registered" format uri
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown to indicate that no actor identifier has been set.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class ActorIdentifierNotSetException extends RuntimeException {
|
||||
override def getMessage = "actor identifier not set"
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[akka] object AsyncCallbackAdapter {
|
||||
/**
|
||||
* Creates and starts an <code>AsyncCallbackAdapter</code>.
|
||||
*
|
||||
* @param exchange message exchange to write results to.
|
||||
* @param callback callback object to generate completion notifications.
|
||||
*/
|
||||
def apply(exchange: Exchange, callback: AsyncCallback) =
|
||||
new AsyncCallbackAdapter(exchange, callback).start
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts an <code>ActorRef</code> to a Camel <code>AsyncCallback</code>. Used by receiving actors to reply
|
||||
* asynchronously to Camel routes with <code>ActorRef.reply</code>.
|
||||
* <p>
|
||||
* <em>Please note</em> that this adapter can only be used locally at the moment which should not
|
||||
* be a problem is most situations since Camel endpoints are only activated for local actor references,
|
||||
* never for remote references.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[akka] class AsyncCallbackAdapter(exchange: Exchange, callback: AsyncCallback) extends ActorRef with ScalaActorRef {
|
||||
|
||||
def start = {
|
||||
_status = ActorRefInternals.RUNNING
|
||||
this
|
||||
}
|
||||
|
||||
def stop() = {
|
||||
_status = ActorRefInternals.SHUTDOWN
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the initial <code>exchange</code> with the reply <code>message</code> and uses the
|
||||
* <code>callback</code> handler to notify Camel about the asynchronous completion of the message
|
||||
* exchange.
|
||||
*
|
||||
* @param message reply message
|
||||
* @param sender ignored
|
||||
*/
|
||||
protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]) = {
|
||||
message match {
|
||||
case msg: Failure => exchange.fromFailureMessage(msg)
|
||||
case msg => exchange.fromResponseMessage(Message.canonicalize(msg))
|
||||
}
|
||||
callback.done(false)
|
||||
}
|
||||
|
||||
def actorClass: Class[_ <: Actor] = unsupported
|
||||
def actorClassName = unsupported
|
||||
def dispatcher_=(md: MessageDispatcher): Unit = unsupported
|
||||
def dispatcher: MessageDispatcher = unsupported
|
||||
def makeRemote(hostname: String, port: Int): Unit = unsupported
|
||||
def makeRemote(address: InetSocketAddress): Unit = unsupported
|
||||
def homeAddress_=(address: InetSocketAddress): Unit = unsupported
|
||||
def remoteAddress: Option[InetSocketAddress] = unsupported
|
||||
def link(actorRef: ActorRef): Unit = unsupported
|
||||
def unlink(actorRef: ActorRef): Unit = unsupported
|
||||
def startLink(actorRef: ActorRef): Unit = unsupported
|
||||
def startLinkRemote(actorRef: ActorRef, hostname: String, port: Int): Unit = unsupported
|
||||
def spawn(clazz: Class[_ <: Actor]): ActorRef = unsupported
|
||||
def spawnRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = unsupported
|
||||
def spawnLink(clazz: Class[_ <: Actor]): ActorRef = unsupported
|
||||
def spawnLinkRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = unsupported
|
||||
def shutdownLinkedActors: Unit = unsupported
|
||||
def supervisor: Option[ActorRef] = unsupported
|
||||
protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout[T](message: Any, timeout: Long, senderOption: Option[ActorRef], senderFuture: Option[CompletableFuture[T]]) = unsupported
|
||||
protected[akka] def mailbox: AnyRef = unsupported
|
||||
protected[akka] def mailbox_=(msg: AnyRef):AnyRef = unsupported
|
||||
protected[akka] def restart(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]): Unit = unsupported
|
||||
protected[akka] def restartLinkedActors(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]): Unit = unsupported
|
||||
protected[akka] def handleTrapExit(dead: ActorRef, reason: Throwable): Unit = unsupported
|
||||
protected[akka] def linkedActors: JMap[Uuid, ActorRef] = unsupported
|
||||
protected[akka] def linkedActorsAsList: List[ActorRef] = unsupported
|
||||
protected[akka] def invoke(messageHandle: MessageInvocation): Unit = unsupported
|
||||
protected[akka] def remoteAddress_=(addr: Option[InetSocketAddress]): Unit = unsupported
|
||||
protected[akka] def registerSupervisorAsRemoteActor = unsupported
|
||||
protected[akka] def supervisor_=(sup: Option[ActorRef]): Unit = unsupported
|
||||
protected[akka] def actorInstance: AtomicReference[Actor] = unsupported
|
||||
|
||||
private def unsupported = throw new UnsupportedOperationException("Not supported for %s" format classOf[AsyncCallbackAdapter].getName)
|
||||
}
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package akka.camel.component
|
||||
|
||||
import java.util.Map
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import org.apache.camel.CamelContext
|
||||
import org.apache.camel.component.bean._
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
object TypedActorComponent {
|
||||
/**
|
||||
* Default schema name for typed actor endpoint URIs.
|
||||
*/
|
||||
val InternalSchema = "typed-actor-internal"
|
||||
}
|
||||
|
||||
/**
|
||||
* Camel component for exchanging messages with typed actors. This component
|
||||
* tries to obtain the typed actor from its <code>typedActorRegistry</code>
|
||||
* first. If it's not there it tries to obtain it from the CamelContext's registry.
|
||||
*
|
||||
* @see org.apache.camel.component.bean.BeanComponent
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class TypedActorComponent extends BeanComponent {
|
||||
val typedActorRegistry = new ConcurrentHashMap[String, AnyRef]
|
||||
|
||||
/**
|
||||
* Creates an <code>org.apache.camel.component.bean.BeanEndpoint</code> with a custom
|
||||
* bean holder that uses <code>typedActorRegistry</code> for getting access to typed
|
||||
* actors (beans).
|
||||
*
|
||||
* @see akka.camel.component.TypedActorHolder
|
||||
*/
|
||||
override def createEndpoint(uri: String, remaining: String, parameters: Map[String, AnyRef]) = {
|
||||
val endpoint = new BeanEndpoint(uri, this)
|
||||
endpoint.setBeanName(remaining)
|
||||
endpoint.setBeanHolder(createBeanHolder(remaining))
|
||||
setProperties(endpoint.getProcessor, parameters)
|
||||
endpoint
|
||||
}
|
||||
|
||||
private def createBeanHolder(beanName: String) =
|
||||
new TypedActorHolder(typedActorRegistry, getCamelContext, beanName).createCacheHolder
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>org.apache.camel.component.bean.BeanHolder</code> implementation that uses a custom
|
||||
* registry for getting access to typed actors.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class TypedActorHolder(typedActorRegistry: Map[String, AnyRef], context: CamelContext, name: String)
|
||||
extends RegistryBean(context, name) {
|
||||
|
||||
/**
|
||||
* Returns an <code>akka.camel.component.TypedActorInfo</code> instance.
|
||||
*/
|
||||
override def getBeanInfo: BeanInfo =
|
||||
new TypedActorInfo(getContext, getBean.getClass, getParameterMappingStrategy)
|
||||
|
||||
/**
|
||||
* Obtains a typed actor from <code>typedActorRegistry</code>. If the typed actor cannot
|
||||
* be found then this method tries to obtain the actor from the CamelContext's registry.
|
||||
*
|
||||
* @return a typed actor or <code>null</code>.
|
||||
*/
|
||||
override def getBean: AnyRef = {
|
||||
val bean = typedActorRegistry.get(getName)
|
||||
if (bean eq null) super.getBean else bean
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Typed actor meta information.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class TypedActorInfo(context: CamelContext, clazz: Class[_], strategy: ParameterMappingStrategy)
|
||||
extends BeanInfo(context, clazz, strategy) {
|
||||
|
||||
/**
|
||||
* Introspects AspectWerkz proxy classes.
|
||||
*
|
||||
* @param clazz AspectWerkz proxy class.
|
||||
*/
|
||||
protected override def introspect(clazz: Class[_]): Unit = {
|
||||
|
||||
// TODO: fix target class detection in BeanInfo.introspect(Class)
|
||||
// Camel assumes that classes containing a '$$' in the class name
|
||||
// are classes generated with CGLIB. This conflicts with proxies
|
||||
// created from interfaces with AspectWerkz. Once the fix is in
|
||||
// place this method can be removed.
|
||||
|
||||
for (method <- clazz.getDeclaredMethods) {
|
||||
if (isValidMethod(clazz, method)) {
|
||||
introspect(clazz, method)
|
||||
}
|
||||
}
|
||||
val superclass = clazz.getSuperclass
|
||||
if ((superclass ne null) && !superclass.equals(classOf[AnyRef])) {
|
||||
introspect(superclass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.ActorRegistry;
|
||||
import akka.actor.TypedActor;
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.japi.SideEffect;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import static akka.camel.CamelContextManager.*;
|
||||
import static akka.camel.CamelServiceManager.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class ConsumerJavaTestBase {
|
||||
|
||||
private SampleErrorHandlingTypedConsumer consumer;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpBeforeClass() {
|
||||
startCamelService();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownAfterClass() {
|
||||
stopCamelService();
|
||||
ActorRegistry.shutdownAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleExceptionThrownByActorAndGenerateCustomResponse() {
|
||||
getMandatoryService().awaitEndpointActivation(1, new SideEffect() {
|
||||
public void apply() {
|
||||
UntypedActor.actorOf(SampleErrorHandlingConsumer.class).start();
|
||||
}
|
||||
});
|
||||
String result = getMandatoryTemplate().requestBody("direct:error-handler-test-java", "hello", String.class);
|
||||
assertEquals("error: hello", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleExceptionThrownByTypedActorAndGenerateCustomResponse() {
|
||||
getMandatoryService().awaitEndpointActivation(1, new SideEffect() {
|
||||
public void apply() {
|
||||
consumer = TypedActor.newInstance(
|
||||
SampleErrorHandlingTypedConsumer.class,
|
||||
SampleErrorHandlingTypedConsumerImpl.class);
|
||||
}
|
||||
});
|
||||
String result = getMandatoryTemplate().requestBody("direct:error-handler-test-java-typed", "hello", String.class);
|
||||
assertEquals("error: hello", result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import org.apache.camel.NoTypeConversionAvailableException;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import akka.camel.CamelContextManager;
|
||||
import akka.camel.Message;
|
||||
import akka.japi.Function;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class MessageJavaTestBase {
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpBeforeClass() {
|
||||
CamelContextManager.init();
|
||||
}
|
||||
|
||||
@Test public void shouldConvertDoubleBodyToString() {
|
||||
assertEquals("1.4", new Message("1.4").getBodyAs(String.class));
|
||||
}
|
||||
|
||||
@Test(expected=NoTypeConversionAvailableException.class)
|
||||
public void shouldThrowExceptionWhenConvertingDoubleBodyToInputStream() {
|
||||
new Message(1.4).getBodyAs(InputStream.class);
|
||||
}
|
||||
|
||||
@Test public void shouldReturnDoubleHeader() {
|
||||
Message message = new Message("test" , createMap("test", 1.4));
|
||||
assertEquals(1.4, message.getHeader("test"));
|
||||
}
|
||||
|
||||
@Test public void shouldConvertDoubleHeaderToString() {
|
||||
Message message = new Message("test" , createMap("test", 1.4));
|
||||
assertEquals("1.4", message.getHeaderAs("test", String.class));
|
||||
}
|
||||
|
||||
@Test public void shouldReturnSubsetOfHeaders() {
|
||||
Message message = new Message("test" , createMap("A", "1", "B", "2"));
|
||||
assertEquals(createMap("B", "2"), message.getHeaders(createSet("B")));
|
||||
}
|
||||
|
||||
@Test(expected=UnsupportedOperationException.class)
|
||||
public void shouldReturnSubsetOfHeadersUnmodifiable() {
|
||||
Message message = new Message("test" , createMap("A", "1", "B", "2"));
|
||||
message.getHeaders(createSet("B")).put("x", "y");
|
||||
}
|
||||
|
||||
@Test public void shouldReturnAllHeaders() {
|
||||
Message message = new Message("test" , createMap("A", "1", "B", "2"));
|
||||
assertEquals(createMap("A", "1", "B", "2"), message.getHeaders());
|
||||
}
|
||||
|
||||
@Test(expected=UnsupportedOperationException.class)
|
||||
public void shouldReturnAllHeadersUnmodifiable() {
|
||||
Message message = new Message("test" , createMap("A", "1", "B", "2"));
|
||||
message.getHeaders().put("x", "y");
|
||||
}
|
||||
|
||||
@Test public void shouldTransformBodyAndPreserveHeaders() {
|
||||
assertEquals(
|
||||
new Message("ab", createMap("A", "1")),
|
||||
new Message("a" , createMap("A", "1")).transformBody((Function)new TestTransformer()));
|
||||
}
|
||||
|
||||
@Test public void shouldConvertBodyAndPreserveHeaders() {
|
||||
assertEquals(
|
||||
new Message("1.4", createMap("A", "1")),
|
||||
new Message(1.4 , createMap("A", "1")).setBodyAs(String.class));
|
||||
}
|
||||
|
||||
@Test public void shouldSetBodyAndPreserveHeaders() {
|
||||
assertEquals(
|
||||
new Message("test2" , createMap("A", "1")),
|
||||
new Message("test1" , createMap("A", "1")).setBody("test2"));
|
||||
}
|
||||
|
||||
@Test public void shouldSetHeadersAndPreserveBody() {
|
||||
assertEquals(
|
||||
new Message("test1" , createMap("C", "3")),
|
||||
new Message("test1" , createMap("A", "1")).setHeaders(createMap("C", "3")));
|
||||
}
|
||||
|
||||
@Test public void shouldAddHeaderAndPreserveBodyAndHeaders() {
|
||||
assertEquals(
|
||||
new Message("test1" , createMap("A", "1", "B", "2")),
|
||||
new Message("test1" , createMap("A", "1")).addHeader("B", "2"));
|
||||
}
|
||||
|
||||
@Test public void shouldAddHeadersAndPreserveBodyAndHeaders() {
|
||||
assertEquals(
|
||||
new Message("test1" , createMap("A", "1", "B", "2")),
|
||||
new Message("test1" , createMap("A", "1")).addHeaders(createMap("B", "2")));
|
||||
}
|
||||
|
||||
@Test public void shouldRemoveHeadersAndPreserveBodyAndRemainingHeaders() {
|
||||
assertEquals(
|
||||
new Message("test1" , createMap("A", "1")),
|
||||
new Message("test1" , createMap("A", "1", "B", "2")).removeHeader("B"));
|
||||
}
|
||||
|
||||
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 implements Function<String, String> {
|
||||
public String apply(String param) {
|
||||
return param + "b";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import org.apache.camel.builder.Builder;
|
||||
import org.apache.camel.model.ProcessorDefinition;
|
||||
import org.apache.camel.model.RouteDefinition;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleErrorHandlingConsumer extends UntypedConsumerActor {
|
||||
|
||||
public String getEndpointUri() {
|
||||
return "direct:error-handler-test-java";
|
||||
}
|
||||
|
||||
public boolean isBlocking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void preStart() {
|
||||
onRouteDefinition(new RouteDefinitionHandler() {
|
||||
public ProcessorDefinition<?> onRouteDefinition(RouteDefinition rd) {
|
||||
return rd.onException(Exception.class).handled(true).transform(Builder.exceptionMessage()).end();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onReceive(Object message) throws Exception {
|
||||
Message msg = (Message)message;
|
||||
String body = msg.getBodyAs(String.class);
|
||||
throw new Exception(String.format("error: %s", body));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public interface SampleErrorHandlingTypedConsumer {
|
||||
|
||||
@consume(value="direct:error-handler-test-java-typed", routeDefinitionHandler=SampleRouteDefinitionHandler.class)
|
||||
String willFail(String s);
|
||||
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.TypedActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleErrorHandlingTypedConsumerImpl extends TypedActor implements SampleErrorHandlingTypedConsumer {
|
||||
|
||||
public String willFail(String s) {
|
||||
throw new RuntimeException(String.format("error: %s", s));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.camel.consume;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public interface SampleRemoteTypedConsumer {
|
||||
|
||||
@consume("direct:remote-typed-consumer")
|
||||
public String foo(String s);
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.TypedActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleRemoteTypedConsumerImpl extends TypedActor implements SampleRemoteTypedConsumer {
|
||||
|
||||
public String foo(String s) {
|
||||
return String.format("remote typed actor: %s", s);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.camel.RemoteUntypedConsumerActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleRemoteUntypedConsumer extends RemoteUntypedConsumerActor {
|
||||
|
||||
public SampleRemoteUntypedConsumer() {
|
||||
this("localhost", 7774);
|
||||
}
|
||||
|
||||
public SampleRemoteUntypedConsumer(String host, int port) {
|
||||
super(host, port);
|
||||
}
|
||||
|
||||
public String getEndpointUri() {
|
||||
return "direct:remote-untyped-consumer";
|
||||
}
|
||||
|
||||
public void onReceive(Object message) {
|
||||
Message msg = (Message)message;
|
||||
String body = msg.getBodyAs(String.class);
|
||||
String header = msg.getHeaderAs("test", String.class);
|
||||
getContext().replySafe(String.format("%s %s", body, header));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import org.apache.camel.builder.Builder;
|
||||
import org.apache.camel.model.ProcessorDefinition;
|
||||
import org.apache.camel.model.RouteDefinition;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleRouteDefinitionHandler implements RouteDefinitionHandler {
|
||||
public ProcessorDefinition<?> onRouteDefinition(RouteDefinition rd) {
|
||||
return rd.onException(Exception.class).handled(true).transform(Builder.exceptionMessage()).end();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public interface SampleTypedActor {
|
||||
|
||||
public String foo(String s);
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.TypedActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleTypedActorImpl extends TypedActor implements SampleTypedActor {
|
||||
|
||||
public String foo(String s) {
|
||||
return String.format("foo: %s", s);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import org.apache.camel.Body;
|
||||
import org.apache.camel.Header;
|
||||
|
||||
import akka.camel.consume;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public interface SampleTypedConsumer {
|
||||
|
||||
public String m1(String b, String h);
|
||||
public String m2(@Body String b, @Header("test") String h);
|
||||
public String m3(@Body String b, @Header("test") String h);
|
||||
|
||||
@consume("direct:m4")
|
||||
public String m4(@Body String b, @Header("test") String h);
|
||||
public void m5(@Body String b, @Header("test") String h);
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.TypedActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleTypedConsumerImpl extends TypedActor implements SampleTypedConsumer {
|
||||
|
||||
public String m1(String b, String h) {
|
||||
return "m1: " + b + " " + h;
|
||||
}
|
||||
|
||||
@consume("direct:m2")
|
||||
public String m2(String b, String h) {
|
||||
return "m2: " + b + " " + h;
|
||||
}
|
||||
|
||||
@consume("direct:m3")
|
||||
public String m3(String b, String h) {
|
||||
return "m3: " + b + " " + h;
|
||||
}
|
||||
|
||||
public String m4(String b, String h) {
|
||||
return "m4: " + b + " " + h;
|
||||
}
|
||||
|
||||
public void m5(String b, String h) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.camel.consume;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public interface SampleTypedSingleConsumer {
|
||||
|
||||
@consume("direct:foo")
|
||||
public void foo(String b);
|
||||
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.TypedActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleTypedSingleConsumerImpl extends TypedActor implements SampleTypedSingleConsumer {
|
||||
|
||||
public void foo(String b) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.UntypedActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleUntypedActor extends UntypedActor {
|
||||
public void onReceive(Object message) {
|
||||
logger().debug("Yay! I haz a message!");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.camel.UntypedConsumerActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleUntypedConsumer extends UntypedConsumerActor {
|
||||
|
||||
public String getEndpointUri() {
|
||||
return "direct:test-untyped-consumer";
|
||||
}
|
||||
|
||||
public void onReceive(Object message) {
|
||||
Message msg = (Message)message;
|
||||
String body = msg.getBodyAs(String.class);
|
||||
String header = msg.getHeaderAs("test", String.class);
|
||||
getContext().replySafe(String.format("%s %s", body, header));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleUntypedConsumerBlocking extends UntypedConsumerActor {
|
||||
|
||||
public String getEndpointUri() {
|
||||
return "direct:test-untyped-consumer-blocking";
|
||||
}
|
||||
|
||||
public boolean isBlocking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onReceive(Object message) {
|
||||
Message msg = (Message)message;
|
||||
String body = msg.getBodyAs(String.class);
|
||||
String header = msg.getHeaderAs("test", String.class);
|
||||
getContext().replySafe(String.format("%s %s", body, header));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleUntypedForwardingProducer extends UntypedProducerActor {
|
||||
|
||||
public String getEndpointUri() {
|
||||
return "direct:producer-test-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveAfterProduce(Object message) {
|
||||
Message msg = (Message)message;
|
||||
String body = msg.getBodyAs(String.class);
|
||||
CamelContextManager.getMandatoryTemplate().sendBody("direct:forward-test-1", body);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package akka.camel;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleUntypedReplyingProducer extends UntypedProducerActor {
|
||||
|
||||
public String getEndpointUri() {
|
||||
return "direct:producer-test-1";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import org.apache.camel.impl.{DefaultProducerTemplate, DefaultCamelContext}
|
||||
import org.junit.Test
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class CamelContextLifecycleTest extends JUnitSuite with CamelContextLifecycle {
|
||||
@Test def shouldManageCustomCamelContext {
|
||||
assert(context === None)
|
||||
assert(template === None)
|
||||
|
||||
intercept[IllegalStateException] { mandatoryContext }
|
||||
intercept[IllegalStateException] { mandatoryTemplate }
|
||||
|
||||
val ctx = new TestCamelContext
|
||||
assert(ctx.isStreamCaching === false)
|
||||
|
||||
init(ctx)
|
||||
|
||||
assert(mandatoryContext.isStreamCaching === true)
|
||||
assert(!mandatoryContext.asInstanceOf[TestCamelContext].isStarted)
|
||||
assert(mandatoryTemplate.asInstanceOf[DefaultProducerTemplate].isStarted)
|
||||
|
||||
start
|
||||
|
||||
assert(mandatoryContext.asInstanceOf[TestCamelContext].isStarted)
|
||||
assert(mandatoryTemplate.asInstanceOf[DefaultProducerTemplate].isStarted)
|
||||
|
||||
stop
|
||||
|
||||
assert(!mandatoryContext.asInstanceOf[TestCamelContext].isStarted)
|
||||
assert(!mandatoryTemplate.asInstanceOf[DefaultProducerTemplate].isStarted)
|
||||
}
|
||||
|
||||
class TestCamelContext extends DefaultCamelContext
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import org.apache.camel.impl.{DefaultCamelContext, DefaultExchange}
|
||||
import org.apache.camel.ExchangePattern
|
||||
import org.junit.Test
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class CamelExchangeAdapterTest extends JUnitSuite {
|
||||
import CamelMessageConversion.toExchangeAdapter
|
||||
|
||||
@Test def shouldSetInMessageFromRequestMessage = {
|
||||
val e1 = sampleInOnly.fromRequestMessage(Message("x"))
|
||||
assert(e1.getIn.getBody === "x")
|
||||
val e2 = sampleInOut.fromRequestMessage(Message("y"))
|
||||
assert(e2.getIn.getBody === "y")
|
||||
}
|
||||
|
||||
@Test def shouldSetOutMessageFromResponseMessage = {
|
||||
val e1 = sampleInOut.fromResponseMessage(Message("y"))
|
||||
assert(e1.getOut.getBody === "y")
|
||||
}
|
||||
|
||||
@Test def shouldSetInMessageFromResponseMessage = {
|
||||
val e1 = sampleInOnly.fromResponseMessage(Message("x"))
|
||||
assert(e1.getIn.getBody === "x")
|
||||
}
|
||||
|
||||
@Test def shouldSetExceptionFromFailureMessage = {
|
||||
val e1 = sampleInOnly.fromFailureMessage(Failure(new Exception("test1")))
|
||||
assert(e1.getException.getMessage === "test1")
|
||||
val e2 = sampleInOut.fromFailureMessage(Failure(new Exception("test2")))
|
||||
assert(e2.getException.getMessage === "test2")
|
||||
}
|
||||
|
||||
@Test def shouldCreateRequestMessageFromInMessage = {
|
||||
val m = sampleInOnly.toRequestMessage
|
||||
assert(m === Message("test-in", Map("key-in" -> "val-in")))
|
||||
}
|
||||
|
||||
@Test def shouldCreateResponseMessageFromInMessage = {
|
||||
val m = sampleInOnly.toResponseMessage
|
||||
assert(m === Message("test-in", Map("key-in" -> "val-in")))
|
||||
}
|
||||
|
||||
@Test def shouldCreateResponseMessageFromOutMessage = {
|
||||
val m = sampleInOut.toResponseMessage
|
||||
assert(m === Message("test-out", Map("key-out" -> "val-out")))
|
||||
}
|
||||
|
||||
@Test def shouldCreateFailureMessageFromExceptionAndInMessage = {
|
||||
val e1 = sampleInOnly
|
||||
e1.setException(new Exception("test1"))
|
||||
assert(e1.toFailureMessage.cause.getMessage === "test1")
|
||||
assert(e1.toFailureMessage.headers("key-in") === "val-in")
|
||||
}
|
||||
|
||||
@Test def shouldCreateFailureMessageFromExceptionAndOutMessage = {
|
||||
val e1 = sampleInOut
|
||||
e1.setException(new Exception("test2"))
|
||||
assert(e1.toFailureMessage.cause.getMessage === "test2")
|
||||
assert(e1.toFailureMessage.headers("key-out") === "val-out")
|
||||
}
|
||||
|
||||
@Test def shouldCreateRequestMessageFromInMessageWithAdditionalHeader = {
|
||||
val m = sampleInOnly.toRequestMessage(Map("x" -> "y"))
|
||||
assert(m === Message("test-in", Map("key-in" -> "val-in", "x" -> "y")))
|
||||
}
|
||||
|
||||
@Test def shouldCreateResponseMessageFromInMessageWithAdditionalHeader = {
|
||||
val m = sampleInOnly.toResponseMessage(Map("x" -> "y"))
|
||||
assert(m === Message("test-in", Map("key-in" -> "val-in", "x" -> "y")))
|
||||
}
|
||||
|
||||
@Test def shouldCreateResponseMessageFromOutMessageWithAdditionalHeader = {
|
||||
val m = sampleInOut.toResponseMessage(Map("x" -> "y"))
|
||||
assert(m === Message("test-out", Map("key-out" -> "val-out", "x" -> "y")))
|
||||
}
|
||||
|
||||
@Test def shouldCreateFailureMessageFromExceptionAndInMessageWithAdditionalHeader = {
|
||||
val e1 = sampleInOnly
|
||||
e1.setException(new Exception("test1"))
|
||||
assert(e1.toFailureMessage.cause.getMessage === "test1")
|
||||
val headers = e1.toFailureMessage(Map("x" -> "y")).headers
|
||||
assert(headers("key-in") === "val-in")
|
||||
assert(headers("x") === "y")
|
||||
}
|
||||
|
||||
@Test def shouldCreateFailureMessageFromExceptionAndOutMessageWithAdditionalHeader = {
|
||||
val e1 = sampleInOut
|
||||
e1.setException(new Exception("test2"))
|
||||
assert(e1.toFailureMessage.cause.getMessage === "test2")
|
||||
val headers = e1.toFailureMessage(Map("x" -> "y")).headers
|
||||
assert(headers("key-out") === "val-out")
|
||||
assert(headers("x") === "y")
|
||||
}
|
||||
|
||||
private def sampleInOnly = sampleExchange(ExchangePattern.InOnly)
|
||||
private def sampleInOut = sampleExchange(ExchangePattern.InOut)
|
||||
|
||||
private def sampleExchange(pattern: ExchangePattern) = {
|
||||
val exchange = new DefaultExchange(new DefaultCamelContext)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import org.apache.camel.impl.DefaultMessage
|
||||
import org.junit.Test
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class CamelMessageAdapterTest extends JUnitSuite {
|
||||
import CamelMessageConversion.toMessageAdapter
|
||||
|
||||
@Test def shouldOverwriteBodyAndAddHeader = {
|
||||
val cm = sampleMessage.fromMessage(Message("blah", Map("key" -> "baz")))
|
||||
assert(cm.getBody === "blah")
|
||||
assert(cm.getHeader("foo") === "bar")
|
||||
assert(cm.getHeader("key") === "baz")
|
||||
}
|
||||
|
||||
@Test def shouldCreateMessageWithBodyAndHeader = {
|
||||
val m = sampleMessage.toMessage
|
||||
assert(m.body === "test")
|
||||
assert(m.headers("foo") === "bar")
|
||||
}
|
||||
|
||||
@Test def shouldCreateMessageWithBodyAndHeaderAndCustomHeader = {
|
||||
val m = sampleMessage.toMessage(Map("key" -> "baz"))
|
||||
assert(m.body === "test")
|
||||
assert(m.headers("foo") === "bar")
|
||||
assert(m.headers("key") === "baz")
|
||||
}
|
||||
|
||||
private[camel] def sampleMessage = {
|
||||
val message = new DefaultMessage
|
||||
message.setBody("test")
|
||||
message.setHeader("foo", "bar")
|
||||
message
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import org.scalatest.{BeforeAndAfterAll, WordSpec}
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
|
||||
import akka.actor.ActorRegistry
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class CamelServiceManagerTest extends WordSpec with BeforeAndAfterAll with MustMatchers {
|
||||
|
||||
override def afterAll = {
|
||||
CamelServiceManager.stopCamelService
|
||||
ActorRegistry.shutdownAll
|
||||
}
|
||||
|
||||
"A CamelServiceManager" when {
|
||||
"the startCamelService method been has been called" must {
|
||||
"have registered the started CamelService instance" in {
|
||||
val service = CamelServiceManager.startCamelService
|
||||
CamelServiceManager.mandatoryService must be theSameInstanceAs (service)
|
||||
}
|
||||
}
|
||||
"the stopCamelService method been has been called" must {
|
||||
"have unregistered the current CamelService instance" in {
|
||||
val service = CamelServiceManager.stopCamelService
|
||||
CamelServiceManager.service must be (None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"A CamelServiceManager" when {
|
||||
val service = CamelServiceFactory.createCamelService
|
||||
"a CamelService instance has been started externally" must {
|
||||
"have registered the started CamelService instance" in {
|
||||
service.start
|
||||
CamelServiceManager.mandatoryService must be theSameInstanceAs (service)
|
||||
}
|
||||
}
|
||||
"the current CamelService instance has been stopped externally" must {
|
||||
"have unregistered the current CamelService instance" in {
|
||||
service.stop
|
||||
CamelServiceManager.service must be (None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"A CamelServiceManager" when {
|
||||
"a CamelService has been started" must {
|
||||
"not allow further CamelService instances to be started" in {
|
||||
CamelServiceManager.startCamelService
|
||||
intercept[IllegalStateException] { CamelServiceManager.startCamelService }
|
||||
}
|
||||
}
|
||||
"a CamelService has been stopped" must {
|
||||
"only allow the current CamelService instance to be stopped" in {
|
||||
intercept[IllegalStateException] { CamelServiceFactory.createCamelService.stop }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class ConsumerJavaTest extends ConsumerJavaTestBase with JUnitSuite
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import org.junit.Test
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import akka.actor.{ActorRef, Actor, UntypedActor}
|
||||
|
||||
class ConsumerRegisteredTest extends JUnitSuite {
|
||||
import ConsumerRegisteredTest._
|
||||
|
||||
@Test def shouldCreateSomeNonBlockingPublishRequestFromConsumer = {
|
||||
val c = Actor.actorOf[ConsumerActor1]
|
||||
val event = ConsumerActorRegistered.forConsumer(c)
|
||||
assert(event === Some(ConsumerActorRegistered(c, consumerOf(c))))
|
||||
}
|
||||
|
||||
@Test def shouldCreateSomeBlockingPublishRequestFromConsumer = {
|
||||
val c = Actor.actorOf[ConsumerActor2]
|
||||
val event = ConsumerActorRegistered.forConsumer(c)
|
||||
assert(event === Some(ConsumerActorRegistered(c, consumerOf(c))))
|
||||
}
|
||||
|
||||
@Test def shouldCreateNoneFromConsumer = {
|
||||
val event = ConsumerActorRegistered.forConsumer(Actor.actorOf[PlainActor])
|
||||
assert(event === None)
|
||||
}
|
||||
|
||||
@Test def shouldCreateSomeNonBlockingPublishRequestFromUntypedConsumer = {
|
||||
val uc = UntypedActor.actorOf(classOf[SampleUntypedConsumer])
|
||||
val event = ConsumerActorRegistered.forConsumer(uc)
|
||||
assert(event === Some(ConsumerActorRegistered(uc, consumerOf(uc))))
|
||||
}
|
||||
|
||||
@Test def shouldCreateSomeBlockingPublishRequestFromUntypedConsumer = {
|
||||
val uc = UntypedActor.actorOf(classOf[SampleUntypedConsumerBlocking])
|
||||
val event = ConsumerActorRegistered.forConsumer(uc)
|
||||
assert(event === Some(ConsumerActorRegistered(uc, consumerOf(uc))))
|
||||
}
|
||||
|
||||
@Test def shouldCreateNoneFromUntypedConsumer = {
|
||||
val a = UntypedActor.actorOf(classOf[SampleUntypedActor])
|
||||
val event = ConsumerActorRegistered.forConsumer(a)
|
||||
assert(event === None)
|
||||
}
|
||||
|
||||
private def consumerOf(ref: ActorRef) = ref.actor.asInstanceOf[Consumer]
|
||||
}
|
||||
|
||||
object ConsumerRegisteredTest {
|
||||
class ConsumerActor1 extends Actor with Consumer {
|
||||
def endpointUri = "mock:test1"
|
||||
protected def receive = null
|
||||
}
|
||||
|
||||
class ConsumerActor2 extends Actor with Consumer {
|
||||
def endpointUri = "mock:test2"
|
||||
override def blocking = true
|
||||
protected def receive = null
|
||||
}
|
||||
|
||||
class PlainActor extends Actor {
|
||||
protected def receive = null
|
||||
}
|
||||
}
|
||||
|
|
@ -1,271 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import java.util.concurrent.{TimeoutException, CountDownLatch, TimeUnit}
|
||||
|
||||
import org.apache.camel.CamelExecutionException
|
||||
import org.apache.camel.builder.Builder
|
||||
import org.apache.camel.model.RouteDefinition
|
||||
import org.scalatest.{BeforeAndAfterAll, WordSpec}
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
|
||||
import akka.actor.Actor._
|
||||
import akka.actor._
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class ConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMatchers {
|
||||
import CamelContextManager.mandatoryTemplate
|
||||
import ConsumerScalaTest._
|
||||
|
||||
var service: CamelService = _
|
||||
|
||||
override protected def beforeAll = {
|
||||
ActorRegistry.shutdownAll
|
||||
// create new CamelService instance
|
||||
service = CamelServiceFactory.createCamelService
|
||||
// Register publish requestor as listener
|
||||
service.registerPublishRequestor
|
||||
// register test consumer before starting the CamelService
|
||||
actorOf(new TestConsumer("direct:publish-test-1")).start
|
||||
// start consumer publisher, otherwise we cannot set message
|
||||
// count expectations in the next step (needed for testing only).
|
||||
service.consumerPublisher.start
|
||||
service.awaitEndpointActivation(1) {
|
||||
service.start
|
||||
} must be (true)
|
||||
}
|
||||
|
||||
override protected def afterAll = {
|
||||
service.stop
|
||||
ActorRegistry.shutdownAll
|
||||
}
|
||||
|
||||
"A responding consumer" when {
|
||||
val consumer = actorOf(new TestConsumer("direct:publish-test-2"))
|
||||
"started before starting the CamelService" must {
|
||||
"support an in-out message exchange via its endpoint" in {
|
||||
mandatoryTemplate.requestBody("direct:publish-test-1", "msg1") must equal ("received msg1")
|
||||
}
|
||||
}
|
||||
"not started" must {
|
||||
"not have an associated endpoint in the CamelContext" in {
|
||||
CamelContextManager.mandatoryContext.hasEndpoint("direct:publish-test-2") must be (null)
|
||||
}
|
||||
}
|
||||
"started" must {
|
||||
"support an in-out message exchange via its endpoint" in {
|
||||
service.awaitEndpointActivation(1) {
|
||||
consumer.start
|
||||
} must be (true)
|
||||
mandatoryTemplate.requestBody("direct:publish-test-2", "msg2") must equal ("received msg2")
|
||||
}
|
||||
"have an associated endpoint in the CamelContext" in {
|
||||
CamelContextManager.mandatoryContext.hasEndpoint("direct:publish-test-2") must not be (null)
|
||||
}
|
||||
}
|
||||
"stopped" must {
|
||||
"not support an in-out message exchange via its endpoint" in {
|
||||
service.awaitEndpointDeactivation(1) {
|
||||
consumer.stop
|
||||
} must be (true)
|
||||
intercept[CamelExecutionException] {
|
||||
mandatoryTemplate.requestBody("direct:publish-test-2", "msg2")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"A responding, typed consumer" when {
|
||||
var actor: SampleTypedConsumer = null
|
||||
"started" must {
|
||||
"support in-out message exchanges via its endpoints" in {
|
||||
service.awaitEndpointActivation(3) {
|
||||
actor = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl])
|
||||
} must be (true)
|
||||
mandatoryTemplate.requestBodyAndHeader("direct:m2", "x", "test", "y") must equal ("m2: x y")
|
||||
mandatoryTemplate.requestBodyAndHeader("direct:m3", "x", "test", "y") must equal ("m3: x y")
|
||||
mandatoryTemplate.requestBodyAndHeader("direct:m4", "x", "test", "y") must equal ("m4: x y")
|
||||
}
|
||||
}
|
||||
"stopped" must {
|
||||
"not support in-out message exchanges via its endpoints" in {
|
||||
service.awaitEndpointDeactivation(3) {
|
||||
TypedActor.stop(actor)
|
||||
} must be (true)
|
||||
intercept[CamelExecutionException] {
|
||||
mandatoryTemplate.requestBodyAndHeader("direct:m2", "x", "test", "y")
|
||||
}
|
||||
intercept[CamelExecutionException] {
|
||||
mandatoryTemplate.requestBodyAndHeader("direct:m3", "x", "test", "y")
|
||||
}
|
||||
intercept[CamelExecutionException] {
|
||||
mandatoryTemplate.requestBodyAndHeader("direct:m4", "x", "test", "y")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"A responding, typed consumer (Scala)" when {
|
||||
var actor: TestTypedConsumer = null
|
||||
"started" must {
|
||||
"support in-out message exchanges via its endpoints" in {
|
||||
service.awaitEndpointActivation(2) {
|
||||
actor = TypedActor.newInstance(classOf[TestTypedConsumer], classOf[TestTypedConsumerImpl])
|
||||
} must be (true)
|
||||
mandatoryTemplate.requestBody("direct:publish-test-3", "x") must equal ("foo: x")
|
||||
mandatoryTemplate.requestBody("direct:publish-test-4", "x") must equal ("bar: x")
|
||||
}
|
||||
}
|
||||
"stopped" must {
|
||||
"not support in-out message exchanges via its endpoints" in {
|
||||
service.awaitEndpointDeactivation(2) {
|
||||
TypedActor.stop(actor)
|
||||
} must be (true)
|
||||
intercept[CamelExecutionException] {
|
||||
mandatoryTemplate.requestBody("direct:publish-test-3", "x")
|
||||
}
|
||||
intercept[CamelExecutionException] {
|
||||
mandatoryTemplate.requestBody("direct:publish-test-4", "x")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"A responding, untyped consumer" when {
|
||||
val consumer = UntypedActor.actorOf(classOf[SampleUntypedConsumer])
|
||||
"started" must {
|
||||
"support an in-out message exchange via its endpoint" in {
|
||||
service.awaitEndpointActivation(1) {
|
||||
consumer.start
|
||||
} must be (true)
|
||||
mandatoryTemplate.requestBodyAndHeader("direct:test-untyped-consumer", "x", "test", "y") must equal ("x y")
|
||||
}
|
||||
}
|
||||
"stopped" must {
|
||||
"not support an in-out message exchange via its endpoint" in {
|
||||
service.awaitEndpointDeactivation(1) {
|
||||
consumer.stop
|
||||
} must be (true)
|
||||
intercept[CamelExecutionException] {
|
||||
mandatoryTemplate.sendBodyAndHeader("direct:test-untyped-consumer", "blah", "test", "blub")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"A non-responding, blocking consumer" when {
|
||||
"receiving an in-out message exchange" must {
|
||||
"lead to a TimeoutException" in {
|
||||
service.awaitEndpointActivation(1) {
|
||||
actorOf(new TestBlocker("direct:publish-test-5")).start
|
||||
} must be (true)
|
||||
|
||||
try {
|
||||
mandatoryTemplate.requestBody("direct:publish-test-5", "msg3")
|
||||
fail("expected TimoutException not thrown")
|
||||
} catch {
|
||||
case e => {
|
||||
assert(e.getCause.isInstanceOf[TimeoutException])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"A responding, blocking consumer" when {
|
||||
"activated with a custom error handler" must {
|
||||
"handle thrown exceptions by generating a custom response" in {
|
||||
service.awaitEndpointActivation(1) {
|
||||
actorOf[ErrorHandlingConsumer].start
|
||||
} must be (true)
|
||||
mandatoryTemplate.requestBody("direct:error-handler-test", "hello") must equal ("error: hello")
|
||||
|
||||
}
|
||||
}
|
||||
"activated with a custom redelivery handler" must {
|
||||
"handle thrown exceptions by redelivering the initial message" in {
|
||||
service.awaitEndpointActivation(1) {
|
||||
actorOf[RedeliveringConsumer].start
|
||||
} must be (true)
|
||||
mandatoryTemplate.requestBody("direct:redelivery-test", "hello") must equal ("accepted: hello")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ConsumerScalaTest {
|
||||
trait BlockingConsumer extends Consumer { self: Actor =>
|
||||
override def blocking = true
|
||||
}
|
||||
|
||||
class TestConsumer(uri: String) extends Actor with Consumer {
|
||||
def endpointUri = uri
|
||||
protected def receive = {
|
||||
case msg: Message => self.reply("received %s" format msg.body)
|
||||
}
|
||||
}
|
||||
|
||||
class TestBlocker(uri: String) extends Actor with BlockingConsumer {
|
||||
self.timeout = 1000
|
||||
def endpointUri = uri
|
||||
protected def receive = {
|
||||
case msg: Message => { /* do not reply */ }
|
||||
}
|
||||
}
|
||||
|
||||
class ErrorHandlingConsumer extends Actor with BlockingConsumer {
|
||||
def endpointUri = "direct:error-handler-test"
|
||||
|
||||
onRouteDefinition {rd: RouteDefinition =>
|
||||
rd.onException(classOf[Exception]).handled(true).transform(Builder.exceptionMessage).end
|
||||
}
|
||||
|
||||
protected def receive = {
|
||||
case msg: Message => throw new Exception("error: %s" format msg.body)
|
||||
}
|
||||
}
|
||||
|
||||
class RedeliveringConsumer extends Actor with BlockingConsumer {
|
||||
def endpointUri = "direct:redelivery-test"
|
||||
|
||||
onRouteDefinition {rd: RouteDefinition =>
|
||||
rd.onException(classOf[Exception]).maximumRedeliveries(1).end
|
||||
}
|
||||
|
||||
//
|
||||
// first message to this actor is not valid and will be rejected
|
||||
//
|
||||
|
||||
var valid = false
|
||||
|
||||
protected def receive = {
|
||||
case msg: Message => try {
|
||||
respondTo(msg)
|
||||
} finally {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
|
||||
private def respondTo(msg: Message) =
|
||||
if (valid) self.reply("accepted: %s" format msg.body)
|
||||
else throw new Exception("rejected: %s" format msg.body)
|
||||
|
||||
}
|
||||
|
||||
trait TestTypedConsumer {
|
||||
@consume("direct:publish-test-3")
|
||||
def foo(s: String): String
|
||||
def bar(s: String): String
|
||||
}
|
||||
|
||||
class TestTypedConsumerImpl extends TypedActor with TestTypedConsumer {
|
||||
def foo(s: String) = "foo: %s" format s
|
||||
@consume("direct:publish-test-4")
|
||||
def bar(s: String) = "bar: %s" format s
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class MessageJavaTest extends MessageJavaTestBase with JUnitSuite
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import java.io.InputStream
|
||||
|
||||
import org.apache.camel.NoTypeConversionAvailableException
|
||||
import org.junit.Assert._
|
||||
import org.junit.Test
|
||||
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
||||
class MessageScalaTest extends JUnitSuite with BeforeAndAfterAll {
|
||||
override protected def beforeAll = CamelContextManager.init
|
||||
|
||||
@Test def shouldConvertDoubleBodyToString = {
|
||||
assertEquals("1.4", Message(1.4).bodyAs[String])
|
||||
}
|
||||
|
||||
@Test def shouldThrowExceptionWhenConvertingDoubleBodyToInputStream {
|
||||
intercept[NoTypeConversionAvailableException] {
|
||||
Message(1.4).bodyAs[InputStream]
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldReturnDoubleHeader = {
|
||||
val message = Message("test" , Map("test" -> 1.4))
|
||||
assertEquals(1.4, message.header("test"))
|
||||
}
|
||||
|
||||
@Test def shouldConvertDoubleHeaderToString = {
|
||||
val message = Message("test" , Map("test" -> 1.4))
|
||||
assertEquals("1.4", message.headerAs[String]("test"))
|
||||
}
|
||||
|
||||
@Test def shouldReturnSubsetOfHeaders = {
|
||||
val message = Message("test" , Map("A" -> "1", "B" -> "2"))
|
||||
assertEquals(Map("B" -> "2"), message.headers(Set("B")))
|
||||
}
|
||||
|
||||
@Test def shouldTransformBodyAndPreserveHeaders = {
|
||||
assertEquals(
|
||||
Message("ab", Map("A" -> "1")),
|
||||
Message("a" , Map("A" -> "1")).transformBody((body: String) => body + "b"))
|
||||
}
|
||||
|
||||
@Test def shouldConvertBodyAndPreserveHeaders = {
|
||||
assertEquals(
|
||||
Message("1.4", Map("A" -> "1")),
|
||||
Message(1.4 , Map("A" -> "1")).setBodyAs[String])
|
||||
}
|
||||
|
||||
@Test def shouldSetBodyAndPreserveHeaders = {
|
||||
assertEquals(
|
||||
Message("test2" , Map("A" -> "1")),
|
||||
Message("test1" , Map("A" -> "1")).setBody("test2"))
|
||||
}
|
||||
|
||||
@Test def shouldSetHeadersAndPreserveBody = {
|
||||
assertEquals(
|
||||
Message("test1" , Map("C" -> "3")),
|
||||
Message("test1" , Map("A" -> "1")).setHeaders(Map("C" -> "3")))
|
||||
|
||||
}
|
||||
|
||||
@Test def shouldAddHeaderAndPreserveBodyAndHeaders = {
|
||||
assertEquals(
|
||||
Message("test1" , Map("A" -> "1", "B" -> "2")),
|
||||
Message("test1" , Map("A" -> "1")).addHeader("B" -> "2"))
|
||||
}
|
||||
|
||||
@Test def shouldAddHeadersAndPreserveBodyAndHeaders = {
|
||||
assertEquals(
|
||||
Message("test1" , Map("A" -> "1", "B" -> "2")),
|
||||
Message("test1" , Map("A" -> "1")).addHeaders(Map("B" -> "2")))
|
||||
}
|
||||
|
||||
@Test def shouldRemoveHeadersAndPreserveBodyAndRemainingHeaders = {
|
||||
assertEquals(
|
||||
Message("test1" , Map("A" -> "1")),
|
||||
Message("test1" , Map("A" -> "1", "B" -> "2")).removeHeader("B"))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,301 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import org.apache.camel.{Exchange, Processor}
|
||||
import org.apache.camel.builder.RouteBuilder
|
||||
import org.apache.camel.component.mock.MockEndpoint
|
||||
import org.scalatest.{GivenWhenThen, BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec}
|
||||
|
||||
import akka.actor.Actor._
|
||||
import akka.actor.{ActorRef, Actor, ActorRegistry}
|
||||
|
||||
class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach with GivenWhenThen {
|
||||
import ProducerFeatureTest._
|
||||
|
||||
override protected def beforeAll = {
|
||||
ActorRegistry.shutdownAll
|
||||
CamelContextManager.init
|
||||
CamelContextManager.mandatoryContext.addRoutes(new TestRoute)
|
||||
CamelContextManager.start
|
||||
}
|
||||
|
||||
override protected def afterAll = {
|
||||
CamelContextManager.stop
|
||||
ActorRegistry.shutdownAll
|
||||
}
|
||||
|
||||
override protected def afterEach = {
|
||||
mockEndpoint.reset
|
||||
}
|
||||
|
||||
feature("Produce a message to a sync Camel route") {
|
||||
|
||||
scenario("produce message and receive normal response") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-2", true))
|
||||
producer.start
|
||||
|
||||
when("a test message is sent to the producer with !!")
|
||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
||||
val result = producer !! message
|
||||
|
||||
then("a normal response should have been returned by the producer")
|
||||
val expected = Message("received TEST", Map(Message.MessageExchangeId -> "123"))
|
||||
assert(result === Some(expected))
|
||||
}
|
||||
|
||||
scenario("produce message and receive failure response") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-2"))
|
||||
producer.start
|
||||
|
||||
when("a test message causing an exception is sent to the producer with !!")
|
||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
||||
val result = (producer !! message).as[Failure]
|
||||
|
||||
then("a failure response should have been returned by the producer")
|
||||
val expectedFailureText = result.get.cause.getMessage
|
||||
val expectedHeaders = result.get.headers
|
||||
assert(expectedFailureText === "failure")
|
||||
assert(expectedHeaders === Map(Message.MessageExchangeId -> "123"))
|
||||
}
|
||||
|
||||
scenario("produce message oneway") {
|
||||
given("a registered one-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-1", true) with Oneway)
|
||||
producer.start
|
||||
|
||||
when("a test message is sent to the producer with !")
|
||||
mockEndpoint.expectedBodiesReceived("TEST")
|
||||
producer ! Message("test")
|
||||
|
||||
then("the test message should have been sent to mock:mock")
|
||||
mockEndpoint.assertIsSatisfied
|
||||
}
|
||||
|
||||
scenario("produce message twoway without sender reference") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-1"))
|
||||
producer.start
|
||||
|
||||
when("a test message is sent to the producer with !")
|
||||
mockEndpoint.expectedBodiesReceived("test")
|
||||
producer ! Message("test")
|
||||
|
||||
then("there should be only a warning that there's no sender reference")
|
||||
mockEndpoint.assertIsSatisfied
|
||||
}
|
||||
}
|
||||
|
||||
feature("Produce a message to an async Camel route") {
|
||||
|
||||
scenario("produce message and receive normal response") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-3"))
|
||||
producer.start
|
||||
|
||||
when("a test message is sent to the producer with !!")
|
||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
||||
val result = producer !! message
|
||||
|
||||
then("a normal response should have been returned by the producer")
|
||||
val expected = Message("received test", Map(Message.MessageExchangeId -> "123"))
|
||||
assert(result === Some(expected))
|
||||
}
|
||||
|
||||
scenario("produce message and receive failure response") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-3"))
|
||||
producer.start
|
||||
|
||||
when("a test message causing an exception is sent to the producer with !!")
|
||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
||||
val result = (producer !! message).as[Failure]
|
||||
|
||||
then("a failure response should have been returned by the producer")
|
||||
val expectedFailureText = result.get.cause.getMessage
|
||||
val expectedHeaders = result.get.headers
|
||||
assert(expectedFailureText === "failure")
|
||||
assert(expectedHeaders === Map(Message.MessageExchangeId -> "123"))
|
||||
}
|
||||
}
|
||||
|
||||
feature("Produce a message to a sync Camel route and then forward the response") {
|
||||
|
||||
scenario("produce message, forward normal response to a replying target actor and receive response") {
|
||||
given("a registered two-way producer configured with a forward target")
|
||||
val target = actorOf[ReplyingForwardTarget].start
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-2", target)).start
|
||||
|
||||
when("a test message is sent to the producer with !!")
|
||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
||||
val result = producer !! message
|
||||
|
||||
then("a normal response should have been returned by the forward target")
|
||||
val expected = Message("received test", Map(Message.MessageExchangeId -> "123", "test" -> "result"))
|
||||
assert(result === Some(expected))
|
||||
}
|
||||
|
||||
scenario("produce message, forward failure response to a replying target actor and receive response") {
|
||||
given("a registered two-way producer configured with a forward target")
|
||||
val target = actorOf[ReplyingForwardTarget].start
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-2", target)).start
|
||||
|
||||
when("a test message causing an exception is sent to the producer with !!")
|
||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
||||
val result = (producer !! message).as[Failure]
|
||||
|
||||
then("a failure response should have been returned by the forward target")
|
||||
val expectedFailureText = result.get.cause.getMessage
|
||||
val expectedHeaders = result.get.headers
|
||||
assert(expectedFailureText === "failure")
|
||||
assert(expectedHeaders === Map(Message.MessageExchangeId -> "123", "test" -> "failure"))
|
||||
}
|
||||
|
||||
scenario("produce message, forward normal response to a producing target actor and produce response to direct:forward-test-1") {
|
||||
given("a registered one-way producer configured with a forward target")
|
||||
val target = actorOf[ProducingForwardTarget].start
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-2", target)).start
|
||||
|
||||
when("a test message is sent to the producer with !")
|
||||
mockEndpoint.expectedBodiesReceived("received test")
|
||||
val result = producer.!(Message("test"))(Some(producer))
|
||||
|
||||
then("a normal response should have been produced by the forward target")
|
||||
mockEndpoint.assertIsSatisfied
|
||||
}
|
||||
|
||||
scenario("produce message, forward failure response to a producing target actor and produce response to direct:forward-test-1") {
|
||||
given("a registered one-way producer configured with a forward target")
|
||||
val target = actorOf[ProducingForwardTarget].start
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-2", target)).start
|
||||
|
||||
when("a test message causing an exception is sent to the producer with !")
|
||||
mockEndpoint.expectedMessageCount(1)
|
||||
mockEndpoint.message(0).body().isInstanceOf(classOf[Failure])
|
||||
val result = producer.!(Message("fail"))(Some(producer))
|
||||
|
||||
then("a failure response should have been produced by the forward target")
|
||||
mockEndpoint.assertIsSatisfied
|
||||
}
|
||||
}
|
||||
|
||||
feature("Produce a message to an async Camel route and then forward the response") {
|
||||
|
||||
scenario("produce message, forward normal response to a replying target actor and receive response") {
|
||||
given("a registered two-way producer configured with a forward target")
|
||||
val target = actorOf[ReplyingForwardTarget].start
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-3", target)).start
|
||||
|
||||
when("a test message is sent to the producer with !!")
|
||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
||||
val result = producer !! message
|
||||
|
||||
then("a normal response should have been returned by the forward target")
|
||||
val expected = Message("received test", Map(Message.MessageExchangeId -> "123", "test" -> "result"))
|
||||
assert(result === Some(expected))
|
||||
}
|
||||
|
||||
scenario("produce message, forward failure response to a replying target actor and receive response") {
|
||||
given("a registered two-way producer configured with a forward target")
|
||||
val target = actorOf[ReplyingForwardTarget].start
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-3", target)).start
|
||||
|
||||
when("a test message causing an exception is sent to the producer with !!")
|
||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
||||
val result = (producer !! message).as[Failure]
|
||||
|
||||
then("a failure response should have been returned by the forward target")
|
||||
val expectedFailureText = result.get.cause.getMessage
|
||||
val expectedHeaders = result.get.headers
|
||||
assert(expectedFailureText === "failure")
|
||||
assert(expectedHeaders === Map(Message.MessageExchangeId -> "123", "test" -> "failure"))
|
||||
}
|
||||
|
||||
scenario("produce message, forward normal response to a producing target actor and produce response to direct:forward-test-1") {
|
||||
given("a registered one-way producer configured with a forward target")
|
||||
val target = actorOf[ProducingForwardTarget].start
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-3", target)).start
|
||||
|
||||
when("a test message is sent to the producer with !")
|
||||
mockEndpoint.expectedBodiesReceived("received test")
|
||||
val result = producer.!(Message("test"))(Some(producer))
|
||||
|
||||
then("a normal response should have been produced by the forward target")
|
||||
mockEndpoint.assertIsSatisfied
|
||||
}
|
||||
|
||||
scenario("produce message, forward failure response to a producing target actor and produce response to direct:forward-test-1") {
|
||||
given("a registered one-way producer configured with a forward target")
|
||||
val target = actorOf[ProducingForwardTarget].start
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-3", target)).start
|
||||
|
||||
when("a test message causing an exception is sent to the producer with !")
|
||||
mockEndpoint.expectedMessageCount(1)
|
||||
mockEndpoint.message(0).body().isInstanceOf(classOf[Failure])
|
||||
val result = producer.!(Message("fail"))(Some(producer))
|
||||
|
||||
then("a failure response should have been produced by the forward target")
|
||||
mockEndpoint.assertIsSatisfied
|
||||
}
|
||||
}
|
||||
|
||||
private def mockEndpoint = CamelContextManager.mandatoryContext.getEndpoint("mock:mock", classOf[MockEndpoint])
|
||||
}
|
||||
|
||||
object ProducerFeatureTest {
|
||||
class TestProducer(uri: String, upper: Boolean = false) extends Actor with Producer {
|
||||
def endpointUri = uri
|
||||
override protected def receiveBeforeProduce = {
|
||||
case msg: Message => if (upper) msg.transformBody { body: String => body.toUpperCase } else msg
|
||||
}
|
||||
}
|
||||
|
||||
class TestForwarder(uri: String, target: ActorRef) extends Actor with Producer {
|
||||
def endpointUri = uri
|
||||
override protected def receiveAfterProduce = {
|
||||
case msg => target forward msg
|
||||
}
|
||||
}
|
||||
|
||||
class TestResponder extends Actor {
|
||||
protected def receive = {
|
||||
case msg: Message => msg.body match {
|
||||
case "fail" => self.reply(Failure(new Exception("failure"), msg.headers))
|
||||
case _ => self.reply(msg.transformBody { body: String => "received %s" format body })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReplyingForwardTarget extends Actor {
|
||||
protected def receive = {
|
||||
case msg: Message =>
|
||||
self.reply(msg.addHeader("test" -> "result"))
|
||||
case msg: Failure =>
|
||||
self.reply(Failure(msg.cause, msg.headers + ("test" -> "failure")))
|
||||
}
|
||||
}
|
||||
|
||||
class ProducingForwardTarget extends Actor with Producer with Oneway {
|
||||
def endpointUri = "direct:forward-test-1"
|
||||
}
|
||||
|
||||
class TestRoute extends RouteBuilder {
|
||||
val responder = actorOf[TestResponder].start
|
||||
def configure {
|
||||
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("actor:uuid:%s" format responder.uuid)
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import java.util.concurrent.{CountDownLatch, TimeUnit}
|
||||
|
||||
import org.junit.{Before, After, Test}
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
import akka.actor._
|
||||
import akka.actor.Actor._
|
||||
import akka.camel.support.{SetExpectedMessageCount => SetExpectedTestMessageCount, _}
|
||||
|
||||
class PublishRequestorTest extends JUnitSuite {
|
||||
import PublishRequestorTest._
|
||||
|
||||
var publisher: ActorRef = _
|
||||
var requestor: ActorRef = _
|
||||
var consumer: ActorRef = _
|
||||
|
||||
val ascendingMethodName = (r1: ConsumerMethodRegistered, r2: ConsumerMethodRegistered) =>
|
||||
r1.method.getName < r2.method.getName
|
||||
|
||||
@Before def setUp: Unit = {
|
||||
publisher = actorOf[PublisherMock].start
|
||||
requestor = actorOf[PublishRequestor].start
|
||||
requestor ! PublishRequestorInit(publisher)
|
||||
consumer = actorOf(new Actor with Consumer {
|
||||
def endpointUri = "mock:test"
|
||||
protected def receive = null
|
||||
}).start
|
||||
}
|
||||
|
||||
@After def tearDown = {
|
||||
AspectInitRegistry.removeListener(requestor);
|
||||
ActorRegistry.shutdownAll
|
||||
}
|
||||
|
||||
@Test def shouldReceiveOneConsumerMethodRegisteredEvent = {
|
||||
AspectInitRegistry.addListener(requestor)
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
val obj = TypedActor.newInstance(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl])
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val event = (publisher !! GetRetainedMessage).as[ConsumerMethodRegistered].get
|
||||
assert(event.endpointUri === "direct:foo")
|
||||
assert(event.typedActor === obj)
|
||||
assert(event.methodName === "foo")
|
||||
}
|
||||
|
||||
@Test def shouldReceiveOneConsumerMethodUnregisteredEvent = {
|
||||
val obj = TypedActor.newInstance(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl])
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
AspectInitRegistry.addListener(requestor)
|
||||
TypedActor.stop(obj)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val event = (publisher !! GetRetainedMessage).as[ConsumerMethodUnregistered].get
|
||||
assert(event.endpointUri === "direct:foo")
|
||||
assert(event.typedActor === obj)
|
||||
assert(event.methodName === "foo")
|
||||
}
|
||||
|
||||
@Test def shouldReceiveThreeConsumerMethodRegisteredEvents = {
|
||||
AspectInitRegistry.addListener(requestor)
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(3)).as[CountDownLatch].get
|
||||
val obj = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl])
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val request = GetRetainedMessages(_.isInstanceOf[ConsumerMethodRegistered])
|
||||
val events = (publisher !! request).as[List[ConsumerMethodRegistered]].get
|
||||
assert(events.map(_.method.getName).sortWith(_ < _) === List("m2", "m3", "m4"))
|
||||
}
|
||||
|
||||
@Test def shouldReceiveThreeConsumerMethodUnregisteredEvents = {
|
||||
val obj = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl])
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(3)).as[CountDownLatch].get
|
||||
AspectInitRegistry.addListener(requestor)
|
||||
TypedActor.stop(obj)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val request = GetRetainedMessages(_.isInstanceOf[ConsumerMethodUnregistered])
|
||||
val events = (publisher !! request).as[List[ConsumerMethodUnregistered]].get
|
||||
assert(events.map(_.method.getName).sortWith(_ < _) === List("m2", "m3", "m4"))
|
||||
}
|
||||
|
||||
@Test def shouldReceiveOneConsumerRegisteredEvent = {
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
requestor ! ActorRegistered(consumer)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
assert((publisher !! GetRetainedMessage) ===
|
||||
Some(ConsumerActorRegistered(consumer, consumer.actor.asInstanceOf[Consumer])))
|
||||
}
|
||||
|
||||
@Test def shouldReceiveOneConsumerUnregisteredEvent = {
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
requestor ! ActorUnregistered(consumer)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
assert((publisher !! GetRetainedMessage) ===
|
||||
Some(ConsumerActorUnregistered(consumer, consumer.actor.asInstanceOf[Consumer])))
|
||||
}
|
||||
}
|
||||
|
||||
object PublishRequestorTest {
|
||||
class PublisherMock extends TestActor with Retain with Countdown {
|
||||
def handler = retain andThen countdown
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import java.util.concurrent.{CountDownLatch, TimeUnit}
|
||||
|
||||
import org.scalatest.{GivenWhenThen, BeforeAndAfterAll, FeatureSpec}
|
||||
|
||||
import akka.actor._
|
||||
import akka.actor.Actor._
|
||||
import akka.remote.{RemoteClient, RemoteServer}
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWhenThen {
|
||||
import CamelServiceManager._
|
||||
import RemoteConsumerTest._
|
||||
|
||||
var server: RemoteServer = _
|
||||
|
||||
override protected def beforeAll = {
|
||||
ActorRegistry.shutdownAll
|
||||
|
||||
startCamelService
|
||||
|
||||
server = new RemoteServer()
|
||||
server.start(host, port)
|
||||
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
|
||||
override protected def afterAll = {
|
||||
server.shutdown
|
||||
|
||||
stopCamelService
|
||||
|
||||
RemoteClient.shutdownAll
|
||||
ActorRegistry.shutdownAll
|
||||
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
|
||||
feature("Publish consumer on remote node") {
|
||||
scenario("access published remote consumer") {
|
||||
given("a client-initiated remote consumer")
|
||||
val consumer = actorOf[RemoteConsumer].start
|
||||
|
||||
when("remote consumer publication is triggered")
|
||||
assert(mandatoryService.awaitEndpointActivation(1) {
|
||||
consumer !! "init"
|
||||
})
|
||||
|
||||
then("the published consumer is accessible via its endpoint URI")
|
||||
val response = CamelContextManager.mandatoryTemplate.requestBody("direct:remote-consumer", "test")
|
||||
assert(response === "remote actor: test")
|
||||
}
|
||||
}
|
||||
|
||||
feature("Publish typed consumer on remote node") {
|
||||
scenario("access published remote consumer method") {
|
||||
given("a client-initiated remote typed consumer")
|
||||
val consumer = TypedActor.newRemoteInstance(classOf[SampleRemoteTypedConsumer], classOf[SampleRemoteTypedConsumerImpl], host, port)
|
||||
|
||||
when("remote typed consumer publication is triggered")
|
||||
assert(mandatoryService.awaitEndpointActivation(1) {
|
||||
consumer.foo("init")
|
||||
})
|
||||
then("the published method is accessible via its endpoint URI")
|
||||
val response = CamelContextManager.mandatoryTemplate.requestBody("direct:remote-typed-consumer", "test")
|
||||
assert(response === "remote typed actor: test")
|
||||
}
|
||||
}
|
||||
|
||||
feature("Publish untyped consumer on remote node") {
|
||||
scenario("access published remote untyped consumer") {
|
||||
given("a client-initiated remote untyped consumer")
|
||||
val consumer = UntypedActor.actorOf(classOf[SampleRemoteUntypedConsumer]).start
|
||||
|
||||
when("remote untyped consumer publication is triggered")
|
||||
assert(mandatoryService.awaitEndpointActivation(1) {
|
||||
consumer.sendRequestReply(Message("init", Map("test" -> "init")))
|
||||
})
|
||||
then("the published untyped consumer is accessible via its endpoint URI")
|
||||
val response = CamelContextManager.mandatoryTemplate.requestBodyAndHeader("direct:remote-untyped-consumer", "a", "test", "b")
|
||||
assert(response === "a b")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object RemoteConsumerTest {
|
||||
val host = "localhost"
|
||||
val port = 7774
|
||||
|
||||
class RemoteConsumer extends RemoteActor(host, port) with Consumer {
|
||||
def endpointUri = "direct:remote-consumer"
|
||||
|
||||
protected def receive = {
|
||||
case "init" => self.reply("done")
|
||||
case m: Message => self.reply("remote actor: %s" format m.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
package akka.camel
|
||||
|
||||
import org.apache.camel.{Exchange, Processor}
|
||||
import org.apache.camel.builder.RouteBuilder
|
||||
import org.apache.camel.component.mock.MockEndpoint
|
||||
import org.scalatest.{GivenWhenThen, BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec}
|
||||
|
||||
import akka.actor.UntypedActor._
|
||||
import akka.actor.ActorRegistry
|
||||
|
||||
class UntypedProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach with GivenWhenThen {
|
||||
import UntypedProducerFeatureTest._
|
||||
|
||||
override protected def beforeAll = {
|
||||
ActorRegistry.shutdownAll
|
||||
CamelContextManager.init
|
||||
CamelContextManager.mandatoryContext.addRoutes(new TestRoute)
|
||||
CamelContextManager.start
|
||||
}
|
||||
|
||||
override protected def afterAll = {
|
||||
CamelContextManager.stop
|
||||
ActorRegistry.shutdownAll
|
||||
}
|
||||
|
||||
override protected def afterEach = {
|
||||
mockEndpoint.reset
|
||||
}
|
||||
|
||||
feature("Produce a message to a sync Camel route") {
|
||||
|
||||
scenario("produce message and receive normal response") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(classOf[SampleUntypedReplyingProducer])
|
||||
producer.start
|
||||
|
||||
when("a test message is sent to the producer with !!")
|
||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
||||
val result = producer.sendRequestReply(message)
|
||||
|
||||
then("a normal response should have been returned by the producer")
|
||||
val expected = Message("received test", Map(Message.MessageExchangeId -> "123"))
|
||||
assert(result === expected)
|
||||
}
|
||||
|
||||
scenario("produce message and receive failure response") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(classOf[SampleUntypedReplyingProducer])
|
||||
producer.start
|
||||
|
||||
when("a test message causing an exception is sent to the producer with !!")
|
||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
||||
val result = producer.sendRequestReply(message).asInstanceOf[Failure]
|
||||
|
||||
then("a failure response should have been returned by the producer")
|
||||
val expectedFailureText = result.cause.getMessage
|
||||
val expectedHeaders = result.headers
|
||||
assert(expectedFailureText === "failure")
|
||||
assert(expectedHeaders === Map(Message.MessageExchangeId -> "123"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
feature("Produce a message to a sync Camel route and then forward the response") {
|
||||
|
||||
scenario("produce message and send normal response to direct:forward-test-1") {
|
||||
given("a registered one-way producer configured with a forward target")
|
||||
val producer = actorOf(classOf[SampleUntypedForwardingProducer])
|
||||
producer.start
|
||||
|
||||
when("a test message is sent to the producer with !")
|
||||
mockEndpoint.expectedBodiesReceived("received test")
|
||||
val result = producer.sendOneWay(Message("test"), producer)
|
||||
|
||||
then("a normal response should have been sent")
|
||||
mockEndpoint.assertIsSatisfied
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private def mockEndpoint = CamelContextManager.mandatoryContext.getEndpoint("mock:mock", classOf[MockEndpoint])
|
||||
}
|
||||
|
||||
object UntypedProducerFeatureTest {
|
||||
class TestRoute extends RouteBuilder {
|
||||
def configure {
|
||||
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,130 +0,0 @@
|
|||
package akka.camel.component
|
||||
|
||||
import java.util.concurrent.{TimeUnit, CountDownLatch}
|
||||
|
||||
import org.apache.camel.RuntimeCamelException
|
||||
import org.apache.camel.builder.RouteBuilder
|
||||
import org.apache.camel.component.mock.MockEndpoint
|
||||
import org.scalatest.{BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec}
|
||||
|
||||
import akka.actor.Actor._
|
||||
import akka.actor.{ActorRegistry, Actor}
|
||||
import akka.camel.{Failure, Message, CamelContextManager}
|
||||
import akka.camel.support._
|
||||
|
||||
class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach {
|
||||
import ActorComponentFeatureTest._
|
||||
|
||||
override protected def beforeAll = {
|
||||
ActorRegistry.shutdownAll
|
||||
CamelContextManager.init
|
||||
CamelContextManager.mandatoryContext.addRoutes(new TestRoute)
|
||||
CamelContextManager.start
|
||||
}
|
||||
|
||||
override protected def afterAll = CamelContextManager.stop
|
||||
|
||||
override protected def afterEach = {
|
||||
ActorRegistry.shutdownAll
|
||||
mockEndpoint.reset
|
||||
}
|
||||
|
||||
feature("Communicate with an actor via an actor:uuid endpoint") {
|
||||
import CamelContextManager.mandatoryTemplate
|
||||
|
||||
scenario("one-way communication") {
|
||||
val actor = actorOf[Tester1].start
|
||||
val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
mandatoryTemplate.sendBody("actor:uuid:%s" format actor.uuid, "Martin")
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val reply = (actor !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
assert(reply.body === "Martin")
|
||||
}
|
||||
|
||||
scenario("two-way communication") {
|
||||
val actor = actorOf[Tester2].start
|
||||
assert(mandatoryTemplate.requestBody("actor:uuid:%s" format actor.uuid, "Martin") === "Hello Martin")
|
||||
}
|
||||
|
||||
scenario("two-way communication with timeout") {
|
||||
val actor = actorOf[Tester3].start
|
||||
intercept[RuntimeCamelException] {
|
||||
mandatoryTemplate.requestBody("actor:uuid:%s?blocking=true" format actor.uuid, "Martin")
|
||||
}
|
||||
}
|
||||
|
||||
scenario("two-way communication via a custom route with failure response") {
|
||||
mockEndpoint.expectedBodiesReceived("whatever")
|
||||
mandatoryTemplate.requestBody("direct:failure-test-1", "whatever")
|
||||
mockEndpoint.assertIsSatisfied
|
||||
}
|
||||
|
||||
scenario("two-way communication via a custom route with exception") {
|
||||
mockEndpoint.expectedBodiesReceived("whatever")
|
||||
mandatoryTemplate.requestBody("direct:failure-test-2", "whatever")
|
||||
mockEndpoint.assertIsSatisfied
|
||||
}
|
||||
}
|
||||
|
||||
feature("Communicate with an actor via an actor:id endpoint") {
|
||||
import CamelContextManager.mandatoryTemplate
|
||||
|
||||
scenario("one-way communication") {
|
||||
val actor = actorOf[Tester1].start
|
||||
val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
mandatoryTemplate.sendBody("actor:%s" format actor.id, "Martin")
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val reply = (actor !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
assert(reply.body === "Martin")
|
||||
}
|
||||
|
||||
scenario("two-way communication") {
|
||||
val actor = actorOf[Tester2].start
|
||||
assert(mandatoryTemplate.requestBody("actor:%s" format actor.id, "Martin") === "Hello Martin")
|
||||
}
|
||||
|
||||
scenario("two-way communication via a custom route") {
|
||||
val actor = actorOf[CustomIdActor].start
|
||||
assert(mandatoryTemplate.requestBody("direct:custom-id-test-1", "Martin") === "Received Martin")
|
||||
assert(mandatoryTemplate.requestBody("direct:custom-id-test-2", "Martin") === "Received Martin")
|
||||
}
|
||||
}
|
||||
|
||||
private def mockEndpoint = CamelContextManager.mandatoryContext.getEndpoint("mock:mock", classOf[MockEndpoint])
|
||||
}
|
||||
|
||||
object ActorComponentFeatureTest {
|
||||
class CustomIdActor extends Actor {
|
||||
self.id = "custom-id"
|
||||
protected def receive = {
|
||||
case msg: Message => self.reply("Received %s" format msg.body)
|
||||
}
|
||||
}
|
||||
|
||||
class FailWithMessage extends Actor {
|
||||
protected def receive = {
|
||||
case msg: Message => self.reply(Failure(new Exception("test")))
|
||||
}
|
||||
}
|
||||
|
||||
class FailWithException extends Actor {
|
||||
protected def receive = {
|
||||
case msg: Message => throw new Exception("test")
|
||||
}
|
||||
}
|
||||
|
||||
class TestRoute extends RouteBuilder {
|
||||
val failWithMessage = actorOf[FailWithMessage].start
|
||||
val failWithException = actorOf[FailWithException].start
|
||||
def configure {
|
||||
from("direct:custom-id-test-1").to("actor:custom-id")
|
||||
from("direct:custom-id-test-2").to("actor:id:custom-id")
|
||||
from("direct:failure-test-1")
|
||||
.onException(classOf[Exception]).to("mock:mock").handled(true).end
|
||||
.to("actor:uuid:%s" format failWithMessage.uuid)
|
||||
from("direct:failure-test-2")
|
||||
.onException(classOf[Exception]).to("mock:mock").handled(true).end
|
||||
.to("actor:uuid:%s?blocking=true" format failWithException.uuid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
package akka.camel.component
|
||||
|
||||
import org.apache.camel.{Endpoint, AsyncProcessor}
|
||||
import org.apache.camel.impl.DefaultCamelContext
|
||||
import org.junit._
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
import akka.actor.uuidFrom
|
||||
|
||||
class ActorComponentTest extends JUnitSuite {
|
||||
val component: ActorComponent = ActorComponentTest.actorComponent
|
||||
|
||||
def testUUID = "93da8c80-c3fd-11df-abed-60334b120057"
|
||||
|
||||
@Test def shouldCreateEndpointWithIdDefined = {
|
||||
val ep1: ActorEndpoint = component.createEndpoint("actor:abc").asInstanceOf[ActorEndpoint]
|
||||
val ep2: ActorEndpoint = component.createEndpoint("actor:id:abc").asInstanceOf[ActorEndpoint]
|
||||
assert(ep1.idValue === Some("abc"))
|
||||
assert(ep2.idValue === Some("abc"))
|
||||
assert(ep1.idType === "id")
|
||||
assert(ep2.idType === "id")
|
||||
assert(!ep1.blocking)
|
||||
assert(!ep2.blocking)
|
||||
}
|
||||
|
||||
@Test def shouldCreateEndpointWithIdTemplate = {
|
||||
val ep: ActorEndpoint = component.createEndpoint("actor:id:").asInstanceOf[ActorEndpoint]
|
||||
assert(ep.idValue === None)
|
||||
assert(ep.idType === "id")
|
||||
assert(!ep.blocking)
|
||||
}
|
||||
|
||||
@Test def shouldCreateEndpointWithIdTemplateAndBlockingSet = {
|
||||
val ep: ActorEndpoint = component.createEndpoint("actor:id:?blocking=true").asInstanceOf[ActorEndpoint]
|
||||
assert(ep.idValue === None)
|
||||
assert(ep.idType === "id")
|
||||
assert(ep.blocking)
|
||||
}
|
||||
|
||||
@Test def shouldCreateEndpointWithUuidDefined = {
|
||||
val ep: ActorEndpoint = component.createEndpoint("actor:uuid:%s" format testUUID).asInstanceOf[ActorEndpoint]
|
||||
assert(ep.idValue === Some(testUUID))
|
||||
assert(ep.idType === "uuid")
|
||||
assert(!ep.blocking)
|
||||
}
|
||||
|
||||
@Test def shouldCreateEndpointWithUuidTemplate = {
|
||||
val ep: ActorEndpoint = component.createEndpoint("actor:uuid:").asInstanceOf[ActorEndpoint]
|
||||
assert(ep.idValue === None)
|
||||
assert(ep.idType === "uuid")
|
||||
assert(!ep.blocking)
|
||||
}
|
||||
|
||||
@Test def shouldCreateEndpointWithUuidTemplateandBlockingSet = {
|
||||
val ep: ActorEndpoint = component.createEndpoint("actor:uuid:?blocking=true").asInstanceOf[ActorEndpoint]
|
||||
assert(ep.idValue === None)
|
||||
assert(ep.idType === "uuid")
|
||||
assert(ep.blocking)
|
||||
}
|
||||
|
||||
@Test def shouldCreateEndpointWithBlockingSet = {
|
||||
val ep: ActorEndpoint = component.createEndpoint("actor:uuid:%s?blocking=true" format testUUID).asInstanceOf[ActorEndpoint]
|
||||
assert(ep.idValue === Some(testUUID))
|
||||
assert(ep.idType === "uuid")
|
||||
assert(ep.blocking)
|
||||
}
|
||||
}
|
||||
|
||||
object ActorComponentTest {
|
||||
def actorComponent = {
|
||||
val component = new ActorComponent
|
||||
component.setCamelContext(new DefaultCamelContext)
|
||||
component
|
||||
}
|
||||
|
||||
def actorEndpoint(uri:String) = actorComponent.createEndpoint(uri)
|
||||
def actorProducer(endpoint: Endpoint) = endpoint.createProducer
|
||||
def actorAsyncProducer(endpoint: Endpoint) = endpoint.createProducer.asInstanceOf[AsyncProcessor]
|
||||
}
|
||||
|
|
@ -1,230 +0,0 @@
|
|||
package akka.camel.component
|
||||
|
||||
import ActorComponentTest._
|
||||
|
||||
import java.util.concurrent.{CountDownLatch, TimeoutException, TimeUnit}
|
||||
|
||||
import org.apache.camel.{AsyncCallback, ExchangePattern}
|
||||
|
||||
import org.junit.{After, Test}
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
|
||||
import akka.actor.Actor._
|
||||
import akka.actor.ActorRegistry
|
||||
import akka.camel.{Failure, Message}
|
||||
import akka.camel.support._
|
||||
|
||||
class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
||||
import ActorProducerTest._
|
||||
|
||||
@After def tearDown = ActorRegistry.shutdownAll
|
||||
|
||||
@Test def shouldSendMessageToActorWithSyncProcessor = {
|
||||
val actor = actorOf[Tester1].start
|
||||
val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:uuid:%s" format actor.uuid)
|
||||
val exchange = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
exchange.getIn.setBody("Martin")
|
||||
exchange.getIn.setHeader("k1", "v1")
|
||||
actorProducer(endpoint).process(exchange)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val reply = (actor !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
assert(reply.body === "Martin")
|
||||
assert(reply.headers === Map(Message.MessageExchangeId -> exchange.getExchangeId, "k1" -> "v1"))
|
||||
}
|
||||
|
||||
@Test def shouldSendMessageToActorWithAsyncProcessor = {
|
||||
val actor = actorOf[Tester1].start
|
||||
val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:uuid:%s" format actor.uuid)
|
||||
val exchange = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
exchange.getIn.setBody("Martin")
|
||||
exchange.getIn.setHeader("k1", "v1")
|
||||
actorAsyncProducer(endpoint).process(exchange, expectSyncCompletion)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val reply = (actor !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
assert(reply.body === "Martin")
|
||||
assert(reply.headers === Map(Message.MessageExchangeId -> exchange.getExchangeId, "k1" -> "v1"))
|
||||
}
|
||||
|
||||
@Test def shouldSendMessageToActorAndReceiveResponseWithSyncProcessor = {
|
||||
val actor = actorOf(new Tester2 {
|
||||
override def response(msg: Message) = Message(super.response(msg), Map("k2" -> "v2"))
|
||||
}).start
|
||||
val endpoint = actorEndpoint("actor:uuid:%s" format actor.uuid)
|
||||
val exchange = endpoint.createExchange(ExchangePattern.InOut)
|
||||
exchange.getIn.setBody("Martin")
|
||||
exchange.getIn.setHeader("k1", "v1")
|
||||
actorProducer(endpoint).process(exchange)
|
||||
assert(exchange.getOut.getBody === "Hello Martin")
|
||||
assert(exchange.getOut.getHeader("k2") === "v2")
|
||||
}
|
||||
|
||||
@Test def shouldSendMessageToActorAndReceiveResponseWithAsyncProcessor = {
|
||||
val actor = actorOf(new Tester2 {
|
||||
override def response(msg: Message) = Message(super.response(msg), Map("k2" -> "v2"))
|
||||
}).start
|
||||
val completion = expectAsyncCompletion
|
||||
val endpoint = actorEndpoint("actor:uuid:%s" format actor.uuid)
|
||||
val exchange = endpoint.createExchange(ExchangePattern.InOut)
|
||||
exchange.getIn.setBody("Martin")
|
||||
exchange.getIn.setHeader("k1", "v1")
|
||||
actorAsyncProducer(endpoint).process(exchange, completion)
|
||||
assert(completion.latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
assert(exchange.getOut.getBody === "Hello Martin")
|
||||
assert(exchange.getOut.getHeader("k2") === "v2")
|
||||
}
|
||||
|
||||
@Test def shouldSendMessageToActorAndReceiveFailureWithAsyncProcessor = {
|
||||
val actor = actorOf(new Tester2 {
|
||||
override def response(msg: Message) = Failure(new Exception("testmsg"), Map("k3" -> "v3"))
|
||||
}).start
|
||||
val completion = expectAsyncCompletion
|
||||
val endpoint = actorEndpoint("actor:uuid:%s" format actor.uuid)
|
||||
val exchange = endpoint.createExchange(ExchangePattern.InOut)
|
||||
exchange.getIn.setBody("Martin")
|
||||
exchange.getIn.setHeader("k1", "v1")
|
||||
actorAsyncProducer(endpoint).process(exchange, completion)
|
||||
assert(completion.latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
assert(exchange.getException.getMessage === "testmsg")
|
||||
assert(exchange.getOut.getBody === null)
|
||||
assert(exchange.getOut.getHeader("k3") === null) // headers from failure message are currently ignored
|
||||
}
|
||||
|
||||
@Test def shouldDynamicallyRouteMessageToActorWithDefaultId = {
|
||||
val actor1 = actorOf[Tester1]
|
||||
val actor2 = actorOf[Tester1]
|
||||
actor1.id = "x"
|
||||
actor2.id = "y"
|
||||
actor1.start
|
||||
actor2.start
|
||||
val latch1 = (actor1 !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val latch2 = (actor2 !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:id:%s" format actor1.id)
|
||||
val exchange1 = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
val exchange2 = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
exchange1.getIn.setBody("Test1")
|
||||
exchange2.getIn.setBody("Test2")
|
||||
exchange2.getIn.setHeader(ActorComponent.ActorIdentifier, actor2.id)
|
||||
actorProducer(endpoint).process(exchange1)
|
||||
actorProducer(endpoint).process(exchange2)
|
||||
assert(latch1.await(5, TimeUnit.SECONDS))
|
||||
assert(latch2.await(5, TimeUnit.SECONDS))
|
||||
val reply1 = (actor1 !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
val reply2 = (actor2 !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
assert(reply1.body === "Test1")
|
||||
assert(reply2.body === "Test2")
|
||||
}
|
||||
|
||||
@Test def shouldDynamicallyRouteMessageToActorWithoutDefaultId = {
|
||||
val actor1 = actorOf[Tester1]
|
||||
val actor2 = actorOf[Tester1]
|
||||
actor1.id = "x"
|
||||
actor2.id = "y"
|
||||
actor1.start
|
||||
actor2.start
|
||||
val latch1 = (actor1 !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val latch2 = (actor2 !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:id:")
|
||||
val exchange1 = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
val exchange2 = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
exchange1.getIn.setBody("Test1")
|
||||
exchange2.getIn.setBody("Test2")
|
||||
exchange1.getIn.setHeader(ActorComponent.ActorIdentifier, actor1.id)
|
||||
exchange2.getIn.setHeader(ActorComponent.ActorIdentifier, actor2.id)
|
||||
actorProducer(endpoint).process(exchange1)
|
||||
actorProducer(endpoint).process(exchange2)
|
||||
assert(latch1.await(5, TimeUnit.SECONDS))
|
||||
assert(latch2.await(5, TimeUnit.SECONDS))
|
||||
val reply1 = (actor1 !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
val reply2 = (actor2 !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
assert(reply1.body === "Test1")
|
||||
assert(reply2.body === "Test2")
|
||||
}
|
||||
|
||||
@Test def shouldDynamicallyRouteMessageToActorWithDefaultUuid = {
|
||||
val actor1 = actorOf[Tester1].start
|
||||
val actor2 = actorOf[Tester1].start
|
||||
val latch1 = (actor1 !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val latch2 = (actor2 !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:uuid:%s" format actor1.uuid)
|
||||
val exchange1 = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
val exchange2 = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
exchange1.getIn.setBody("Test1")
|
||||
exchange2.getIn.setBody("Test2")
|
||||
exchange2.getIn.setHeader(ActorComponent.ActorIdentifier, actor2.uuid.toString)
|
||||
actorProducer(endpoint).process(exchange1)
|
||||
actorProducer(endpoint).process(exchange2)
|
||||
assert(latch1.await(5, TimeUnit.SECONDS))
|
||||
assert(latch2.await(5, TimeUnit.SECONDS))
|
||||
val reply1 = (actor1 !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
val reply2 = (actor2 !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
assert(reply1.body === "Test1")
|
||||
assert(reply2.body === "Test2")
|
||||
}
|
||||
|
||||
@Test def shouldDynamicallyRouteMessageToActorWithoutDefaultUuid = {
|
||||
val actor1 = actorOf[Tester1].start
|
||||
val actor2 = actorOf[Tester1].start
|
||||
val latch1 = (actor1 !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val latch2 = (actor2 !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:uuid:")
|
||||
val exchange1 = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
val exchange2 = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
exchange1.getIn.setBody("Test1")
|
||||
exchange2.getIn.setBody("Test2")
|
||||
exchange1.getIn.setHeader(ActorComponent.ActorIdentifier, actor1.uuid)
|
||||
exchange2.getIn.setHeader(ActorComponent.ActorIdentifier, actor2.uuid.toString)
|
||||
actorProducer(endpoint).process(exchange1)
|
||||
actorProducer(endpoint).process(exchange2)
|
||||
assert(latch1.await(5, TimeUnit.SECONDS))
|
||||
assert(latch2.await(5, TimeUnit.SECONDS))
|
||||
val reply1 = (actor1 !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
val reply2 = (actor2 !! GetRetainedMessage).get.asInstanceOf[Message]
|
||||
assert(reply1.body === "Test1")
|
||||
assert(reply2.body === "Test2")
|
||||
}
|
||||
|
||||
@Test def shouldThrowExceptionWhenIdNotSet: Unit = {
|
||||
val actor = actorOf[Tester1].start
|
||||
val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:id:")
|
||||
intercept[ActorIdentifierNotSetException] {
|
||||
actorProducer(endpoint).process(endpoint.createExchange(ExchangePattern.InOnly))
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldThrowExceptionWhenUuidNotSet: Unit = {
|
||||
val actor = actorOf[Tester1].start
|
||||
val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:uuid:")
|
||||
intercept[ActorIdentifierNotSetException] {
|
||||
actorProducer(endpoint).process(endpoint.createExchange(ExchangePattern.InOnly))
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldSendMessageToActorAndTimeout(): Unit = {
|
||||
val actor = actorOf[Tester3].start
|
||||
val endpoint = actorEndpoint("actor:uuid:%s" format actor.uuid)
|
||||
val exchange = endpoint.createExchange(ExchangePattern.InOut)
|
||||
exchange.getIn.setBody("Martin")
|
||||
intercept[TimeoutException] {
|
||||
endpoint.createProducer.process(exchange)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ActorProducerTest {
|
||||
def expectSyncCompletion = new AsyncCallback {
|
||||
def done(doneSync: Boolean) = assert(doneSync)
|
||||
}
|
||||
|
||||
def expectAsyncCompletion = new AsyncCallback {
|
||||
val latch = new CountDownLatch(1);
|
||||
def done(doneSync: Boolean) = {
|
||||
assert(!doneSync)
|
||||
latch.countDown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
package akka.camel.component
|
||||
|
||||
import org.apache.camel._
|
||||
import org.apache.camel.builder.RouteBuilder
|
||||
import org.apache.camel.impl.{DefaultCamelContext, SimpleRegistry}
|
||||
import org.scalatest.{BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec}
|
||||
|
||||
import akka.actor.{ActorRegistry, TypedActor}
|
||||
import akka.camel._
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class TypedActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach {
|
||||
import TypedActorComponentFeatureTest._
|
||||
import CamelContextManager.mandatoryTemplate
|
||||
|
||||
override protected def beforeAll = {
|
||||
val typedActor = TypedActor.newInstance(classOf[SampleTypedActor], classOf[SampleTypedActorImpl]) // not a consumer
|
||||
val typedConsumer = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl])
|
||||
|
||||
val registry = new SimpleRegistry
|
||||
// external registration
|
||||
registry.put("ta", typedActor)
|
||||
|
||||
CamelContextManager.init(new DefaultCamelContext(registry))
|
||||
CamelContextManager.mandatoryContext.addRoutes(new CustomRouteBuilder)
|
||||
CamelContextManager.start
|
||||
|
||||
// Internal registration
|
||||
CamelContextManager.typedActorRegistry.put("tc", typedConsumer)
|
||||
}
|
||||
|
||||
override protected def afterAll = {
|
||||
CamelContextManager.stop
|
||||
ActorRegistry.shutdownAll
|
||||
}
|
||||
|
||||
feature("Communicate with an internally-registered typed actor using typed-actor-internal endpoint URIs") {
|
||||
import TypedActorComponent.InternalSchema
|
||||
import ExchangePattern._
|
||||
|
||||
scenario("two-way communication with method returning String") {
|
||||
val result1 = mandatoryTemplate.requestBodyAndHeader("%s:tc?method=m2" format InternalSchema, "x", "test", "y")
|
||||
val result2 = mandatoryTemplate.requestBodyAndHeader("%s:tc?method=m4" format InternalSchema, "x", "test", "y")
|
||||
assert(result1 === "m2: x y")
|
||||
assert(result2 === "m4: x y")
|
||||
}
|
||||
|
||||
scenario("two-way communication with method returning void") {
|
||||
val result = mandatoryTemplate.requestBodyAndHeader("%s:tc?method=m5" format InternalSchema, "x", "test", "y")
|
||||
assert(result === "x") // returns initial body
|
||||
}
|
||||
|
||||
scenario("one-way communication with method returning String") {
|
||||
val result = mandatoryTemplate.send("%s:tc?method=m2" format InternalSchema, InOnly, new Processor {
|
||||
def process(exchange: Exchange) = {
|
||||
exchange.getIn.setBody("x")
|
||||
exchange.getIn.setHeader("test", "y")
|
||||
}
|
||||
});
|
||||
assert(result.getPattern === InOnly)
|
||||
assert(result.getIn.getBody === "m2: x y")
|
||||
assert(result.getOut.getBody === null)
|
||||
}
|
||||
|
||||
scenario("one-way communication with method returning void") {
|
||||
val result = mandatoryTemplate.send("%s:tc?method=m5" format InternalSchema, InOnly, new Processor {
|
||||
def process(exchange: Exchange) = {
|
||||
exchange.getIn.setBody("x")
|
||||
exchange.getIn.setHeader("test", "y")
|
||||
}
|
||||
});
|
||||
assert(result.getPattern === InOnly)
|
||||
assert(result.getIn.getBody === "x")
|
||||
assert(result.getOut.getBody === null)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
feature("Communicate with an internally-registered typed actor using typed-actor endpoint URIs") {
|
||||
scenario("communication not possible") {
|
||||
intercept[ResolveEndpointFailedException] {
|
||||
mandatoryTemplate.requestBodyAndHeader("typed-actor:tc?method=m2", "x", "test", "y")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
feature("Communicate with an externally-registered typed actor using typed-actor endpoint URIs") {
|
||||
scenario("two-way communication with method returning String") {
|
||||
val result = mandatoryTemplate.requestBody("typed-actor:ta?method=foo", "test")
|
||||
assert(result === "foo: test")
|
||||
}
|
||||
|
||||
scenario("two-way communication with method returning String via custom route") {
|
||||
val result = mandatoryTemplate.requestBody("direct:test", "test")
|
||||
assert(result === "foo: test")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object TypedActorComponentFeatureTest {
|
||||
class CustomRouteBuilder extends RouteBuilder {
|
||||
def configure = {
|
||||
from("direct:test").to("typed-actor:ta?method=foo")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
package akka.camel.support
|
||||
|
||||
import java.util.concurrent.{TimeUnit, CountDownLatch}
|
||||
|
||||
import collection.mutable.Buffer
|
||||
|
||||
import akka.camel.Message
|
||||
import akka.actor.Actor
|
||||
|
||||
import TestSupport._
|
||||
|
||||
object TestSupport {
|
||||
type Handler = PartialFunction[Any, Any]
|
||||
}
|
||||
|
||||
trait TestActor extends Actor {
|
||||
def receive = {
|
||||
case msg => {
|
||||
handler(msg)
|
||||
}
|
||||
}
|
||||
|
||||
def handler: Handler
|
||||
}
|
||||
|
||||
class Tester1 extends TestActor with Retain with Countdown {
|
||||
def handler = retain andThen countdown
|
||||
}
|
||||
|
||||
class Tester2 extends TestActor with Respond {
|
||||
def handler = respond
|
||||
}
|
||||
|
||||
class Tester3 extends TestActor with Noop {
|
||||
self.timeout = 1
|
||||
def handler = noop
|
||||
}
|
||||
|
||||
trait Countdown { this: Actor =>
|
||||
var latch: CountDownLatch = new CountDownLatch(0)
|
||||
def countdown: Handler = {
|
||||
case SetExpectedMessageCount(num) => {
|
||||
latch = new CountDownLatch(num)
|
||||
self.reply(latch)
|
||||
}
|
||||
case msg => latch.countDown
|
||||
}
|
||||
}
|
||||
|
||||
trait Respond { this: Actor =>
|
||||
def respond: Handler = {
|
||||
case msg: Message => self.reply(response(msg))
|
||||
}
|
||||
|
||||
def response(msg: Message): Any = "Hello %s" format msg.body
|
||||
}
|
||||
|
||||
trait Retain { this: Actor =>
|
||||
val messages = Buffer[Any]()
|
||||
|
||||
def retain: Handler = {
|
||||
case GetRetainedMessage => self.reply(messages.last)
|
||||
case GetRetainedMessages(p) => self.reply(messages.toList.filter(p))
|
||||
case msg => {
|
||||
messages += msg
|
||||
msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait Noop { this: Actor =>
|
||||
def noop: Handler = {
|
||||
case msg => msg
|
||||
}
|
||||
}
|
||||
|
||||
case class SetExpectedMessageCount(num: Int)
|
||||
case class GetRetainedMessage()
|
||||
case class GetRetainedMessages(p: Any => Boolean) {
|
||||
def this() = this(_ => true)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue