Processed review wip-camel pull request 344
This commit is contained in:
parent
4d6511c5c6
commit
f74616f828
56 changed files with 544 additions and 1992 deletions
|
|
@ -1,34 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (C) 2009-2010 Typesafe Inc. <http://www.typesafe.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
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.TypedActorComponent
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
package akka.camel
|
|
||||||
|
|
||||||
import org.apache.camel.CamelContext
|
|
||||||
|
|
||||||
import akka.actor.Actor._
|
|
||||||
import akka.actor._
|
|
||||||
import akka.camel.component.TypedActorComponent
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module that adds typed consumer actor support to akka-camel. It is automatically
|
|
||||||
* detected by CamelService if added to the classpath.
|
|
||||||
*
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
private[camel] object TypedCamel {
|
|
||||||
private var consumerPublisher: ActorRef = _
|
|
||||||
private var publishRequestor: ActorRef = _
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the <code>TypedActorComponent</code> to <code>context</code>.
|
|
||||||
*/
|
|
||||||
def onCamelContextInit(context: CamelContext) {
|
|
||||||
context.addComponent(TypedActorComponent.InternalSchema, new TypedActorComponent)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures a <code>TypedConsumerPublishRequestor</code> and a <code>TypedConsumerPublisher</code>
|
|
||||||
* and re-uses the <code>activationTracker</code> of <code>service</code>.
|
|
||||||
*/
|
|
||||||
def onCamelServiceStart(service: CamelService) {
|
|
||||||
consumerPublisher = new LocalActorRef(Props(new TypedConsumerPublisher(service.activationTracker)), Props.randomName, true)
|
|
||||||
publishRequestor = new LocalActorRef(Props(new TypedConsumerPublishRequestor), Props.randomName, true)
|
|
||||||
|
|
||||||
registerPublishRequestor
|
|
||||||
|
|
||||||
for (event ← PublishRequestor.pastActorRegisteredEvents) publishRequestor ! event
|
|
||||||
publishRequestor ! InitPublishRequestor(consumerPublisher)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the configured Configures <code>TypedConsumerPublishRequestor</code> and
|
|
||||||
* <code>TypedConsumerPublisher</code>.
|
|
||||||
*/
|
|
||||||
def onCamelServiceStop(service: CamelService) {
|
|
||||||
unregisterPublishRequestor
|
|
||||||
consumerPublisher.stop
|
|
||||||
}
|
|
||||||
|
|
||||||
private def registerPublishRequestor: Unit = registry.addListener(publishRequestor)
|
|
||||||
private def unregisterPublishRequestor: Unit = registry.removeListener(publishRequestor)
|
|
||||||
}
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
package akka.camel
|
|
||||||
|
|
||||||
import java.lang.reflect.Method
|
|
||||||
import java.lang.reflect.Proxy._
|
|
||||||
|
|
||||||
import akka.actor.{ LocalActorRef, TypedActor, ActorRef }
|
|
||||||
import akka.actor.TypedActor._
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
private[camel] object TypedConsumer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies a function <code>f</code> to <code>actorRef</code> if <code>actorRef</code>
|
|
||||||
* references a typed consumer actor. A valid reference to a typed consumer actor is a
|
|
||||||
* local actor reference with a target actor that implements <code>TypedActor</code> and
|
|
||||||
* has at least one of its methods annotated with <code>@consume</code> (on interface or
|
|
||||||
* implementation class). For each <code>@consume</code>-annotated method, <code>f</code>
|
|
||||||
* is called with the corresponding <code>method</code> instance and the return value is
|
|
||||||
* added to a list which is then returned by this method.
|
|
||||||
*/
|
|
||||||
def withTypedConsumer[T](actorRef: ActorRef, typedActor: Option[AnyRef])(f: (AnyRef, Method) ⇒ T): List[T] = {
|
|
||||||
typedActor match {
|
|
||||||
case None ⇒ Nil
|
|
||||||
case Some(tc) ⇒ {
|
|
||||||
withConsumeAnnotatedMethodsOnInterfaces(tc, f) ++
|
|
||||||
withConsumeAnnotatedMethodsonImplClass(tc, actorRef, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private implicit def class2ProxyClass(c: Class[_]) = new ProxyClass(c)
|
|
||||||
|
|
||||||
private def withConsumeAnnotatedMethodsOnInterfaces[T](tc: AnyRef, f: (AnyRef, Method) ⇒ T): List[T] = for {
|
|
||||||
i ← tc.getClass.allInterfaces
|
|
||||||
m ← i.getDeclaredMethods.toList
|
|
||||||
if (m.isAnnotationPresent(classOf[consume]))
|
|
||||||
} yield f(tc, m)
|
|
||||||
|
|
||||||
private def withConsumeAnnotatedMethodsonImplClass[T](tc: AnyRef, actorRef: ActorRef, f: (AnyRef, Method) ⇒ T): List[T] = actorRef match {
|
|
||||||
case l: LocalActorRef ⇒
|
|
||||||
val implClass = l.underlyingActorInstance.asInstanceOf[TypedActor.TypedActor[AnyRef, AnyRef]].me.getClass
|
|
||||||
for (m ← implClass.getDeclaredMethods.toList; if (m.isAnnotationPresent(classOf[consume]))) yield f(tc, m)
|
|
||||||
case _ ⇒ Nil
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ProxyClass(c: Class[_]) {
|
|
||||||
def allInterfaces: List[Class[_]] = allInterfaces(c.getInterfaces.toList)
|
|
||||||
def allInterfaces(is: List[Class[_]]): List[Class[_]] = is match {
|
|
||||||
case Nil ⇒ Nil
|
|
||||||
case x :: xs ⇒ x :: allInterfaces(x.getInterfaces.toList) ::: allInterfaces(xs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
package akka.camel
|
|
||||||
|
|
||||||
import java.lang.reflect.Method
|
|
||||||
|
|
||||||
import akka.actor._
|
|
||||||
import akka.camel.component.TypedActorComponent
|
|
||||||
import akka.event.EventHandler
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Concrete publish requestor that requests publication of typed consumer actor methods on
|
|
||||||
* <code>TypedActorRegistered</code> events and unpublication of typed consumer actor methods on
|
|
||||||
* <code>TypedActorUnregistered</code> events.
|
|
||||||
*
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
private[camel] class TypedConsumerPublishRequestor extends PublishRequestor {
|
|
||||||
def receiveActorRegistryEvent = {
|
|
||||||
case TypedActorRegistered(_, actor, typedActor) ⇒ for (event ← ConsumerMethodRegistered.eventsFor(actor, Option(typedActor))) deliverCurrentEvent(event)
|
|
||||||
case TypedActorUnregistered(_, actor, typedActor) ⇒ for (event ← ConsumerMethodUnregistered.eventsFor(actor, Option(typedActor))) deliverCurrentEvent(event)
|
|
||||||
case _ ⇒ ()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Publishes a typed consumer actor method on <code>ConsumerMethodRegistered</code> events and
|
|
||||||
* unpublishes a typed consumer actor method on <code>ConsumerMethodUnregistered</code> events.
|
|
||||||
* Publications are tracked by sending an <code>activationTracker</code> an <code>EndpointActivated</code>
|
|
||||||
* event, unpublications are tracked by sending an <code>EndpointActivated</code> event.
|
|
||||||
*
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
private[camel] class TypedConsumerPublisher(activationTracker: ActorRef) extends Actor {
|
|
||||||
import TypedConsumerPublisher._
|
|
||||||
|
|
||||||
def receive = {
|
|
||||||
case mr: ConsumerMethodRegistered ⇒ {
|
|
||||||
handleConsumerMethodRegistered(mr)
|
|
||||||
activationTracker ! EndpointActivated
|
|
||||||
}
|
|
||||||
case mu: ConsumerMethodUnregistered ⇒ {
|
|
||||||
handleConsumerMethodUnregistered(mu)
|
|
||||||
activationTracker ! EndpointDeactivated
|
|
||||||
}
|
|
||||||
case _ ⇒ { /* ignore */ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
private[camel] object TypedConsumerPublisher {
|
|
||||||
/**
|
|
||||||
* Creates a route to a typed actor method.
|
|
||||||
*/
|
|
||||||
def handleConsumerMethodRegistered(event: ConsumerMethodRegistered) {
|
|
||||||
CamelContextManager.mandatoryContext.addRoutes(new ConsumerMethodRouteBuilder(event))
|
|
||||||
EventHandler.info(this, "published method %s of %s at endpoint %s" format (event.methodName, event.typedActor, event.endpointUri))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the route to the already un-registered typed consumer actor method.
|
|
||||||
*/
|
|
||||||
def handleConsumerMethodUnregistered(event: ConsumerMethodUnregistered) {
|
|
||||||
CamelContextManager.mandatoryContext.stopRoute(event.methodUuid)
|
|
||||||
EventHandler.info(this, "unpublished method %s of %s from endpoint %s" format (event.methodName, event.typedActor, event.endpointUri))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builder of a route to a typed consumer 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 typed consumer method (un)registration event.
|
|
||||||
*/
|
|
||||||
private[camel] trait ConsumerMethodEvent extends ConsumerEvent {
|
|
||||||
val actorRef: ActorRef
|
|
||||||
val typedActor: AnyRef
|
|
||||||
val method: Method
|
|
||||||
|
|
||||||
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 typed consumer actor has been registered at the actor registry. For
|
|
||||||
* each <code>@consume</code> annotated typed actor method a separate event is created.
|
|
||||||
*/
|
|
||||||
private[camel] case class ConsumerMethodRegistered(actorRef: ActorRef, typedActor: AnyRef, method: Method) extends ConsumerMethodEvent
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event indicating that a typed consumer actor has been unregistered from the actor registry. For
|
|
||||||
* each <code>@consume</code> annotated typed actor method a separate event is created.
|
|
||||||
*/
|
|
||||||
private[camel] case class ConsumerMethodUnregistered(actorRef: ActorRef, typedActor: AnyRef, method: Method) extends ConsumerMethodEvent
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
private[camel] object ConsumerMethodRegistered {
|
|
||||||
/**
|
|
||||||
* Creates a list of ConsumerMethodRegistered event messages for a typed consumer actor or an empty
|
|
||||||
* list if <code>actorRef</code> doesn't reference a typed consumer actor.
|
|
||||||
*/
|
|
||||||
def eventsFor(actorRef: ActorRef, typedActor: Option[AnyRef]): List[ConsumerMethodRegistered] = {
|
|
||||||
TypedConsumer.withTypedConsumer(actorRef, typedActor) { (tc, m) ⇒
|
|
||||||
ConsumerMethodRegistered(actorRef, tc, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
private[camel] object ConsumerMethodUnregistered {
|
|
||||||
/**
|
|
||||||
* Creates a list of ConsumerMethodUnregistered event messages for a typed consumer actor or an empty
|
|
||||||
* list if <code>actorRef</code> doesn't reference a typed consumer actor.
|
|
||||||
*/
|
|
||||||
def eventsFor(actorRef: ActorRef, typedActor: Option[AnyRef]): List[ConsumerMethodUnregistered] = {
|
|
||||||
TypedConsumer.withTypedConsumer(actorRef, typedActor) { (tc, m) ⇒
|
|
||||||
ConsumerMethodUnregistered(actorRef, tc, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (C) 2009-2010 Typesafe Inc. <http://www.typesafe.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
package akka.camel.component
|
|
||||||
|
|
||||||
import java.util.Map
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
|
|
||||||
import org.apache.camel.CamelContext
|
|
||||||
import org.apache.camel.component.bean._
|
|
||||||
|
|
||||||
import akka.actor._
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 <code>Actor.registry</code> if the
|
|
||||||
* schema is <code>TypedActorComponent.InternalSchema</code>. If the schema
|
|
||||||
* name is <code>typed-actor</code> this component tries to obtain the typed
|
|
||||||
* actor 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>Actor.registry</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(uri, remaining))
|
|
||||||
setProperties(endpoint.getProcessor, parameters)
|
|
||||||
endpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
private def createBeanHolder(uri: String, beanName: String) =
|
|
||||||
new TypedActorHolder(uri, getCamelContext, beanName).createCacheHolder
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>org.apache.camel.component.bean.BeanHolder</code> implementation that uses
|
|
||||||
* <code>Actor.registry</code> for getting access to typed actors.
|
|
||||||
*
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
class TypedActorHolder(uri: String, context: CamelContext, name: String)
|
|
||||||
extends RegistryBean(context, name) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an <code>akka.camel.component.BeanInfo</code> instance.
|
|
||||||
*/
|
|
||||||
override def getBeanInfo: BeanInfo =
|
|
||||||
new BeanInfo(getContext, getBean.getClass, getParameterMappingStrategy)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtains a typed actor from <code>Actor.registry</code> if the schema is
|
|
||||||
* <code>TypedActorComponent.InternalSchema</code>. If the schema name is
|
|
||||||
* <code>typed-actor</code> this method obtains the typed actor from the
|
|
||||||
* CamelContext's registry.
|
|
||||||
*
|
|
||||||
* @return a typed actor or <code>null</code>.
|
|
||||||
*/
|
|
||||||
override def getBean: AnyRef = {
|
|
||||||
val internal = uri.startsWith(TypedActorComponent.InternalSchema)
|
|
||||||
if (internal) Actor.registry.local.typedActorFor(uuidFrom(getName)) getOrElse null else super.getBean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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,12 +0,0 @@
|
||||||
package akka.camel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
public class SampleErrorHandlingTypedConsumerImpl 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,12 +0,0 @@
|
||||||
package akka.camel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
public class SampleRemoteTypedConsumerImpl implements SampleRemoteTypedConsumer {
|
|
||||||
|
|
||||||
public String foo(String s) {
|
|
||||||
return String.format("remote typed actor: %s", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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 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,28 +0,0 @@
|
||||||
package akka.camel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
public class SampleTypedConsumerImpl 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,11 +0,0 @@
|
||||||
package akka.camel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
public class SampleTypedSingleConsumerImpl implements SampleTypedSingleConsumer {
|
|
||||||
|
|
||||||
public void foo(String b) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
package akka.camel;
|
|
||||||
|
|
||||||
import akka.actor.Actor;
|
|
||||||
import akka.actor.TypedActor;
|
|
||||||
import akka.actor.Props;
|
|
||||||
import akka.util.Timeout;
|
|
||||||
import akka.dispatch.Dispatchers;
|
|
||||||
import akka.japi.SideEffect;
|
|
||||||
import akka.util.FiniteDuration;
|
|
||||||
|
|
||||||
import org.junit.AfterClass;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static akka.actor.Actors.*;
|
|
||||||
import static akka.camel.CamelContextManager.*;
|
|
||||||
import static akka.camel.CamelServiceManager.*;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
public class TypedConsumerJavaTestBase {
|
|
||||||
|
|
||||||
private SampleErrorHandlingTypedConsumer consumer;
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void setUpBeforeClass() {
|
|
||||||
startCamelService();
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterClass
|
|
||||||
public static void tearDownAfterClass() {
|
|
||||||
stopCamelService();
|
|
||||||
registry().local().shutdownAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldHandleExceptionThrownByTypedActorAndGenerateCustomResponse() {
|
|
||||||
getMandatoryService().awaitEndpointActivation(1, new SideEffect() {
|
|
||||||
public void apply() {
|
|
||||||
consumer = TypedActor.typedActorOf(
|
|
||||||
SampleErrorHandlingTypedConsumer.class,
|
|
||||||
SampleErrorHandlingTypedConsumerImpl.class,
|
|
||||||
(new Props()).withTimeout(new Timeout(new FiniteDuration(5000, "millis"))));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
String result = getMandatoryTemplate().requestBody("direct:error-handler-test-java-typed", "hello", String.class);
|
|
||||||
assertEquals("error: hello", result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
package akka.camel
|
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
|
|
||||||
import collection.mutable.Buffer
|
|
||||||
|
|
||||||
import akka.actor.Actor
|
|
||||||
|
|
||||||
object TypedCamelTestSupport {
|
|
||||||
type Handler = PartialFunction[Any, Any]
|
|
||||||
|
|
||||||
trait TestActor extends Actor {
|
|
||||||
def receive = {
|
|
||||||
case msg ⇒ {
|
|
||||||
handler(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def handler: Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Countdown { this: Actor ⇒
|
|
||||||
var latch: CountDownLatch = new CountDownLatch(0)
|
|
||||||
def countdown: Handler = {
|
|
||||||
case SetExpectedMessageCount(num) ⇒ {
|
|
||||||
latch = new CountDownLatch(num)
|
|
||||||
sender ! latch
|
|
||||||
}
|
|
||||||
case msg ⇒ latch.countDown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Respond { this: Actor ⇒
|
|
||||||
def respond: Handler = {
|
|
||||||
case msg: Message ⇒ sender ! 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 ⇒ sender ! messages.last
|
|
||||||
case GetRetainedMessages(p) ⇒ sender ! messages.filter(p).toList
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package akka.camel
|
|
||||||
|
|
||||||
import org.scalatest.junit.JUnitSuite
|
|
||||||
|
|
||||||
class TypedConsumerJavaTest extends TypedConsumerJavaTestBase with JUnitSuite
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
package akka.camel
|
|
||||||
|
|
||||||
import java.util.concurrent.{ CountDownLatch, TimeUnit }
|
|
||||||
|
|
||||||
import org.junit.{ Before, After, Test }
|
|
||||||
import org.scalatest.junit.JUnitSuite
|
|
||||||
import akka.util.duration._
|
|
||||||
import akka.actor._
|
|
||||||
import akka.actor.Actor._
|
|
||||||
import akka.camel.TypedCamelTestSupport.{ SetExpectedMessageCount ⇒ SetExpectedTestMessageCount, _ }
|
|
||||||
import akka.dispatch.Await
|
|
||||||
|
|
||||||
class TypedConsumerPublishRequestorTest extends JUnitSuite {
|
|
||||||
import TypedConsumerPublishRequestorTest._
|
|
||||||
|
|
||||||
var publisher: ActorRef = _
|
|
||||||
var requestor: ActorRef = _
|
|
||||||
var consumer: ActorRef = _
|
|
||||||
|
|
||||||
val ascendingMethodName = (r1: ConsumerMethodRegistered, r2: ConsumerMethodRegistered) ⇒
|
|
||||||
r1.method.getName < r2.method.getName
|
|
||||||
|
|
||||||
@Before
|
|
||||||
def setUp{
|
|
||||||
publisher = actorOf(Props(new TypedConsumerPublisherMock)
|
|
||||||
requestor = actorOf(Props(new TypedConsumerPublishRequestor)
|
|
||||||
requestor ! InitPublishRequestor(publisher)
|
|
||||||
consumer = actorOf(Props(new Actor with Consumer {
|
|
||||||
def endpointUri = "mock:test"
|
|
||||||
protected def receive = null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
def tearDown = {
|
|
||||||
Actor.registry.removeListener(requestor);
|
|
||||||
Actor.registry.local.shutdownAll
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def shouldReceiveOneConsumerMethodRegisteredEvent = {
|
|
||||||
Actor.registry.addListener(requestor)
|
|
||||||
val latch = Await.result((publisher ? SetExpectedTestMessageCount(1)).mapTo[CountDownLatch], 3 seconds)
|
|
||||||
val obj = TypedActor.typedActorOf(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl], Props())
|
|
||||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
|
||||||
val event = Await.result((publisher ? GetRetainedMessage).mapTo[ConsumerMethodRegistered], 3 seconds)
|
|
||||||
assert(event.endpointUri === "direct:foo")
|
|
||||||
assert(event.typedActor === obj)
|
|
||||||
assert(event.methodName === "foo")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def shouldReceiveOneConsumerMethodUnregisteredEvent = {
|
|
||||||
val latch = Await.result((publisher ? SetExpectedTestMessageCount(1)).mapTo[CountDownLatch], 3 seconds)
|
|
||||||
Actor.registry.addListener(requestor)
|
|
||||||
|
|
||||||
val obj = TypedActor.typedActorOf(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl], Props())
|
|
||||||
|
|
||||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
|
||||||
|
|
||||||
val ignorableEvent = Await.result((publisher ? GetRetainedMessage).mapTo[ConsumerMethodRegistered], 3 seconds)
|
|
||||||
|
|
||||||
val latch2 = Await.result((publisher ? SetExpectedTestMessageCount(1)).mapTo[CountDownLatch], 3 seconds)
|
|
||||||
TypedActor.stop(obj)
|
|
||||||
|
|
||||||
assert(latch2.await(5000, TimeUnit.MILLISECONDS))
|
|
||||||
|
|
||||||
val event = Await.result((publisher ? GetRetainedMessage).mapTo[ConsumerMethodUnregistered], 3 seconds)
|
|
||||||
|
|
||||||
assert(event.endpointUri === "direct:foo")
|
|
||||||
assert(event.typedActor === obj)
|
|
||||||
assert(event.methodName === "foo")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def shouldReceiveThreeConsumerMethodRegisteredEvents = {
|
|
||||||
Actor.registry.addListener(requestor)
|
|
||||||
val latch = Await.result((publisher ? SetExpectedTestMessageCount(3)).mapTo[CountDownLatch], 3 seconds)
|
|
||||||
val obj = TypedActor.typedActorOf(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl], Props())
|
|
||||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
|
||||||
val request = GetRetainedMessages(_.isInstanceOf[ConsumerMethodRegistered])
|
|
||||||
val events = Await.result((publisher ? request).mapTo[List[ConsumerMethodRegistered]], 3 seconds)
|
|
||||||
assert(events.map(_.method.getName).sortWith(_ < _) === List("m2", "m3", "m4"))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def shouldReceiveThreeConsumerMethodUnregisteredEvents = {
|
|
||||||
val obj = TypedActor.typedActorOf(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl], Props())
|
|
||||||
val latch = Await.result((publisher ? SetExpectedTestMessageCount(3)).mapTo[CountDownLatch], 3 seconds)
|
|
||||||
Actor.registry.addListener(requestor)
|
|
||||||
TypedActor.stop(obj)
|
|
||||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
|
||||||
val request = GetRetainedMessages(_.isInstanceOf[ConsumerMethodUnregistered])
|
|
||||||
val events = Await.result((publisher ? request).mapTo[List[ConsumerMethodUnregistered]], 3 seconds)
|
|
||||||
assert(events.map(_.method.getName).sortWith(_ < _) === List("m2", "m3", "m4"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object TypedConsumerPublishRequestorTest {
|
|
||||||
class TypedConsumerPublisherMock extends TestActor with Retain with Countdown {
|
|
||||||
def handler = retain andThen countdown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
package akka.camel
|
|
||||||
|
|
||||||
import org.apache.camel.CamelExecutionException
|
|
||||||
|
|
||||||
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
|
|
||||||
import org.scalatest.matchers.MustMatchers
|
|
||||||
|
|
||||||
import akka.actor.Actor._
|
|
||||||
import akka.actor._
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
class TypedConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMatchers {
|
|
||||||
import CamelContextManager.mandatoryTemplate
|
|
||||||
import TypedConsumerScalaTest._
|
|
||||||
|
|
||||||
var service: CamelService = _
|
|
||||||
|
|
||||||
override protected def beforeAll = {
|
|
||||||
registry.local.shutdownAll
|
|
||||||
service = CamelServiceManager.startCamelService
|
|
||||||
}
|
|
||||||
|
|
||||||
override protected def afterAll = {
|
|
||||||
service.stop
|
|
||||||
registry.local.shutdownAll
|
|
||||||
}
|
|
||||||
|
|
||||||
"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.typedActorOf(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl], Props())
|
|
||||||
} 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.typedActorOf(classOf[TestTypedConsumer], classOf[TestTypedConsumerImpl], Props())
|
|
||||||
} 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object TypedConsumerScalaTest {
|
|
||||||
trait TestTypedConsumer {
|
|
||||||
@consume("direct:publish-test-3")
|
|
||||||
def foo(s: String): String
|
|
||||||
def bar(s: String): String
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestTypedConsumerImpl extends TestTypedConsumer {
|
|
||||||
def foo(s: String) = "foo: %s" format s
|
|
||||||
@consume("direct:publish-test-4")
|
|
||||||
def bar(s: String) = "bar: %s" format s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,113 +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.{ Actor, TypedActor, Props }
|
|
||||||
import akka.camel._
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Martin Krasser
|
|
||||||
*/
|
|
||||||
class TypedActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach {
|
|
||||||
import TypedActorComponentFeatureTest._
|
|
||||||
import CamelContextManager.mandatoryTemplate
|
|
||||||
|
|
||||||
var typedConsumerUuid: String = _
|
|
||||||
|
|
||||||
override protected def beforeAll = {
|
|
||||||
val typedActor = TypedActor.typedActorOf(
|
|
||||||
classOf[SampleTypedActor],
|
|
||||||
classOf[SampleTypedActorImpl], Props()) // not a consumer
|
|
||||||
val typedConsumer = TypedActor.typedActorOf(
|
|
||||||
classOf[SampleTypedConsumer],
|
|
||||||
classOf[SampleTypedConsumerImpl], Props())
|
|
||||||
|
|
||||||
typedConsumerUuid = TypedActor.getActorRefFor(typedConsumer).uuid.toString
|
|
||||||
|
|
||||||
val registry = new SimpleRegistry
|
|
||||||
// external registration
|
|
||||||
registry.put("ta", typedActor)
|
|
||||||
|
|
||||||
CamelContextManager.init(new DefaultCamelContext(registry))
|
|
||||||
CamelContextManager.mandatoryContext.addRoutes(new CustomRouteBuilder)
|
|
||||||
CamelContextManager.start
|
|
||||||
}
|
|
||||||
|
|
||||||
override protected def afterAll = {
|
|
||||||
CamelContextManager.stop
|
|
||||||
Actor.registry.local.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:%s?method=m2" format (InternalSchema, typedConsumerUuid), "x", "test", "y")
|
|
||||||
val result2 = mandatoryTemplate.requestBodyAndHeader("%s:%s?method=m4" format (InternalSchema, typedConsumerUuid), "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:%s?method=m5" format (InternalSchema, typedConsumerUuid), "x", "test", "y")
|
|
||||||
assert(result === "x") // returns initial body
|
|
||||||
}
|
|
||||||
|
|
||||||
scenario("one-way communication with method returning String") {
|
|
||||||
val result = mandatoryTemplate.send("%s:%s?method=m2" format (InternalSchema, typedConsumerUuid), 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:%s?method=m5" format (InternalSchema, typedConsumerUuid), 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:%s?method=m2" format typedConsumerUuid, "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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -11,15 +11,21 @@ import java.util.concurrent.TimeoutException
|
||||||
import akka.actor.{ ActorSystem, Props, ActorRef }
|
import akka.actor.{ ActorSystem, Props, ActorRef }
|
||||||
import akka.pattern._
|
import akka.pattern._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activation trait that can be used to wait on activation or de-activation of Camel endpoints.
|
||||||
|
* The Camel endpoints are activated asynchronously. This trait can signal when an endpoint is activated or de-activated.
|
||||||
|
*/
|
||||||
trait Activation {
|
trait Activation {
|
||||||
import akka.dispatch.Await
|
import akka.dispatch.Await
|
||||||
|
|
||||||
def system: ActorSystem
|
def system: ActorSystem
|
||||||
private[camel] val activationTracker = system.actorOf(Props[ActivationTracker])
|
|
||||||
|
private val activationTracker = system.actorOf(Props[ActivationTracker])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Awaits for endpoint to be activated. It blocks until the endpoint is registered in camel context or timeout expires.
|
* Awaits for endpoint to be activated. It blocks until the endpoint is registered in camel context or timeout expires.
|
||||||
*
|
* @param endpoint the endpoint to wait for to be activated
|
||||||
|
* @param timeout the timeout for the wait
|
||||||
* @throws akka.camel.ActivationTimeoutException if endpoint is not activated within timeout.
|
* @throws akka.camel.ActivationTimeoutException if endpoint is not activated within timeout.
|
||||||
*/
|
*/
|
||||||
def awaitActivation(endpoint: ActorRef, timeout: Duration): ActorRef = {
|
def awaitActivation(endpoint: ActorRef, timeout: Duration): ActorRef = {
|
||||||
|
|
@ -32,7 +38,8 @@ trait Activation {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Awaits for endpoint to be de-activated. It is blocking until endpoint is unregistered in camel context or timeout expires.
|
* Awaits for endpoint to be de-activated. It is blocking until endpoint is unregistered in camel context or timeout expires.
|
||||||
*
|
* @param endpoint the endpoint to wait for to be de-activated
|
||||||
|
* @param timeout the timeout for the wait
|
||||||
* @throws akka.camel.DeActivationTimeoutException if endpoint is not de-activated within timeout.
|
* @throws akka.camel.DeActivationTimeoutException if endpoint is not de-activated within timeout.
|
||||||
*/
|
*/
|
||||||
def awaitDeactivation(endpoint: ActorRef, timeout: Duration) {
|
def awaitDeactivation(endpoint: ActorRef, timeout: Duration) {
|
||||||
|
|
@ -44,7 +51,9 @@ trait Activation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to `awaitActivation` but returns future instead.
|
* Similar to `awaitActivation` but returns a future instead.
|
||||||
|
* @param endpoint the endpoint to be activated
|
||||||
|
* @param timeout the timeout for the Future
|
||||||
*/
|
*/
|
||||||
def activationFutureFor(endpoint: ActorRef, timeout: Duration): Future[ActorRef] = {
|
def activationFutureFor(endpoint: ActorRef, timeout: Duration): Future[ActorRef] = {
|
||||||
(activationTracker.ask(AwaitActivation(endpoint))(Timeout(timeout))).map[ActorRef] {
|
(activationTracker.ask(AwaitActivation(endpoint))(Timeout(timeout))).map[ActorRef] {
|
||||||
|
|
@ -54,20 +63,32 @@ trait Activation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to awaitDeactivation but returns future instead.
|
* Similar to awaitDeactivation but returns a future instead.
|
||||||
|
* @param endpoint the endpoint to be deactivated
|
||||||
|
* @param timeout the timeout of the Future
|
||||||
*/
|
*/
|
||||||
def deactivationFutureFor(endpoint: ActorRef, timeout: Duration): Future[Unit] = {
|
def deactivationFutureFor(endpoint: ActorRef, timeout: Duration): Future[Unit] = {
|
||||||
(activationTracker.ask(AwaitDeActivation(endpoint))(Timeout(timeout))).map[Unit] {
|
(activationTracker.ask(AwaitDeActivation(endpoint))(Timeout(timeout))).map[Unit] {
|
||||||
case EndpointDeActivated(_) ⇒ {}
|
case EndpointDeActivated(_) ⇒ ()
|
||||||
case EndpointFailedToDeActivate(_, cause) ⇒ throw cause
|
case EndpointFailedToDeActivate(_, cause) ⇒ throw cause
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception for when a timeout has occurred during deactivation of an endpoint
|
||||||
|
* @param endpoint the endpoint that could not be de-activated in time
|
||||||
|
* @param timeout the timeout
|
||||||
|
*/
|
||||||
class DeActivationTimeoutException(endpoint: ActorRef, timeout: Duration) extends TimeoutException {
|
class DeActivationTimeoutException(endpoint: ActorRef, timeout: Duration) extends TimeoutException {
|
||||||
override def getMessage = "Timed out after %s, while waiting for de-activation of %s" format (timeout, endpoint.path)
|
override def getMessage = "Timed out after %s, while waiting for de-activation of %s" format (timeout, endpoint.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception for when a timeout has occurred during the activation of an endpoint
|
||||||
|
* @param endpoint the endpoint that could not be activated in time
|
||||||
|
* @param timeout the timeout
|
||||||
|
*/
|
||||||
class ActivationTimeoutException(endpoint: ActorRef, timeout: Duration) extends TimeoutException {
|
class ActivationTimeoutException(endpoint: ActorRef, timeout: Duration) extends TimeoutException {
|
||||||
override def getMessage = "Timed out after %s, while waiting for activation of %s" format (timeout, endpoint.path)
|
override def getMessage = "Timed out after %s, while waiting for activation of %s" format (timeout, endpoint.path)
|
||||||
}
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import org.apache.camel.{ ProducerTemplate, CamelContext }
|
||||||
//TODO complete this doc
|
//TODO complete this doc
|
||||||
/**
|
/**
|
||||||
* Camel trait encapsulates the underlying camel machinery.
|
* Camel trait encapsulates the underlying camel machinery.
|
||||||
|
* '''Note:''' `CamelContext` and `ProducerTemplate` are stopped when the associated actor system is shut down.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
trait Camel extends ConsumerRegistry with ProducerRegistry with Extension with Activation {
|
trait Camel extends ConsumerRegistry with ProducerRegistry with Extension with Activation {
|
||||||
|
|
@ -30,30 +31,6 @@ trait Camel extends ConsumerRegistry with ProducerRegistry with Extension with A
|
||||||
*/
|
*/
|
||||||
def template: ProducerTemplate
|
def template: ProducerTemplate
|
||||||
|
|
||||||
/**
|
|
||||||
* Associated `ActorSystem`.
|
|
||||||
*
|
|
||||||
* <p>It can be used to start producers, consumers or any other actors which need to interact with camel,
|
|
||||||
* for example:
|
|
||||||
* {{{
|
|
||||||
* val system = ActorSystem("test")
|
|
||||||
* system.actorOf(Props[SysOutConsumer])
|
|
||||||
*
|
|
||||||
* class SysOutConsumer extends Consumer {
|
|
||||||
* def endpointUri = "file://data/input/CamelConsumer"
|
|
||||||
*
|
|
||||||
* protected def receive = {
|
|
||||||
* case msg: Message ⇒ {
|
|
||||||
* printf("Received '%s'\\n", msg.bodyAs[String])
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }}}
|
|
||||||
* '''Note:''' This actor system is responsible for stopping the underlying camel instance.
|
|
||||||
*
|
|
||||||
* @see [[akka.camel.CamelExtension]]
|
|
||||||
*/
|
|
||||||
def system: ActorSystem
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -75,10 +52,12 @@ object CamelExtension extends ExtensionId[Camel] with ExtensionIdProvider {
|
||||||
* Creates a new instance of Camel and makes sure it gets stopped when the actor system is shutdown.
|
* Creates a new instance of Camel and makes sure it gets stopped when the actor system is shutdown.
|
||||||
*/
|
*/
|
||||||
def createExtension(system: ExtendedActorSystem) = {
|
def createExtension(system: ExtendedActorSystem) = {
|
||||||
val camel = new DefaultCamel(system).start;
|
val camel = new DefaultCamel(system).start
|
||||||
system.registerOnTermination(camel.shutdown())
|
system.registerOnTermination(camel.shutdown())
|
||||||
camel
|
camel
|
||||||
}
|
}
|
||||||
|
|
||||||
def lookup() = CamelExtension
|
def lookup(): ExtensionId[Camel] = CamelExtension
|
||||||
|
|
||||||
|
override def get(system: ActorSystem): Camel = super.get(system)
|
||||||
}
|
}
|
||||||
|
|
@ -9,18 +9,18 @@ import java.util.{ Map ⇒ JMap, Set ⇒ JSet }
|
||||||
import scala.collection.JavaConversions._
|
import scala.collection.JavaConversions._
|
||||||
|
|
||||||
import akka.japi.{ Function ⇒ JFunction }
|
import akka.japi.{ Function ⇒ JFunction }
|
||||||
import org.apache.camel.{ CamelContext, Message ⇒ CamelMessage }
|
import org.apache.camel.{ CamelContext, Message ⇒ JCamelMessage }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An immutable representation of a Camel message.
|
* An immutable representation of a Camel message.
|
||||||
*
|
*
|
||||||
* @author Martin Krasser
|
* @author Martin Krasser
|
||||||
*/
|
*/
|
||||||
case class Message(body: Any, headers: Map[String, Any]) {
|
case class CamelMessage(body: Any, headers: Map[String, Any]) {
|
||||||
|
|
||||||
def this(body: Any, headers: JMap[String, Any]) = this(body, headers.toMap) //for Java
|
def this(body: Any, headers: JMap[String, Any]) = this(body, headers.toMap) //for Java
|
||||||
|
|
||||||
override def toString = "Message(%s, %s)" format (body, headers)
|
override def toString = "CamelMessage(%s, %s)" format (body, headers)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns those headers from this message whose name is contained in <code>names</code>.
|
* Returns those headers from this message whose name is contained in <code>names</code>.
|
||||||
|
|
@ -60,73 +60,71 @@ case class Message(body: Any, headers: Map[String, Any]) {
|
||||||
def getHeader(name: String): Any = headers(name)
|
def getHeader(name: String): Any = headers(name)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Message with a transformed body using a <code>transformer</code> function.
|
* Creates a CamelMessage with a transformed body using a <code>transformer</code> function.
|
||||||
*/
|
*/
|
||||||
def mapBody[A, B](transformer: A ⇒ B): Message = withBody(transformer(body.asInstanceOf[A]))
|
def mapBody[A, B](transformer: A ⇒ B): CamelMessage = withBody(transformer(body.asInstanceOf[A]))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Message with a transformed body using a <code>transformer</code> function.
|
* Creates a CamelMessage with a transformed body using a <code>transformer</code> function.
|
||||||
* <p>
|
* <p>
|
||||||
* Java API
|
* Java API
|
||||||
*/
|
*/
|
||||||
def mapBody[A, B](transformer: JFunction[A, B]): Message = withBody(transformer(body.asInstanceOf[A]))
|
def mapBody[A, B](transformer: JFunction[A, B]): CamelMessage = withBody(transformer(body.asInstanceOf[A]))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Message with a given <code>body</code>.
|
* Creates a CamelMessage with a given <code>body</code>.
|
||||||
*/
|
*/
|
||||||
def withBody(body: Any) = Message(body, this.headers)
|
def withBody(body: Any) = CamelMessage(body, this.headers)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Message with given <code>headers</code>.
|
* Creates a new CamelMessage with given <code>headers</code>.
|
||||||
*/
|
*/
|
||||||
def withHeaders[A](headers: Map[String, A]): Message = copy(this.body, headers)
|
def withHeaders[A](headers: Map[String, A]): CamelMessage = copy(this.body, headers)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Message with given <code>headers</code>. A copy of the headers map is made.
|
* Creates a new CamelMessage with given <code>headers</code>. A copy of the headers map is made.
|
||||||
* <p>
|
* <p>
|
||||||
* Java API
|
* Java API
|
||||||
*/
|
*/
|
||||||
def withHeaders[A](headers: JMap[String, A]): Message = withHeaders(headers.toMap)
|
def withHeaders[A](headers: JMap[String, A]): CamelMessage = withHeaders(headers.toMap)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Message with given <code>headers</code> added to the current headers.
|
* Creates a new CamelMessage with given <code>headers</code> added to the current headers.
|
||||||
*/
|
*/
|
||||||
def plusHeaders[A](headers: Map[String, A]): Message = copy(this.body, this.headers ++ headers)
|
def addHeaders[A](headers: Map[String, A]): CamelMessage = copy(this.body, this.headers ++ headers)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Message with given <code>headers</code> added to the current headers.
|
* Creates a new CamelMessage with given <code>headers</code> added to the current headers.
|
||||||
* A copy of the headers map is made.
|
* A copy of the headers map is made.
|
||||||
* <p>
|
* <p>
|
||||||
* Java API
|
* Java API
|
||||||
*/
|
*/
|
||||||
def plusHeaders[A](headers: JMap[String, A]): Message = plusHeaders(headers.toMap)
|
def addHeaders[A](headers: JMap[String, A]): CamelMessage = addHeaders(headers.toMap)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Message with the given <code>header</code> added to the current headers.
|
* Creates a new CamelMessage with the given <code>header</code> added to the current headers.
|
||||||
*/
|
*/
|
||||||
def plusHeader(header: (String, Any)): Message = copy(this.body, this.headers + header)
|
def addHeader(header: (String, Any)): CamelMessage = copy(this.body, this.headers + header)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Message with the given header, represented by <code>name</code> and
|
* Creates a new CamelMessage with the given header, represented by <code>name</code> and
|
||||||
* <code>value</code> added to the existing headers.
|
* <code>value</code> added to the existing headers.
|
||||||
* <p>
|
* <p>
|
||||||
* Java API
|
* Java API
|
||||||
*/
|
*/
|
||||||
def plusHeader(name: String, value: Any): Message = plusHeader((name, value))
|
def addHeader(name: String, value: Any): CamelMessage = addHeader((name, value))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Message where the header with given <code>headerName</code> is removed from
|
* Creates a new CamelMessage where the header with given <code>headerName</code> is removed from
|
||||||
* the existing headers.
|
* the existing headers.
|
||||||
*/
|
*/
|
||||||
def withoutHeader(headerName: String) = copy(this.body, this.headers - headerName)
|
def withoutHeader(headerName: String) = copy(this.body, this.headers - headerName)
|
||||||
|
|
||||||
def copyContentTo(to: CamelMessage) = {
|
def copyContentTo(to: JCamelMessage) = {
|
||||||
to.setBody(this.body)
|
to.setBody(this.body)
|
||||||
for ((name, value) ← this.headers) to.getHeaders.put(name, value.asInstanceOf[AnyRef])
|
for ((name, value) ← this.headers) to.getHeaders.put(name, value.asInstanceOf[AnyRef])
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class RichMessage(message: Message, camelContext: CamelContext) {
|
|
||||||
/**
|
/**
|
||||||
* Returns the body of the message converted to the type <code>T</code>. Conversion is done
|
* 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
|
* using Camel's type converter. The type converter is obtained from the CamelContext managed
|
||||||
|
|
@ -136,7 +134,7 @@ class RichMessage(message: Message, camelContext: CamelContext) {
|
||||||
* @see CamelContextManager.
|
* @see CamelContextManager.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
def bodyAs[T](implicit m: Manifest[T]): T = getBodyAs(m.erasure.asInstanceOf[Class[T]])
|
def bodyAs[T](implicit m: Manifest[T], camelContext: CamelContext): T = getBodyAs(m.erasure.asInstanceOf[Class[T]], camelContext)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the body of the message converted to the type as given by the <code>clazz</code>
|
* Returns the body of the message converted to the type as given by the <code>clazz</code>
|
||||||
|
|
@ -148,26 +146,26 @@ class RichMessage(message: Message, camelContext: CamelContext) {
|
||||||
*
|
*
|
||||||
* @see CamelContextManager.
|
* @see CamelContextManager.
|
||||||
*/
|
*/
|
||||||
def getBodyAs[T](clazz: Class[T]): T =
|
def getBodyAs[T](clazz: Class[T], camelContext: CamelContext): T =
|
||||||
camelContext.getTypeConverter.mandatoryConvertTo[T](clazz, message.body)
|
camelContext.getTypeConverter.mandatoryConvertTo[T](clazz, body)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Message with current <code>body</code> converted to type <code>T</code>.
|
* Creates a CamelMessage with current <code>body</code> converted to type <code>T</code>.
|
||||||
*/
|
*/
|
||||||
def withBodyAs[T](implicit m: Manifest[T]): Message = withBodyAs(m.erasure.asInstanceOf[Class[T]])
|
def withBodyAs[T](implicit m: Manifest[T], camelContext: CamelContext): CamelMessage = withBodyAs(m.erasure.asInstanceOf[Class[T]])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Message with current <code>body</code> converted to type <code>clazz</code>.
|
* Creates a CamelMessage with current <code>body</code> converted to type <code>clazz</code>.
|
||||||
* <p>
|
* <p>
|
||||||
* Java API
|
* Java API
|
||||||
*/
|
*/
|
||||||
def withBodyAs[T](clazz: Class[T]): Message = message.withBody(getBodyAs(clazz))
|
def withBodyAs[T](clazz: Class[T])(implicit camelContext: CamelContext): CamelMessage = withBody(getBodyAs(clazz, camelContext))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the header with given <code>name</code> converted to type <code>T</code>. Throws
|
* Returns the header with given <code>name</code> converted to type <code>T</code>. Throws
|
||||||
* <code>NoSuchElementException</code> if the header doesn't exist.
|
* <code>NoSuchElementException</code> if the header doesn't exist.
|
||||||
*/
|
*/
|
||||||
def headerAs[T](name: String)(implicit m: Manifest[T]): Option[T] = message.header(name).map(camelContext.getTypeConverter.mandatoryConvertTo[T](m.erasure.asInstanceOf[Class[T]], _))
|
def headerAs[T](name: String)(implicit m: Manifest[T], camelContext: CamelContext): Option[T] = header(name).map(camelContext.getTypeConverter.mandatoryConvertTo[T](m.erasure.asInstanceOf[Class[T]], _))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the header with given <code>name</code> converted to type as given by the <code>clazz</code>
|
* Returns the header with given <code>name</code> converted to type as given by the <code>clazz</code>
|
||||||
|
|
@ -175,19 +173,19 @@ class RichMessage(message: Message, camelContext: CamelContext) {
|
||||||
* <p>
|
* <p>
|
||||||
* Java API
|
* Java API
|
||||||
*/
|
*/
|
||||||
def getHeaderAs[T](name: String, clazz: Class[T]) = headerAs[T](name)(Manifest.classType(clazz)).get
|
def getHeaderAs[T](name: String, clazz: Class[T], camelContext: CamelContext) = headerAs[T](name)(Manifest.classType(clazz), camelContext).get
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Companion object of Message class.
|
* Companion object of CamelMessage class.
|
||||||
*
|
*
|
||||||
* @author Martin Krasser
|
* @author Martin Krasser
|
||||||
*/
|
*/
|
||||||
object Message {
|
object CamelMessage {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message header to correlate request with response messages. Applications that send
|
* CamelMessage header to correlate request with response messages. Applications that send
|
||||||
* messages to a Producer actor may want to set this header on the request message
|
* 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
|
* so that it can be correlated with an asynchronous response. Messages send to Consumer
|
||||||
* actors have this header already set.
|
* actors have this header already set.
|
||||||
|
|
@ -196,26 +194,26 @@ object Message {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a canonical form of the given message <code>msg</code>. If <code>msg</code> of type
|
* 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
|
* CamelMessage then <code>msg</code> is returned, otherwise <code>msg</code> is set as body of a
|
||||||
* newly created Message object.
|
* newly created CamelMessage object.
|
||||||
*/
|
*/
|
||||||
def canonicalize(msg: Any) = msg match {
|
def canonicalize(msg: Any) = msg match {
|
||||||
case mobj: Message ⇒ mobj
|
case mobj: CamelMessage ⇒ mobj
|
||||||
case body ⇒ Message(body, Map.empty)
|
case body ⇒ CamelMessage(body, Map.empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Message object from the Camel message.
|
* Creates a new CamelMessage object from the Camel message.
|
||||||
*/
|
*/
|
||||||
def from(camelMessage: CamelMessage): Message = from(camelMessage, Map.empty)
|
def from(camelMessage: JCamelMessage): CamelMessage = from(camelMessage, Map.empty)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Message object from the Camel message.
|
* Creates a new CamelMessage object from the Camel message.
|
||||||
*
|
*
|
||||||
* @param headers additional headers to set on the created Message in addition to those
|
* @param headers additional headers to set on the created CamelMessage in addition to those
|
||||||
* in the Camel message.
|
* in the Camel message.
|
||||||
*/
|
*/
|
||||||
def from(camelMessage: CamelMessage, headers: Map[String, Any]): Message = Message(camelMessage.getBody, headers ++ camelMessage.getHeaders)
|
def from(camelMessage: JCamelMessage, headers: Map[String, Any]): CamelMessage = CamelMessage(camelMessage.getBody, headers ++ camelMessage.getHeaders)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,16 +19,22 @@ import akka.util.duration._
|
||||||
trait Consumer extends Actor with ConsumerConfig {
|
trait Consumer extends Actor with ConsumerConfig {
|
||||||
|
|
||||||
def endpointUri: String
|
def endpointUri: String
|
||||||
protected[this] implicit lazy val camel = CamelExtension(context.system)
|
|
||||||
|
protected[this] implicit def camel = CamelExtension(context.system)
|
||||||
|
protected[this] implicit def camelContext = camel.context
|
||||||
|
|
||||||
camel.registerConsumer(endpointUri, this, activationTimeout)
|
camel.registerConsumer(endpointUri, this, activationTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For internal use only.
|
||||||
|
*/
|
||||||
|
private[camel] object DefaultConsumerConfig extends ConsumerConfig
|
||||||
|
|
||||||
trait ConsumerConfig {
|
trait ConsumerConfig {
|
||||||
//TODO: Explain the parameters better with some examples!
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How long should the actor wait for activation before it fails.
|
* How long the actor should wait for activation before it fails.
|
||||||
*/
|
*/
|
||||||
def activationTimeout: Duration = 10 seconds
|
def activationTimeout: Duration = 10 seconds
|
||||||
|
|
||||||
|
|
@ -50,18 +56,12 @@ trait ConsumerConfig {
|
||||||
/**
|
/**
|
||||||
* The route definition handler for creating a custom route to this consumer instance.
|
* The route definition handler for creating a custom route to this consumer instance.
|
||||||
*/
|
*/
|
||||||
//TODO: write a test confirming onRouteDefinition gets called
|
//FIXME: write a test confirming onRouteDefinition gets called
|
||||||
def onRouteDefinition(rd: RouteDefinition): ProcessorDefinition[_] = rd
|
def onRouteDefinition(rd: RouteDefinition): ProcessorDefinition[_] = rd
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For internal use only. Converts this ConsumerConfig to camel URI parameters
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
private[camel] def toCamelParameters: String = "autoack=%s&replyTimeout=%s" format (autoack, DurationTypeConverter.toString(replyTimeout))
|
private[camel] def toCamelParameters: String = "autoack=%s&replyTimeout=%s" format (autoack, DurationTypeConverter.toString(replyTimeout))
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ManualAckConsumer extends Consumer {
|
|
||||||
override def autoack = false
|
|
||||||
}
|
|
||||||
|
|
||||||
trait ErrorPassing { self: Actor ⇒
|
|
||||||
final override def preRestart(reason: Throwable, message: Option[Any]) {
|
|
||||||
sender ! Failure(reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -14,14 +14,14 @@ import org.apache.camel.{ Exchange, ExchangePattern, AsyncCallback }
|
||||||
* @author Martin Krasser
|
* @author Martin Krasser
|
||||||
*/
|
*/
|
||||||
trait ProducerSupport { this: Actor ⇒
|
trait ProducerSupport { this: Actor ⇒
|
||||||
protected[this] implicit lazy val camel = CamelExtension(context.system)
|
protected[this] implicit def camel = CamelExtension(context.system)
|
||||||
|
|
||||||
protected[this] lazy val (endpoint, processor) = camel.registerProducer(self, endpointUri)
|
protected[this] lazy val (endpoint, processor) = camel.registerProducer(self, endpointUri)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message headers to copy by default from request message to response-message.
|
* CamelMessage headers to copy by default from request message to response-message.
|
||||||
*/
|
*/
|
||||||
private val headersToCopyDefault = Set(Message.MessageExchangeId)
|
private val headersToCopyDefault = Set(CamelMessage.MessageExchangeId)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to false (default), this producer expects a response message from the Camel endpoint.
|
* If set to false (default), this producer expects a response message from the Camel endpoint.
|
||||||
|
|
@ -37,7 +37,7 @@ trait ProducerSupport { this: Actor ⇒
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the names of message headers to copy from a request message to a response message.
|
* 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
|
* By default only the CamelMessage.MessageExchangeId is copied. Applications may override this to
|
||||||
* define an application-specific set of message headers to copy.
|
* define an application-specific set of message headers to copy.
|
||||||
*/
|
*/
|
||||||
def headersToCopy: Set[String] = headersToCopyDefault
|
def headersToCopy: Set[String] = headersToCopyDefault
|
||||||
|
|
@ -52,7 +52,7 @@ trait ProducerSupport { this: Actor ⇒
|
||||||
* asynchronously. The original
|
* asynchronously. The original
|
||||||
* sender and senderFuture are preserved.
|
* sender and senderFuture are preserved.
|
||||||
*
|
*
|
||||||
* @see Message#canonicalize(Any)
|
* @see CamelMessage#canonicalize(Any)
|
||||||
*
|
*
|
||||||
* @param msg message to produce
|
* @param msg message to produce
|
||||||
* @param pattern exchange pattern
|
* @param pattern exchange pattern
|
||||||
|
|
@ -60,7 +60,7 @@ trait ProducerSupport { this: Actor ⇒
|
||||||
protected def produce(msg: Any, pattern: ExchangePattern): Unit = {
|
protected def produce(msg: Any, pattern: ExchangePattern): Unit = {
|
||||||
implicit def toExchangeAdapter(exchange: Exchange): CamelExchangeAdapter = new CamelExchangeAdapter(exchange)
|
implicit def toExchangeAdapter(exchange: Exchange): CamelExchangeAdapter = new CamelExchangeAdapter(exchange)
|
||||||
|
|
||||||
val cmsg = Message.canonicalize(msg)
|
val cmsg = CamelMessage.canonicalize(msg)
|
||||||
val exchange = endpoint.createExchange(pattern)
|
val exchange = endpoint.createExchange(pattern)
|
||||||
exchange.setRequest(cmsg)
|
exchange.setRequest(cmsg)
|
||||||
processor.process(exchange, new AsyncCallback {
|
processor.process(exchange, new AsyncCallback {
|
||||||
|
|
@ -68,15 +68,10 @@ trait ProducerSupport { this: Actor ⇒
|
||||||
// Need copies of sender reference here since the callback could be done
|
// Need copies of sender reference here since the callback could be done
|
||||||
// later by another thread.
|
// later by another thread.
|
||||||
val originalSender = sender
|
val originalSender = sender
|
||||||
|
// Ignoring doneSync, sending back async uniformly.
|
||||||
def done(doneSync: Boolean): Unit = {
|
def done(doneSync: Boolean): Unit = producer.tell(
|
||||||
if (exchange.isFailed) {
|
if (exchange.isFailed) FailureResult(exchange.toFailureMessage(cmsg.headers(headersToCopy)))
|
||||||
dispatch(FailureResult(exchange.toFailureMessage(cmsg.headers(headersToCopy))))
|
else MessageResult(exchange.toResponseMessage(cmsg.headers(headersToCopy))), originalSender)
|
||||||
} else {
|
|
||||||
dispatch(MessageResult(exchange.toResponseMessage(cmsg.headers(headersToCopy))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private def dispatch(result: Any) { producer.tell(result, originalSender) }
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,12 +130,12 @@ trait Producer extends ProducerSupport { this: Actor ⇒
|
||||||
/**
|
/**
|
||||||
* @author Martin Krasser
|
* @author Martin Krasser
|
||||||
*/
|
*/
|
||||||
private[camel] case class MessageResult(message: Message)
|
private case class MessageResult(message: CamelMessage)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Martin Krasser
|
* @author Martin Krasser
|
||||||
*/
|
*/
|
||||||
private[camel] case class FailureResult(failure: Failure)
|
private case class FailureResult(failure: Failure)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A one-way producer.
|
* A one-way producer.
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,24 @@ package akka.camel.internal
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import collection.mutable.WeakHashMap
|
import collection.mutable.WeakHashMap
|
||||||
|
|
||||||
class ActivationTracker extends Actor with ActorLogging {
|
/**
|
||||||
|
* For internal use only. Tracks activation and de-activation of endpoints.
|
||||||
|
*/
|
||||||
|
private[akka] final class ActivationTracker extends Actor with ActorLogging {
|
||||||
val activations = new WeakHashMap[ActorRef, ActivationStateMachine]
|
val activations = new WeakHashMap[ActorRef, ActivationStateMachine]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A state machine that keeps track of the endpoint activation status of an actor.
|
||||||
|
*/
|
||||||
class ActivationStateMachine {
|
class ActivationStateMachine {
|
||||||
type State = PartialFunction[ActivationMessage, Unit]
|
type State = PartialFunction[ActivationMessage, Unit]
|
||||||
|
|
||||||
var receive: State = notActivated()
|
var receive: State = notActivated()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not activated state
|
||||||
|
* @return a partial function that handles messages in the 'not activated' state
|
||||||
|
*/
|
||||||
def notActivated(): State = {
|
def notActivated(): State = {
|
||||||
var awaitingActivation = List[ActorRef]()
|
var awaitingActivation = List[ActorRef]()
|
||||||
var awaitingDeActivation = List[ActorRef]()
|
var awaitingDeActivation = List[ActorRef]()
|
||||||
|
|
@ -22,46 +32,59 @@ class ActivationTracker extends Actor with ActorLogging {
|
||||||
{
|
{
|
||||||
case AwaitActivation(ref) ⇒ awaitingActivation ::= sender
|
case AwaitActivation(ref) ⇒ awaitingActivation ::= sender
|
||||||
case AwaitDeActivation(ref) ⇒ awaitingDeActivation ::= sender
|
case AwaitDeActivation(ref) ⇒ awaitingDeActivation ::= sender
|
||||||
|
case msg @ EndpointActivated(ref) ⇒
|
||||||
case msg @ EndpointActivated(ref) ⇒ {
|
|
||||||
awaitingActivation.foreach(_ ! msg)
|
awaitingActivation.foreach(_ ! msg)
|
||||||
receive = activated(awaitingDeActivation)
|
receive = activated(awaitingDeActivation)
|
||||||
}
|
case EndpointFailedToActivate(ref, cause) ⇒
|
||||||
|
|
||||||
case EndpointFailedToActivate(ref, cause) ⇒ {
|
|
||||||
awaitingActivation.foreach(_ ! EndpointFailedToActivate(ref, cause))
|
awaitingActivation.foreach(_ ! EndpointFailedToActivate(ref, cause))
|
||||||
receive = failedToActivate(cause)
|
receive = failedToActivate(cause)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activated state.
|
||||||
|
* @param currentAwaitingDeActivation the current <code>ActorRef</code>s awaiting de-activation
|
||||||
|
* @return a partial function that handles messages in the 'activated' state
|
||||||
|
*/
|
||||||
def activated(currentAwaitingDeActivation: List[ActorRef]): State = {
|
def activated(currentAwaitingDeActivation: List[ActorRef]): State = {
|
||||||
var awaitingDeActivation = currentAwaitingDeActivation
|
var awaitingDeActivation = currentAwaitingDeActivation
|
||||||
|
|
||||||
{
|
{
|
||||||
case AwaitActivation(ref) ⇒ sender ! EndpointActivated(ref)
|
case AwaitActivation(ref) ⇒ sender ! EndpointActivated(ref)
|
||||||
case AwaitDeActivation(ref) ⇒ awaitingDeActivation ::= sender
|
case AwaitDeActivation(ref) ⇒ awaitingDeActivation ::= sender
|
||||||
case msg @ EndpointDeActivated(ref) ⇒ {
|
case msg @ EndpointDeActivated(ref) ⇒
|
||||||
awaitingDeActivation foreach (_ ! msg)
|
awaitingDeActivation foreach (_ ! msg)
|
||||||
receive = deactivated
|
receive = deactivated
|
||||||
}
|
case msg @ EndpointFailedToDeActivate(ref, cause) ⇒
|
||||||
case msg @ EndpointFailedToDeActivate(ref, cause) ⇒ {
|
|
||||||
awaitingDeActivation foreach (_ ! msg)
|
awaitingDeActivation foreach (_ ! msg)
|
||||||
receive = failedToDeActivate(cause)
|
receive = failedToDeActivate(cause)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* De-activated state
|
||||||
|
* @return a partial function that handles messages in the 'de-activated' state
|
||||||
|
*/
|
||||||
def deactivated: State = {
|
def deactivated: State = {
|
||||||
case AwaitActivation(ref) ⇒ sender ! EndpointActivated(ref)
|
case AwaitActivation(ref) ⇒ sender ! EndpointActivated(ref)
|
||||||
case AwaitDeActivation(ref) ⇒ sender ! EndpointDeActivated(ref)
|
case AwaitDeActivation(ref) ⇒ sender ! EndpointDeActivated(ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Failed to activate state
|
||||||
|
* @param cause the cause for the failure
|
||||||
|
* @return a partial function that handles messages in 'failed to activate' state
|
||||||
|
*/
|
||||||
def failedToActivate(cause: Throwable): State = {
|
def failedToActivate(cause: Throwable): State = {
|
||||||
case AwaitActivation(ref) ⇒ sender ! EndpointFailedToActivate(ref, cause)
|
case AwaitActivation(ref) ⇒ sender ! EndpointFailedToActivate(ref, cause)
|
||||||
case AwaitDeActivation(ref) ⇒ sender ! EndpointFailedToActivate(ref, cause)
|
case AwaitDeActivation(ref) ⇒ sender ! EndpointFailedToActivate(ref, cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Failed to de-activate state
|
||||||
|
* @param cause the cause for the failure
|
||||||
|
* @return a partial function that handles messages in 'failed to de-activate' state
|
||||||
|
*/
|
||||||
def failedToDeActivate(cause: Throwable): State = {
|
def failedToDeActivate(cause: Throwable): State = {
|
||||||
case AwaitActivation(ref) ⇒ sender ! EndpointActivated(ref)
|
case AwaitActivation(ref) ⇒ sender ! EndpointActivated(ref)
|
||||||
case AwaitDeActivation(ref) ⇒ sender ! EndpointFailedToDeActivate(ref, cause)
|
case AwaitDeActivation(ref) ⇒ sender ! EndpointFailedToDeActivate(ref, cause)
|
||||||
|
|
@ -69,15 +92,21 @@ class ActivationTracker extends Actor with ActorLogging {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes self to messages of type <code>ActivationMessage</code>
|
||||||
|
*/
|
||||||
override def preStart() {
|
override def preStart() {
|
||||||
context.system.eventStream.subscribe(self, classOf[ActivationMessage])
|
context.system.eventStream.subscribe(self, classOf[ActivationMessage])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
override def receive = {
|
override def receive = {
|
||||||
case msg @ ActivationMessage(ref) ⇒ {
|
case msg @ ActivationMessage(ref) ⇒
|
||||||
val state = activations.getOrElseUpdate(ref, new ActivationStateMachine)
|
val state = activations.getOrElseUpdate(ref, new ActivationStateMachine)
|
||||||
(state.receive orElse logStateWarning(ref))(msg)
|
(state.receive orElse logStateWarning(ref))(msg)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def logStateWarning(actorRef: ActorRef): Receive = { case msg ⇒ log.warning("Message [{}] not expected in current state of actor [{}]", msg, actorRef) }
|
private[this] def logStateWarning(actorRef: ActorRef): Receive = { case msg ⇒ log.warning("Message [{}] not expected in current state of actor [{}]", msg, actorRef) }
|
||||||
|
|
|
||||||
|
|
@ -7,31 +7,31 @@ import scala.collection.JavaConversions._
|
||||||
import org.apache.camel.util.ExchangeHelper
|
import org.apache.camel.util.ExchangeHelper
|
||||||
|
|
||||||
import akka.japi.{ Function ⇒ JFunction }
|
import akka.japi.{ Function ⇒ JFunction }
|
||||||
import org.apache.camel.{ Exchange, Message ⇒ CamelMessage }
|
import org.apache.camel.{ Exchange, Message ⇒ JCamelMessage }
|
||||||
import akka.camel.{ Failure, Message }
|
import akka.camel.{ Failure, CamelMessage }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter for converting an org.apache.camel.Exchange to and from Message and Failure objects.
|
* For internal use only.
|
||||||
|
* Adapter for converting an org.apache.camel.Exchange to and from CamelMessage and Failure objects.
|
||||||
*
|
*
|
||||||
* @author Martin Krasser
|
* @author Martin Krasser
|
||||||
*/
|
*/
|
||||||
//TODO: rething/rewrite this
|
|
||||||
private[camel] class CamelExchangeAdapter(exchange: Exchange) {
|
private[camel] class CamelExchangeAdapter(exchange: Exchange) {
|
||||||
def getExchangeId = exchange.getExchangeId
|
def getExchangeId = exchange.getExchangeId
|
||||||
|
|
||||||
def isOutCapable = exchange.getPattern.isOutCapable
|
def isOutCapable = exchange.getPattern.isOutCapable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets Exchange.getIn from the given Message object.
|
* Sets Exchange.getIn from the given CamelMessage object.
|
||||||
*/
|
*/
|
||||||
def setRequest(msg: Message) { msg.copyContentTo(request) }
|
def setRequest(msg: CamelMessage) { msg.copyContentTo(request) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Depending on the exchange pattern, sets Exchange.getIn or Exchange.getOut from the given
|
* 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
|
* CamelMessage object. If the exchange is out-capable then the Exchange.getOut is set, otherwise
|
||||||
* Exchange.getIn.
|
* Exchange.getIn.
|
||||||
*/
|
*/
|
||||||
def setResponse(msg: Message) { msg.copyContentTo(response) }
|
def setResponse(msg: CamelMessage) { msg.copyContentTo(response) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets Exchange.getException from the given Failure message. Headers of the Failure message
|
* Sets Exchange.getException from the given Failure message. Headers of the Failure message
|
||||||
|
|
@ -40,15 +40,15 @@ private[camel] class CamelExchangeAdapter(exchange: Exchange) {
|
||||||
def setFailure(msg: Failure) { exchange.setException(msg.cause) }
|
def setFailure(msg: Failure) { exchange.setException(msg.cause) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Message object from Exchange.getIn.
|
* Creates a CamelMessage object from Exchange.getIn.
|
||||||
*/
|
*/
|
||||||
def toRequestMessage: Message = toRequestMessage(Map.empty)
|
def toRequestMessage: CamelMessage = toRequestMessage(Map.empty)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Depending on the exchange pattern, creates a Message object from Exchange.getIn or Exchange.getOut.
|
* Depending on the exchange pattern, creates a CamelMessage object from Exchange.getIn or Exchange.getOut.
|
||||||
* If the exchange is out-capable then the Exchange.getOut is set, otherwise Exchange.getIn.
|
* If the exchange is out-capable then the Exchange.getOut is set, otherwise Exchange.getIn.
|
||||||
*/
|
*/
|
||||||
def toResponseMessage: Message = toResponseMessage(Map.empty)
|
def toResponseMessage: CamelMessage = toResponseMessage(Map.empty)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Failure object from the adapted Exchange.
|
* Creates a Failure object from the adapted Exchange.
|
||||||
|
|
@ -58,26 +58,26 @@ private[camel] class CamelExchangeAdapter(exchange: Exchange) {
|
||||||
def toFailureMessage: Failure = toFailureMessage(Map.empty)
|
def toFailureMessage: Failure = toFailureMessage(Map.empty)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Message object from Exchange.getIn.
|
* Creates a CamelMessage object from Exchange.getIn.
|
||||||
*
|
*
|
||||||
* @param headers additional headers to set on the created Message in addition to those
|
* @param headers additional headers to set on the created CamelMessage in addition to those
|
||||||
* in the Camel message.
|
* in the Camel message.
|
||||||
*/
|
*/
|
||||||
def toRequestMessage(headers: Map[String, Any]): Message = Message.from(request, headers)
|
def toRequestMessage(headers: Map[String, Any]): CamelMessage = CamelMessage.from(request, headers)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Depending on the exchange pattern, creates a Message object from Exchange.getIn or Exchange.getOut.
|
* Depending on the exchange pattern, creates a CamelMessage object from Exchange.getIn or Exchange.getOut.
|
||||||
* If the exchange is out-capable then the Exchange.getOut is set, otherwise Exchange.getIn.
|
* 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
|
* @param headers additional headers to set on the created CamelMessage in addition to those
|
||||||
* in the Camel message.
|
* in the Camel message.
|
||||||
*/
|
*/
|
||||||
def toResponseMessage(headers: Map[String, Any]): Message = Message.from(response, headers)
|
def toResponseMessage(headers: Map[String, Any]): CamelMessage = CamelMessage.from(response, headers)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Failure object from the adapted Exchange.
|
* Creates a Failure object from the adapted Exchange.
|
||||||
*
|
*
|
||||||
* @param headers additional headers to set on the created Message in addition to those
|
* @param headers additional headers to set on the created CamelMessage in addition to those
|
||||||
* in the Camel message.
|
* in the Camel message.
|
||||||
*
|
*
|
||||||
* @see Failure
|
* @see Failure
|
||||||
|
|
@ -87,6 +87,6 @@ private[camel] class CamelExchangeAdapter(exchange: Exchange) {
|
||||||
|
|
||||||
private def request = exchange.getIn
|
private def request = exchange.getIn
|
||||||
|
|
||||||
private def response: CamelMessage = ExchangeHelper.getResultMessage(exchange)
|
private def response: JCamelMessage = ExchangeHelper.getResultMessage(exchange)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ import org.apache.camel.CamelContext
|
||||||
import akka.util.Duration
|
import akka.util.Duration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* For internal use only.
|
||||||
* Manages consumer registration. Consumers call registerConsumer method to register themselves when they get created.
|
* Manages consumer registration. Consumers call registerConsumer method to register themselves when they get created.
|
||||||
* ActorEndpoint uses it to lookup an actor by its path.
|
* ActorEndpoint uses it to lookup an actor by its path.
|
||||||
*/
|
*/
|
||||||
|
|
@ -24,8 +25,17 @@ private[camel] trait ConsumerRegistry { this: Activation ⇒
|
||||||
def system: ActorSystem
|
def system: ActorSystem
|
||||||
def context: CamelContext
|
def context: CamelContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For internal use only.
|
||||||
|
*/
|
||||||
private[this] lazy val idempotentRegistry = system.actorOf(Props(new IdempotentCamelConsumerRegistry(context)))
|
private[this] lazy val idempotentRegistry = system.actorOf(Props(new IdempotentCamelConsumerRegistry(context)))
|
||||||
|
/**
|
||||||
|
* For internal use only.
|
||||||
|
* @param endpointUri the URI to register the consumer on
|
||||||
|
* @param consumer the consumer
|
||||||
|
* @param activationTimeout the timeout for activation
|
||||||
|
* @return the actorRef to the consumer
|
||||||
|
*/
|
||||||
private[camel] def registerConsumer(endpointUri: String, consumer: Consumer, activationTimeout: Duration) = {
|
private[camel] def registerConsumer(endpointUri: String, consumer: Consumer, activationTimeout: Duration) = {
|
||||||
idempotentRegistry ! RegisterConsumer(endpointUri, consumer.self, consumer)
|
idempotentRegistry ! RegisterConsumer(endpointUri, consumer.self, consumer)
|
||||||
awaitActivation(consumer.self, activationTimeout)
|
awaitActivation(consumer.self, activationTimeout)
|
||||||
|
|
@ -33,6 +43,7 @@ private[camel] trait ConsumerRegistry { this: Activation ⇒
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* For internal use only.
|
||||||
* Guarantees idempotent registration of camel consumer endpoints.
|
* Guarantees idempotent registration of camel consumer endpoints.
|
||||||
*
|
*
|
||||||
* Once registered the consumer is watched and unregistered upon termination.
|
* Once registered the consumer is watched and unregistered upon termination.
|
||||||
|
|
@ -48,46 +59,42 @@ private[camel] class IdempotentCamelConsumerRegistry(camelContext: CamelContext)
|
||||||
val registrator = context.actorOf(Props(new CamelConsumerRegistrator))
|
val registrator = context.actorOf(Props(new CamelConsumerRegistrator))
|
||||||
|
|
||||||
def receive = {
|
def receive = {
|
||||||
case msg @ RegisterConsumer(_, consumer, _) ⇒ unless(isAlreadyActivated(consumer)) {
|
case msg @ RegisterConsumer(_, consumer, _) ⇒
|
||||||
activated.add(consumer)
|
if (!isAlreadyActivated(consumer)) {
|
||||||
registrator ! msg
|
activated.add(consumer)
|
||||||
}
|
registrator ! msg
|
||||||
case msg @ EndpointActivated(consumer) ⇒ {
|
}
|
||||||
|
case msg @ EndpointActivated(consumer) ⇒
|
||||||
context.watch(consumer)
|
context.watch(consumer)
|
||||||
context.system.eventStream.publish(msg)
|
context.system.eventStream.publish(msg)
|
||||||
}
|
case msg @ EndpointFailedToActivate(consumer, _) ⇒
|
||||||
case msg @ EndpointFailedToActivate(consumer, _) ⇒ {
|
|
||||||
activated.remove(consumer)
|
activated.remove(consumer)
|
||||||
context.system.eventStream.publish(msg)
|
context.system.eventStream.publish(msg)
|
||||||
}
|
case Terminated(ref) ⇒
|
||||||
case Terminated(ref) ⇒ {
|
|
||||||
activated.remove(ref)
|
activated.remove(ref)
|
||||||
registrator ! UnregisterConsumer(ref)
|
registrator ! UnregisterConsumer(ref)
|
||||||
}
|
case msg @ EndpointFailedToDeActivate(ref, cause) ⇒ context.system.eventStream.publish(msg)
|
||||||
case msg @ EndpointDeActivated(ref) ⇒ { context.system.eventStream.publish(msg) }
|
case msg: EndpointDeActivated ⇒ context.system.eventStream.publish(msg)
|
||||||
case msg @ EndpointFailedToDeActivate(ref, cause) ⇒ { context.system.eventStream.publish(msg) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def unless[A](condition: Boolean)(block: ⇒ A) = if (!condition) block
|
|
||||||
def isAlreadyActivated(ref: ActorRef): Boolean = activated.contains(ref)
|
def isAlreadyActivated(ref: ActorRef): Boolean = activated.contains(ref)
|
||||||
|
|
||||||
class CamelConsumerRegistrator extends Actor with ActorLogging {
|
class CamelConsumerRegistrator extends Actor with ActorLogging {
|
||||||
|
|
||||||
def receive = {
|
def receive = {
|
||||||
case RegisterConsumer(endpointUri, consumer, consumerConfig) ⇒ {
|
case RegisterConsumer(endpointUri, consumer, consumerConfig) ⇒
|
||||||
camelContext.addRoutes(new ConsumerActorRouteBuilder(endpointUri, consumer, consumerConfig))
|
camelContext.addRoutes(new ConsumerActorRouteBuilder(endpointUri, consumer, consumerConfig))
|
||||||
context.sender ! EndpointActivated(consumer)
|
context.sender ! EndpointActivated(consumer)
|
||||||
log.debug("Published actor [{}] at endpoint [{}]", consumerConfig, endpointUri)
|
log.debug("Published actor [{}] at endpoint [{}]", consumerConfig, endpointUri)
|
||||||
}
|
case UnregisterConsumer(consumer) ⇒
|
||||||
|
|
||||||
case UnregisterConsumer(consumer) ⇒ {
|
|
||||||
camelContext.stopRoute(consumer.path.toString)
|
camelContext.stopRoute(consumer.path.toString)
|
||||||
context.sender ! EndpointDeActivated(consumer)
|
context.sender ! EndpointDeActivated(consumer)
|
||||||
log.debug("Unpublished actor [{}] from endpoint [{}]", consumer, consumer.path)
|
log.debug("Unpublished actor [{}] from endpoint [{}]", consumer, consumer.path)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def preRestart(reason: Throwable, message: Option[Any]) {
|
override def preRestart(reason: Throwable, message: Option[Any]) {
|
||||||
|
//FIXME check logic
|
||||||
|
super.preStart()
|
||||||
message match {
|
message match {
|
||||||
case Some(RegisterConsumer(_, consumer, _)) ⇒ sender ! EndpointFailedToActivate(consumer, reason)
|
case Some(RegisterConsumer(_, consumer, _)) ⇒ sender ! EndpointFailedToActivate(consumer, reason)
|
||||||
case Some(UnregisterConsumer(consumer)) ⇒ sender ! EndpointFailedToDeActivate(consumer, reason)
|
case Some(UnregisterConsumer(consumer)) ⇒ sender ! EndpointFailedToDeActivate(consumer, reason)
|
||||||
|
|
@ -97,9 +104,16 @@ private[camel] class IdempotentCamelConsumerRegistry(camelContext: CamelContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For internal use only. A message to register a consumer.
|
||||||
|
* @param endpointUri the endpointUri to register to
|
||||||
|
* @param actorRef the actorRef to register as a consumer
|
||||||
|
* @param config the configuration for the consumer
|
||||||
|
*/
|
||||||
private[camel] case class RegisterConsumer(endpointUri: String, actorRef: ActorRef, config: ConsumerConfig)
|
private[camel] case class RegisterConsumer(endpointUri: String, actorRef: ActorRef, config: ConsumerConfig)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* For internal use only.
|
||||||
* Abstract builder of a route to a target which can be either an actor or an typed actor method.
|
* Abstract builder of a route to a target which can be either an actor or an typed actor method.
|
||||||
*
|
*
|
||||||
* @param endpointUri endpoint URI of the consumer actor or typed actor method.
|
* @param endpointUri endpoint URI of the consumer actor or typed actor method.
|
||||||
|
|
@ -115,7 +129,7 @@ private[camel] class ConsumerActorRouteBuilder(endpointUri: String, consumer: Ac
|
||||||
val scheme = endpointUri take endpointUri.indexOf(":") // e.g. "http" from "http://whatever/..."
|
val scheme = endpointUri take endpointUri.indexOf(":") // e.g. "http" from "http://whatever/..."
|
||||||
|
|
||||||
val route = from(endpointUri).routeId(consumer.path.toString)
|
val route = from(endpointUri).routeId(consumer.path.toString)
|
||||||
val converted = Conversions.apply(scheme, route)
|
val converted = Conversions(scheme, route)
|
||||||
val userCustomized = applyUserRouteCustomization(converted)
|
val userCustomized = applyUserRouteCustomization(converted)
|
||||||
userCustomized.to(targetActorUri)
|
userCustomized.to(targetActorUri)
|
||||||
}
|
}
|
||||||
|
|
@ -138,18 +152,39 @@ private[camel] class ConsumerActorRouteBuilder(endpointUri: String, consumer: Ac
|
||||||
/**
|
/**
|
||||||
* Super class of all activation messages.
|
* Super class of all activation messages.
|
||||||
*/
|
*/
|
||||||
private[camel] abstract class ActivationMessage(val actor: ActorRef)
|
abstract class ActivationMessage(val actor: ActorRef)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For internal use only. companion object of <code>ActivationMessage</code>
|
||||||
|
*
|
||||||
|
*/
|
||||||
private[camel] object ActivationMessage {
|
private[camel] object ActivationMessage {
|
||||||
def unapply(msg: ActivationMessage): Option[ActorRef] = Some(msg.actor)
|
def unapply(msg: ActivationMessage): Option[ActorRef] = Some(msg.actor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* For internal use only.
|
||||||
* Event message indicating that a single endpoint has been activated.
|
* Event message indicating that a single endpoint has been activated.
|
||||||
*/
|
*/
|
||||||
private[camel] case class EndpointActivated(actorRef: ActorRef) extends ActivationMessage(actorRef)
|
sealed case class EndpointActivated(actorRef: ActorRef) extends ActivationMessage(actorRef)
|
||||||
|
|
||||||
private[camel] case class EndpointFailedToActivate(actorRef: ActorRef, cause: Throwable) extends ActivationMessage(actorRef)
|
/**
|
||||||
|
* For internal use only.
|
||||||
|
* Event message indicating that a single endpoint failed tp activate
|
||||||
|
* @param actorRef the endpoint that failed to activate
|
||||||
|
* @param cause the cause for failure
|
||||||
|
*/
|
||||||
|
sealed case class EndpointFailedToActivate(actorRef: ActorRef, cause: Throwable) extends ActivationMessage(actorRef)
|
||||||
|
|
||||||
private[camel] case class EndpointDeActivated(actorRef: ActorRef) extends ActivationMessage(actorRef)
|
/**
|
||||||
|
* For internal use only.
|
||||||
|
* @param actorRef the endpoint that was de-activated
|
||||||
|
*/
|
||||||
|
sealed case class EndpointDeActivated(actorRef: ActorRef) extends ActivationMessage(actorRef)
|
||||||
|
|
||||||
private[camel] case class EndpointFailedToDeActivate(actorRef: ActorRef, cause: Throwable) extends ActivationMessage(actorRef)
|
/**
|
||||||
|
* For internal use only.
|
||||||
|
* @param actorRef the endpoint that failed to de-activate
|
||||||
|
* @param cause the cause for failure
|
||||||
|
*/
|
||||||
|
sealed case class EndpointFailedToDeActivate(actorRef: ActorRef, cause: Throwable) extends ActivationMessage(actorRef)
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,13 @@ import akka.actor.ActorSystem
|
||||||
import component.{ DurationTypeConverter, ActorComponent }
|
import component.{ DurationTypeConverter, ActorComponent }
|
||||||
import org.apache.camel.CamelContext
|
import org.apache.camel.CamelContext
|
||||||
import org.apache.camel.impl.DefaultCamelContext
|
import org.apache.camel.impl.DefaultCamelContext
|
||||||
import akka.util.Duration
|
|
||||||
import scala.Predef._
|
import scala.Predef._
|
||||||
import akka.event.Logging
|
import akka.event.Logging
|
||||||
import akka.camel.Camel
|
import akka.camel.Camel
|
||||||
|
import akka.util.{ NonFatal, Duration }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* For internal use only.
|
||||||
* Creates an instance of the Camel subsystem.
|
* Creates an instance of the Camel subsystem.
|
||||||
*
|
*
|
||||||
* @param system is used to create internal actors needed by camel instance.
|
* @param system is used to create internal actors needed by camel instance.
|
||||||
|
|
@ -18,6 +19,9 @@ import akka.camel.Camel
|
||||||
* Also by not creating extra internal actor system we are conserving resources.
|
* Also by not creating extra internal actor system we are conserving resources.
|
||||||
*/
|
*/
|
||||||
private[camel] class DefaultCamel(val system: ActorSystem) extends Camel {
|
private[camel] class DefaultCamel(val system: ActorSystem) extends Camel {
|
||||||
|
/**
|
||||||
|
* For internal use only.
|
||||||
|
*/
|
||||||
private[camel] implicit val log = Logging(system, "Camel")
|
private[camel] implicit val log = Logging(system, "Camel")
|
||||||
|
|
||||||
lazy val context: CamelContext = {
|
lazy val context: CamelContext = {
|
||||||
|
|
@ -38,7 +42,7 @@ private[camel] class DefaultCamel(val system: ActorSystem) extends Camel {
|
||||||
*/
|
*/
|
||||||
def start = {
|
def start = {
|
||||||
context.start()
|
context.start()
|
||||||
Try(template.start()) otherwise context.stop()
|
try template.start() catch { case NonFatal(e) ⇒ context.stop(); throw e }
|
||||||
log.debug("Started CamelContext[{}] for ActorSystem[{}]", context.getName, system.name)
|
log.debug("Started CamelContext[{}] for ActorSystem[{}]", context.getName, system.name)
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +55,9 @@ private[camel] class DefaultCamel(val system: ActorSystem) extends Camel {
|
||||||
* @see akka.camel.DefaultCamel#start()
|
* @see akka.camel.DefaultCamel#start()
|
||||||
*/
|
*/
|
||||||
def shutdown() {
|
def shutdown() {
|
||||||
try context.stop() finally Try.safe(template.stop())
|
try context.stop() finally {
|
||||||
|
try { template.stop() } catch { case NonFatal(e) ⇒ log.debug("Swallowing non-fatal exception [{}] on stopping Camel producer template", e) }
|
||||||
|
}
|
||||||
log.debug("Stopped CamelContext[{}] for ActorSystem[{}]", context.getName, system.name)
|
log.debug("Stopped CamelContext[{}] for ActorSystem[{}]", context.getName, system.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,26 +5,24 @@ import org.apache.camel.processor.SendProcessor
|
||||||
import akka.actor.{ Props, ActorRef, Terminated, Actor }
|
import akka.actor.{ Props, ActorRef, Terminated, Actor }
|
||||||
import org.apache.camel.Endpoint
|
import org.apache.camel.Endpoint
|
||||||
import akka.camel.Camel
|
import akka.camel.Camel
|
||||||
|
import akka.util.NonFatal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Watches the end of life of <code>Producer</code>s.
|
* Watches the end of life of <code>Producer</code>s.
|
||||||
* Removes a <code>Producer</code> from the <code>ProducerRegistry</code> when it is <code>Terminated</code>,
|
* Removes a <code>Producer</code> from the <code>ProducerRegistry</code> when it is <code>Terminated</code>,
|
||||||
* which in turn stops the <code>SendProcessor</code>.
|
* which in turn stops the <code>SendProcessor</code>.
|
||||||
*/
|
*/
|
||||||
private[camel] class ProducerWatcher(registry: ProducerRegistry) extends Actor {
|
private class ProducerWatcher(registry: ProducerRegistry) extends Actor {
|
||||||
override def receive = {
|
override def receive = {
|
||||||
case RegisterProducer(actorRef) ⇒ {
|
case RegisterProducer(actorRef) ⇒ context.watch(actorRef)
|
||||||
context.watch(actorRef)
|
case Terminated(actorRef) ⇒ registry.unregisterProducer(actorRef)
|
||||||
}
|
|
||||||
case Terminated(actorRef) ⇒ {
|
|
||||||
registry.unregisterProducer(actorRef)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private[camel] case class RegisterProducer(actorRef: ActorRef)
|
private case class RegisterProducer(actorRef: ActorRef)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* For internal use only.
|
||||||
* Manages the Camel objects for <code>Producer</code>s.
|
* Manages the Camel objects for <code>Producer</code>s.
|
||||||
* Every <code>Producer</code> needs an <code>Endpoint</code> and a <code>SendProcessor</code>
|
* Every <code>Producer</code> needs an <code>Endpoint</code> and a <code>SendProcessor</code>
|
||||||
* to produce messages over an <code>Exchange</code>.
|
* to produce messages over an <code>Exchange</code>.
|
||||||
|
|
@ -39,6 +37,7 @@ private[camel] trait ProducerRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* For internal use only.
|
||||||
* Unregisters <code>Endpoint</code> and <code>SendProcessor</code> and stops the SendProcessor
|
* Unregisters <code>Endpoint</code> and <code>SendProcessor</code> and stops the SendProcessor
|
||||||
*/
|
*/
|
||||||
private[camel] def unregisterProducer(actorRef: ActorRef): Unit = {
|
private[camel] def unregisterProducer(actorRef: ActorRef): Unit = {
|
||||||
|
|
@ -49,12 +48,13 @@ private[camel] trait ProducerRegistry {
|
||||||
processor.stop()
|
processor.stop()
|
||||||
system.eventStream.publish(EndpointDeActivated(actorRef))
|
system.eventStream.publish(EndpointDeActivated(actorRef))
|
||||||
} catch {
|
} catch {
|
||||||
case e ⇒ system.eventStream.publish(EndpointFailedToDeActivate(actorRef, e))
|
case NonFatal(e) ⇒ system.eventStream.publish(EndpointFailedToDeActivate(actorRef, e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* For internal use only.
|
||||||
* Creates <code>Endpoint</code> and <code>SendProcessor</code> and associates the actorRef to these.
|
* Creates <code>Endpoint</code> and <code>SendProcessor</code> and associates the actorRef to these.
|
||||||
* @param actorRef the actorRef of the <code>Producer</code> actor.
|
* @param actorRef the actorRef of the <code>Producer</code> actor.
|
||||||
* @param endpointUri the endpoint Uri of the producer
|
* @param endpointUri the endpoint Uri of the producer
|
||||||
|
|
@ -65,17 +65,16 @@ private[camel] trait ProducerRegistry {
|
||||||
val endpoint = context.getEndpoint(endpointUri)
|
val endpoint = context.getEndpoint(endpointUri)
|
||||||
val processor = new SendProcessor(endpoint)
|
val processor = new SendProcessor(endpoint)
|
||||||
|
|
||||||
val prev = camelObjects.putIfAbsent(actorRef, (endpoint, processor))
|
camelObjects.putIfAbsent(actorRef, (endpoint, processor)) match {
|
||||||
if (prev != null) {
|
case null ⇒
|
||||||
prev
|
processor.start()
|
||||||
} else {
|
registerWatch(actorRef)
|
||||||
processor.start()
|
system.eventStream.publish(EndpointActivated(actorRef))
|
||||||
system.eventStream.publish(EndpointActivated(actorRef))
|
(endpoint, processor)
|
||||||
registerWatch(actorRef)
|
case prev ⇒ prev
|
||||||
(endpoint, processor)
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case e ⇒ {
|
case NonFatal(e) ⇒ {
|
||||||
system.eventStream.publish(EndpointFailedToActivate(actorRef, e))
|
system.eventStream.publish(EndpointFailedToActivate(actorRef, e))
|
||||||
// can't return null to the producer actor, so blow up actor in initialization.
|
// can't return null to the producer actor, so blow up actor in initialization.
|
||||||
throw e
|
throw e
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
package akka.camel.internal
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import akka.event.LoggingAdapter
|
|
||||||
|
|
||||||
private[camel] object Try {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to execute body.
|
|
||||||
*
|
|
||||||
* Example below tries to start template and if it's unsuccessful it stops context in a safe way, by logging exceptions and then it rethrows exception thrown by template.start()
|
|
||||||
* <pre> Try(template.start()) otherwise context.stop() </pre>
|
|
||||||
*
|
|
||||||
* @param body block of code to execute.
|
|
||||||
* @return Ok, if no exception is thrown by body.
|
|
||||||
* @return Failed, if exception was thrown by body.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
def apply(body: ⇒ Unit): Result =
|
|
||||||
try {
|
|
||||||
body; Ok
|
|
||||||
} catch {
|
|
||||||
case e ⇒ Failed(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed trait Result {
|
|
||||||
def otherwise(onError: ⇒ Unit)(implicit log: LoggingAdapter): Unit = ()
|
|
||||||
}
|
|
||||||
|
|
||||||
private[this] case object Ok extends Result
|
|
||||||
|
|
||||||
private[this] case class Failed(e: Throwable) extends Result {
|
|
||||||
override def otherwise(onError: ⇒ Unit)(implicit log: LoggingAdapter) = {
|
|
||||||
safe(onError)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the block and logs the exception, if it's thrown by the block, and swallows the exception.
|
|
||||||
*/
|
|
||||||
@inline def safe(block: ⇒ Unit)(implicit log: LoggingAdapter) {
|
|
||||||
try {
|
|
||||||
block
|
|
||||||
} catch {
|
|
||||||
case e ⇒ log.warning("Safe operation failed. Swallowing exception [{}]", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -13,13 +13,14 @@ import akka.actor._
|
||||||
import akka.pattern._
|
import akka.pattern._
|
||||||
|
|
||||||
import scala.reflect.BeanProperty
|
import scala.reflect.BeanProperty
|
||||||
import akka.util.{ Duration, Timeout }
|
|
||||||
import akka.util.duration._
|
import akka.util.duration._
|
||||||
import java.util.concurrent.{ TimeoutException, CountDownLatch }
|
import java.util.concurrent.{ TimeoutException, CountDownLatch }
|
||||||
import akka.camel.{ ConsumerConfig, Camel, Ack, Failure ⇒ CamelFailure, Message }
|
|
||||||
import akka.camel.internal.CamelExchangeAdapter
|
import akka.camel.internal.CamelExchangeAdapter
|
||||||
|
import akka.util.{ NonFatal, Duration, Timeout }
|
||||||
|
import akka.camel.{ DefaultConsumerConfig, ConsumerConfig, Camel, Ack, Failure ⇒ CamelFailure, CamelMessage }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* For internal use only.
|
||||||
* Camel component for sending messages to and receiving replies from (untyped) actors.
|
* Camel component for sending messages to and receiving replies from (untyped) actors.
|
||||||
*
|
*
|
||||||
* @see akka.camel.component.ActorEndpoint
|
* @see akka.camel.component.ActorEndpoint
|
||||||
|
|
@ -27,7 +28,7 @@ import akka.camel.internal.CamelExchangeAdapter
|
||||||
*
|
*
|
||||||
* @author Martin Krasser
|
* @author Martin Krasser
|
||||||
*/
|
*/
|
||||||
class ActorComponent(camel: Camel) extends DefaultComponent {
|
private[camel] class ActorComponent(camel: Camel) extends DefaultComponent {
|
||||||
def createEndpoint(uri: String, remaining: String, parameters: JMap[String, Object]): ActorEndpoint = {
|
def createEndpoint(uri: String, remaining: String, parameters: JMap[String, Object]): ActorEndpoint = {
|
||||||
val path = ActorEndpointPath.fromCamelPath(remaining)
|
val path = ActorEndpointPath.fromCamelPath(remaining)
|
||||||
new ActorEndpoint(uri, this, path, camel)
|
new ActorEndpoint(uri, this, path, camel)
|
||||||
|
|
@ -35,32 +36,31 @@ class ActorComponent(camel: Camel) extends DefaultComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO fix the doc to be consistent with implementation
|
* For internal use only.
|
||||||
* Camel endpoint for sending messages to and receiving replies from (untyped) actors. Actors
|
* The ActorEndpoint is a Camel Endpoint that is used to receive messages from Camel through the ActorComponent
|
||||||
* are referenced using <code>actor</code> endpoint URIs of the following format:
|
* Actors are referenced using <code>actor</code> endpoint URIs of the following format:
|
||||||
* <code>actor:<actor-id></code>,
|
* <code>actor://path:[actorPath]?[options]%s</code>,
|
||||||
* <code>actor:id:[<actor-id>]</code> and
|
* where <code>[actorPath]</code> refers to the Actor Path to the Actor.
|
||||||
* <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.ActorComponent
|
||||||
* @see akka.camel.component.ActorProducer
|
* @see akka.camel.component.ActorProducer
|
||||||
*
|
*
|
||||||
* @author Martin Krasser
|
* @author Martin Krasser
|
||||||
*/
|
*/
|
||||||
class ActorEndpoint(uri: String,
|
private[camel] class ActorEndpoint(uri: String,
|
||||||
comp: ActorComponent,
|
comp: ActorComponent,
|
||||||
val path: ActorEndpointPath,
|
val path: ActorEndpointPath,
|
||||||
camel: Camel) extends DefaultEndpoint(uri, comp) with ActorEndpointConfig {
|
camel: Camel) extends DefaultEndpoint(uri, comp) with ActorEndpointConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws UnsupportedOperationException
|
*
|
||||||
|
* The ActorEndpoint right now only supports receiving messages from Camel.
|
||||||
|
* The createProducer method (not to be confused with a producer actor) is used to send messages into the endpoint.
|
||||||
|
* The ActorComponent is only there to send to actors registered through an actor endpoint URI.
|
||||||
|
* You can use an actor as an endpoint to send to in a camel route (as in, a Camel Consumer Actor). so from(someuri) to (actoruri), but not 'the other way around'.
|
||||||
|
* Supporting createConsumer would mean that messages are consumed from an Actor endpoint in a route, and an Actor is not necessarily a producer of messages
|
||||||
|
* [[akka.camel.Producer]] Actors can be used for sending messages to some other uri/ component type registered in Camel.
|
||||||
|
* @throws UnsupportedOperationException this method is not supported
|
||||||
*/
|
*/
|
||||||
def createConsumer(processor: Processor): org.apache.camel.Consumer =
|
def createConsumer(processor: Processor): org.apache.camel.Consumer =
|
||||||
throw new UnsupportedOperationException("actor consumer not supported yet")
|
throw new UnsupportedOperationException("actor consumer not supported yet")
|
||||||
|
|
@ -76,7 +76,7 @@ class ActorEndpoint(uri: String,
|
||||||
def isSingleton: Boolean = true
|
def isSingleton: Boolean = true
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ActorEndpointConfig {
|
private[camel] trait ActorEndpointConfig {
|
||||||
def path: ActorEndpointPath
|
def path: ActorEndpointPath
|
||||||
|
|
||||||
@BeanProperty var replyTimeout: Duration = 1 minute
|
@BeanProperty var replyTimeout: Duration = 1 minute
|
||||||
|
|
@ -91,43 +91,54 @@ trait ActorEndpointConfig {
|
||||||
@BeanProperty var autoack: Boolean = true
|
@BeanProperty var autoack: Boolean = true
|
||||||
}
|
}
|
||||||
|
|
||||||
//FIXME: rewrite this doc
|
|
||||||
/**
|
/**
|
||||||
* Sends the in-message of an exchange to an (untyped) actor, identified by an
|
* Sends the in-message of an exchange to an untyped actor, identified by an [[akka.camel.internal.component.ActorEndPoint]]
|
||||||
* 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.ActorComponent
|
||||||
* @see akka.camel.component.ActorEndpoint
|
* @see akka.camel.component.ActorEndpoint
|
||||||
*
|
*
|
||||||
* @author Martin Krasser
|
* @author Martin Krasser
|
||||||
*/
|
*/
|
||||||
class ActorProducer(val ep: ActorEndpoint, camel: Camel) extends DefaultProducer(ep) with AsyncProcessor {
|
private[camel] class ActorProducer(val endpoint: ActorEndpoint, camel: Camel) extends DefaultProducer(endpoint) with AsyncProcessor {
|
||||||
def process(exchange: Exchange) { new ConsumerAsyncProcessor(ep, camel).process(new CamelExchangeAdapter(exchange)) }
|
/**
|
||||||
def process(exchange: Exchange, callback: AsyncCallback) = new ConsumerAsyncProcessor(ep, camel).process(new CamelExchangeAdapter(exchange), callback)
|
* Processes the exchange.
|
||||||
}
|
* Calls the asynchronous version of the method and waits for the result (blocking)
|
||||||
|
* @param exchange the exchange to process
|
||||||
|
*/
|
||||||
|
def process(exchange: Exchange) { processExchangeAdapter(new CamelExchangeAdapter(exchange)) }
|
||||||
|
|
||||||
class ConsumerAsyncProcessor(config: ActorEndpointConfig, camel: Camel) {
|
/**
|
||||||
|
* Processes the message exchange. the caller supports having the exchange asynchronously processed.
|
||||||
|
* If there was a failure processing then the caused Exception would be set on the Exchange.
|
||||||
|
*
|
||||||
|
* @param exchange the message exchange
|
||||||
|
* @param callback the AsyncCallback will be invoked when the processing of the exchange is completed.
|
||||||
|
* If the exchange is completed synchronously, then the callback is also invoked synchronously.
|
||||||
|
* The callback should therefore be careful of starting recursive loop.
|
||||||
|
* @return (doneSync) true to continue execute synchronously, false to continue being executed asynchronously
|
||||||
|
*/
|
||||||
|
def process(exchange: Exchange, callback: AsyncCallback): Boolean = { processExchangeAdapter(new CamelExchangeAdapter(exchange), callback) }
|
||||||
|
|
||||||
def process(exchange: CamelExchangeAdapter) {
|
/**
|
||||||
|
* For internal use only. Processes the [[akka.camel.internal.CamelExchangeAdapter]]
|
||||||
|
* @param exchange the [[akka.camel.internal.CamelExchangeAdapter]]
|
||||||
|
*/
|
||||||
|
private[camel] def processExchangeAdapter(exchange: CamelExchangeAdapter) {
|
||||||
val isDone = new CountDownLatch(1)
|
val isDone = new CountDownLatch(1)
|
||||||
process(exchange, new AsyncCallback { def done(doneSync: Boolean) { isDone.countDown() } })
|
processExchangeAdapter(exchange, new AsyncCallback { def done(doneSync: Boolean) { isDone.countDown() } })
|
||||||
isDone.await() // this should never wait forever as the process(exchange, callback) method guarantees that.
|
isDone.await() // this should never wait forever as the process(exchange, callback) method guarantees that.
|
||||||
}
|
}
|
||||||
|
|
||||||
def process(exchange: CamelExchangeAdapter, callback: AsyncCallback): Boolean = {
|
/**
|
||||||
|
* For internal use only. Processes the [[akka.camel.internal.CamelExchangeAdapter]].
|
||||||
|
* This method is blocking when the exchange is inOnly. The method returns true if it executed synchronously/blocking.
|
||||||
|
* @param exchange the [[akka.camel.internal.CamelExchangeAdapter]]
|
||||||
|
* @param callback the callback
|
||||||
|
* @return (doneSync) true to continue execute synchronously, false to continue being executed asynchronously
|
||||||
|
*/
|
||||||
|
private[camel] def processExchangeAdapter(exchange: CamelExchangeAdapter, callback: AsyncCallback): Boolean = {
|
||||||
|
|
||||||
// this notify methods are just a syntax sugar
|
// these notify methods are just a syntax sugar
|
||||||
def notifyDoneSynchronously[A](a: A = null) = callback.done(true)
|
def notifyDoneSynchronously[A](a: A = null) = callback.done(true)
|
||||||
def notifyDoneAsynchronously[A](a: A = null) = callback.done(false)
|
def notifyDoneAsynchronously[A](a: A = null) = callback.done(false)
|
||||||
|
|
||||||
|
|
@ -136,7 +147,7 @@ class ConsumerAsyncProcessor(config: ActorEndpointConfig, camel: Camel) {
|
||||||
if (exchange.isOutCapable) { //InOut
|
if (exchange.isOutCapable) { //InOut
|
||||||
sendAsync(message, onComplete = forwardResponseTo(exchange) andThen notifyDoneAsynchronously)
|
sendAsync(message, onComplete = forwardResponseTo(exchange) andThen notifyDoneAsynchronously)
|
||||||
} else { // inOnly
|
} else { // inOnly
|
||||||
if (config.autoack) { //autoAck
|
if (endpoint.autoack) { //autoAck
|
||||||
fireAndForget(message, exchange)
|
fireAndForget(message, exchange)
|
||||||
notifyDoneSynchronously()
|
notifyDoneSynchronously()
|
||||||
true // done sync
|
true // done sync
|
||||||
|
|
@ -146,46 +157,45 @@ class ConsumerAsyncProcessor(config: ActorEndpointConfig, camel: Camel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
private def forwardResponseTo(exchange: CamelExchangeAdapter): PartialFunction[Either[Throwable, Any], Unit] = {
|
||||||
|
case Right(failure: CamelFailure) ⇒ exchange.setFailure(failure);
|
||||||
|
case Right(msg) ⇒ exchange.setResponse(CamelMessage.canonicalize(msg))
|
||||||
|
case Left(e: TimeoutException) ⇒ exchange.setFailure(CamelFailure(new TimeoutException("Failed to get response from the actor [%s] within timeout [%s]. Check replyTimeout and blocking settings [%s]" format (endpoint.path, endpoint.replyTimeout, endpoint))))
|
||||||
|
case Left(throwable) ⇒ exchange.setFailure(CamelFailure(throwable))
|
||||||
|
}
|
||||||
|
|
||||||
private def sendAsync(message: Message, onComplete: PartialFunction[Either[Throwable, Any], Unit]): Boolean = {
|
private def forwardAckTo(exchange: CamelExchangeAdapter): PartialFunction[Either[Throwable, Any], Unit] = {
|
||||||
|
case Right(Ack) ⇒ { /* no response message to set */ }
|
||||||
|
case Right(failure: CamelFailure) ⇒ exchange.setFailure(failure)
|
||||||
|
case Right(msg) ⇒ exchange.setFailure(CamelFailure(new IllegalArgumentException("Expected Ack or Failure message, but got: [%s] from actor [%s]" format (msg, endpoint.path))))
|
||||||
|
case Left(e: TimeoutException) ⇒ exchange.setFailure(CamelFailure(new TimeoutException("Failed to get Ack or Failure response from the actor [%s] within timeout [%s]. Check replyTimeout and blocking settings [%s]" format (endpoint.path, endpoint.replyTimeout, endpoint))))
|
||||||
|
case Left(throwable) ⇒ exchange.setFailure(CamelFailure(throwable))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def sendAsync(message: CamelMessage, onComplete: PartialFunction[Either[Throwable, Any], Unit]): Boolean = {
|
||||||
try {
|
try {
|
||||||
val actor = actorFor(config.path)
|
val actor = actorFor(endpoint.path)
|
||||||
val future = actor.ask(message)(new Timeout(config.replyTimeout))
|
val future = actor.ask(message)(new Timeout(endpoint.replyTimeout))
|
||||||
future.onComplete(onComplete)
|
future.onComplete(onComplete)
|
||||||
} catch {
|
} catch {
|
||||||
case e ⇒ onComplete(Left(e))
|
case NonFatal(e) ⇒ onComplete(Left(e))
|
||||||
}
|
}
|
||||||
false // Done async
|
false // Done async
|
||||||
}
|
}
|
||||||
|
|
||||||
private def fireAndForget(message: Message, exchange: CamelExchangeAdapter) {
|
private def fireAndForget(message: CamelMessage, exchange: CamelExchangeAdapter) {
|
||||||
try {
|
try {
|
||||||
actorFor(config.path) ! message
|
actorFor(endpoint.path) ! message
|
||||||
} catch {
|
} catch {
|
||||||
case e ⇒ exchange.setFailure(new CamelFailure(e))
|
case e ⇒ exchange.setFailure(new CamelFailure(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def forwardResponseTo(exchange: CamelExchangeAdapter): PartialFunction[Either[Throwable, Any], Unit] = {
|
|
||||||
case Right(failure: CamelFailure) ⇒ exchange.setFailure(failure);
|
|
||||||
case Right(msg) ⇒ exchange.setResponse(Message.canonicalize(msg))
|
|
||||||
case Left(e: TimeoutException) ⇒ exchange.setFailure(CamelFailure(new TimeoutException("Failed to get response from the actor [%s] within timeout [%s]. Check replyTimeout and blocking settings [%s]" format (config.path, config.replyTimeout, config))))
|
|
||||||
case Left(throwable) ⇒ exchange.setFailure(CamelFailure(throwable))
|
|
||||||
}
|
|
||||||
|
|
||||||
def forwardAckTo(exchange: CamelExchangeAdapter): PartialFunction[Either[Throwable, Any], Unit] = {
|
|
||||||
case Right(Ack) ⇒ { /* no response message to set */ }
|
|
||||||
case Right(failure: CamelFailure) ⇒ exchange.setFailure(failure)
|
|
||||||
case Right(msg) ⇒ exchange.setFailure(CamelFailure(new IllegalArgumentException("Expected Ack or Failure message, but got: [%s] from actor [%s]" format (msg, config.path))))
|
|
||||||
case Left(e: TimeoutException) ⇒ exchange.setFailure(CamelFailure(new TimeoutException("Failed to get Ack or Failure response from the actor [%s] within timeout [%s]. Check replyTimeout and blocking settings [%s]" format (config.path, config.replyTimeout, config))))
|
|
||||||
case Left(throwable) ⇒ exchange.setFailure(CamelFailure(throwable))
|
|
||||||
}
|
|
||||||
|
|
||||||
private[this] def actorFor(path: ActorEndpointPath): ActorRef =
|
private[this] def actorFor(path: ActorEndpointPath): ActorRef =
|
||||||
path.findActorIn(camel.system) getOrElse (throw new ActorNotRegisteredException(path.actorPath))
|
path.findActorIn(camel.system) getOrElse (throw new ActorNotRegisteredException(path.actorPath))
|
||||||
|
|
||||||
private[this] def messageFor(exchange: CamelExchangeAdapter) =
|
private[this] def messageFor(exchange: CamelExchangeAdapter) =
|
||||||
exchange.toRequestMessage(Map(Message.MessageExchangeId -> exchange.getExchangeId))
|
exchange.toRequestMessage(Map(CamelMessage.MessageExchangeId -> exchange.getExchangeId))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,25 +209,34 @@ class ActorNotRegisteredException(uri: String) extends RuntimeException {
|
||||||
override def getMessage = "Actor [%s] doesn't exist" format uri
|
override def getMessage = "Actor [%s] doesn't exist" format uri
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For internal use only.
|
||||||
|
*/
|
||||||
private[camel] object DurationTypeConverter extends CamelTypeConverter {
|
private[camel] object DurationTypeConverter extends CamelTypeConverter {
|
||||||
def convertTo[T](`type`: Class[T], value: AnyRef) = {
|
def convertTo[T](`type`: Class[T], value: AnyRef) = {
|
||||||
require(value.toString.endsWith(" nanos"))
|
Duration(value.toString).asInstanceOf[T]
|
||||||
Duration.fromNanos(value.toString.dropRight(6).toLong).asInstanceOf[T]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def toString(duration: Duration) = duration.toNanos + " nanos"
|
def toString(duration: Duration) = duration.toNanos + " nanos"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For internal use only.
|
||||||
|
*/
|
||||||
private[camel] abstract class CamelTypeConverter extends TypeConverter {
|
private[camel] abstract class CamelTypeConverter extends TypeConverter {
|
||||||
def convertTo[T](`type`: Class[T], exchange: Exchange, value: AnyRef) = convertTo(`type`, value)
|
def convertTo[T](`type`: Class[T], exchange: Exchange, value: AnyRef) = convertTo(`type`, value)
|
||||||
def mandatoryConvertTo[T](`type`: Class[T], value: AnyRef) = convertTo(`type`, value)
|
def mandatoryConvertTo[T](`type`: Class[T], value: AnyRef) = convertTo(`type`, value)
|
||||||
def mandatoryConvertTo[T](`type`: Class[T], exchange: Exchange, value: AnyRef) = convertTo(`type`, value)
|
def mandatoryConvertTo[T](`type`: Class[T], exchange: Exchange, value: AnyRef) = convertTo(`type`, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For internal use only. An endpoint to an <code>ActorRef</code>
|
||||||
|
* @param actorPath the path to the actor
|
||||||
|
*/
|
||||||
private[camel] case class ActorEndpointPath private (actorPath: String) {
|
private[camel] case class ActorEndpointPath private (actorPath: String) {
|
||||||
require(actorPath != null)
|
require(actorPath != null)
|
||||||
require(actorPath.length() > 0)
|
require(actorPath.length() > 0)
|
||||||
def toCamelPath(config: ConsumerConfig = new ConsumerConfig {}): String = "actor://path:%s?%s" format (actorPath, config.toCamelParameters)
|
def toCamelPath(config: ConsumerConfig = DefaultConsumerConfig): String = "actor://path:%s?%s" format (actorPath, config.toCamelParameters)
|
||||||
|
|
||||||
def findActorIn(system: ActorSystem): Option[ActorRef] = {
|
def findActorIn(system: ActorSystem): Option[ActorRef] = {
|
||||||
val ref = system.actorFor(actorPath)
|
val ref = system.actorFor(actorPath)
|
||||||
|
|
@ -226,6 +245,9 @@ private[camel] case class ActorEndpointPath private (actorPath: String) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For internal use only. Companion of <code>ActorEndpointPath</code>
|
||||||
|
*/
|
||||||
private[camel] object ActorEndpointPath {
|
private[camel] object ActorEndpointPath {
|
||||||
def apply(actorRef: ActorRef) = new ActorEndpointPath(actorRef.path.toString)
|
def apply(actorRef: ActorRef) = new ActorEndpointPath(actorRef.path.toString)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ package akka.camel.javaapi
|
||||||
|
|
||||||
import akka.actor.UntypedActor
|
import akka.actor.UntypedActor
|
||||||
import akka.camel._
|
import akka.camel._
|
||||||
|
import org.apache.camel.{ ProducerTemplate, CamelContext }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java-friendly Consumer.
|
* Java-friendly Consumer.
|
||||||
|
|
@ -23,11 +24,22 @@ trait UntypedConsumer extends Consumer { self: UntypedActor ⇒
|
||||||
*/
|
*/
|
||||||
def getEndpointUri(): String
|
def getEndpointUri(): String
|
||||||
|
|
||||||
def rich(message: Message): RichMessage = message
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclass this abstract class to create an MDB-style untyped consumer actor. This
|
* Subclass this abstract class to create an MDB-style untyped consumer actor. This
|
||||||
* class is meant to be used from Java.
|
* class is meant to be used from Java.
|
||||||
*/
|
*/
|
||||||
abstract class UntypedConsumerActor extends UntypedActor with UntypedConsumer
|
abstract class UntypedConsumerActor extends UntypedActor with UntypedConsumer {
|
||||||
|
/**
|
||||||
|
* Returns the [[org.apache.camel.CamelContext]]
|
||||||
|
* @return the CamelContext
|
||||||
|
*/
|
||||||
|
protected def getCamelContext: CamelContext = camelContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the [[org.apache.camel.ProducerTemplate]]
|
||||||
|
* @return the ProducerTemplate
|
||||||
|
*/
|
||||||
|
protected def getProducerTemplate: ProducerTemplate = camel.template
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ abstract class UntypedProducerActor extends UntypedActor with ProducerSupport {
|
||||||
* message is passed as argument. By default, this method simply returns the argument but may be overridden
|
* message is passed as argument. By default, this method simply returns the argument but may be overridden
|
||||||
* by subclasses.
|
* by subclasses.
|
||||||
*/
|
*/
|
||||||
def onReceiveBeforeProduce(message: Any): Any = super.receiveBeforeProduce(message)
|
def onReceiveBeforeProduce(message: AnyRef): AnyRef = message
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called after a response was received from the endpoint specified by <code>endpointUri</code>. The
|
* Called after a response was received from the endpoint specified by <code>endpointUri</code>. The
|
||||||
|
|
@ -27,14 +27,14 @@ abstract class UntypedProducerActor extends UntypedActor with ProducerSupport {
|
||||||
* if <code>oneway</code> is <code>false</code>. If <code>oneway</code> is <code>true</code>, nothing is
|
* 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).
|
* done. This method may be overridden by subclasses (e.g. to forward responses to another actor).
|
||||||
*/
|
*/
|
||||||
def onReceiveAfterProduce(message: Any): Unit = super.receiveAfterProduce(message)
|
def onReceiveAfterProduce(message: AnyRef): Unit = super.receiveAfterProduce(message)
|
||||||
|
|
||||||
final override def receiveBeforeProduce = {
|
final override def receiveBeforeProduce = {
|
||||||
case msg ⇒ onReceiveBeforeProduce(msg)
|
case msg: AnyRef ⇒ onReceiveBeforeProduce(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
final override def receiveAfterProduce = {
|
final override def receiveAfterProduce = {
|
||||||
case msg ⇒ onReceiveAfterProduce(msg)
|
case msg: AnyRef ⇒ onReceiveAfterProduce(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
final override def endpointUri = getEndpointUri
|
final override def endpointUri = getEndpointUri
|
||||||
|
|
@ -60,13 +60,6 @@ abstract class UntypedProducerActor extends UntypedActor with ProducerSupport {
|
||||||
*/
|
*/
|
||||||
def isOneway() = super.oneway
|
def isOneway() = super.oneway
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a <code>RichMessage</code> out of a message. The <code>RichMessage</code> has convenience methods for accessing body and headers
|
|
||||||
* @param message the message
|
|
||||||
* @return the <code>RichMessage</code>
|
|
||||||
*/
|
|
||||||
def rich(message: Message): RichMessage = message
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the <code>CamelContext</code>.
|
* Returns the <code>CamelContext</code>.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class SysOutConsumer extends Consumer {
|
||||||
def endpointUri = "file://data/input/CamelConsumer"
|
def endpointUri = "file://data/input/CamelConsumer"
|
||||||
|
|
||||||
protected def receive = {
|
protected def receive = {
|
||||||
case msg: Message ⇒ {
|
case msg: CamelMessage ⇒ {
|
||||||
printf("Received '%s'\n", msg.bodyAs[String])
|
printf("Received '%s'\n", msg.bodyAs[String])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -34,8 +34,9 @@ class TroubleMaker extends Consumer {
|
||||||
}
|
}
|
||||||
|
|
||||||
class SysOutActor(implicit camel: Camel) extends Actor {
|
class SysOutActor(implicit camel: Camel) extends Actor {
|
||||||
|
implicit val camelContext = camel.context
|
||||||
protected def receive = {
|
protected def receive = {
|
||||||
case msg: Message ⇒ {
|
case msg: CamelMessage ⇒ {
|
||||||
printf("Received '%s'\n", msg.bodyAs[String])
|
printf("Received '%s'\n", msg.bodyAs[String])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,4 @@ import org.apache.camel.model.ProcessorDefinition
|
||||||
|
|
||||||
package object camel {
|
package object camel {
|
||||||
implicit def toActorRouteDefinition(definition: ProcessorDefinition[_]) = new ActorRouteDefinition(definition)
|
implicit def toActorRouteDefinition(definition: ProcessorDefinition[_]) = new ActorRouteDefinition(definition)
|
||||||
implicit def messageToRichMessage(m: Message)(implicit camel: Camel): RichMessage = new RichMessage(m, camel.context)
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
package akka.camel
|
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
|
|
||||||
import collection.mutable.Buffer
|
|
||||||
|
|
||||||
import akka.actor.Actor
|
|
||||||
|
|
||||||
object CamelTestSupport {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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.Actor._
|
|
||||||
import akka.camel.{Failure, Message, CamelContextManager}
|
|
||||||
import akka.camel.CamelTestSupport._
|
|
||||||
|
|
||||||
class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach {
|
|
||||||
import ActorComponentFeatureTest._
|
|
||||||
|
|
||||||
override protected def beforeAll = {
|
|
||||||
Actor.registry.shutdownAll
|
|
||||||
CamelContextManager.init
|
|
||||||
CamelContextManager.mandatoryContext.addRoutes(new TestRoute)
|
|
||||||
CamelContextManager.start
|
|
||||||
}
|
|
||||||
|
|
||||||
override protected def afterAll = CamelContextManager.stop
|
|
||||||
|
|
||||||
override protected def afterEach = {
|
|
||||||
Actor.registry.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,243 +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.camel.{Failure, Message}
|
|
||||||
import akka.camel.CamelTestSupport._
|
|
||||||
|
|
||||||
class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|
||||||
import ActorProducerTest._
|
|
||||||
|
|
||||||
@After def tearDown = registry.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 shouldSendMessageToActorAndReceiveAckWithAsyncProcessor = {
|
|
||||||
val actor = actorOf(new Tester2 {
|
|
||||||
override def response(msg: Message) = akka.camel.Ack
|
|
||||||
}).start
|
|
||||||
val completion = expectAsyncCompletion
|
|
||||||
val endpoint = actorEndpoint("actor:uuid:%s?autoack=false" format actor.uuid)
|
|
||||||
val exchange = endpoint.createExchange(ExchangePattern.InOnly)
|
|
||||||
exchange.getIn.setBody("Martin")
|
|
||||||
actorAsyncProducer(endpoint).process(exchange, completion)
|
|
||||||
assert(completion.latch.await(5000, TimeUnit.MILLISECONDS))
|
|
||||||
assert(exchange.getIn.getBody === "Martin")
|
|
||||||
assert(exchange.getOut.getBody === null)
|
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -35,50 +35,49 @@ public class MessageJavaTestBase {
|
||||||
system.shutdown();
|
system.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
Message message(Object body){ return new Message(body, new HashMap()); }
|
CamelMessage message(Object body){ return new CamelMessage(body, new HashMap<String, Object>()); }
|
||||||
Message message(Object body, Map<String, Object> headers){ return new Message(body, headers); }
|
CamelMessage message(Object body, Map<String, Object> headers){ return new CamelMessage(body, headers); }
|
||||||
private RichMessage rich(Message message) { return new RichMessage(message, camel.context()); }
|
|
||||||
|
|
||||||
|
|
||||||
@Test public void shouldConvertDoubleBodyToString() {
|
@Test public void shouldConvertDoubleBodyToString() {
|
||||||
assertEquals("1.4", rich(message("1.4", empty)).getBodyAs(String.class));
|
assertEquals("1.4", message("1.4", empty).getBodyAs(String.class,camel.context()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected=NoTypeConversionAvailableException.class)
|
@Test(expected=NoTypeConversionAvailableException.class)
|
||||||
public void shouldThrowExceptionWhenConvertingDoubleBodyToInputStream() {
|
public void shouldThrowExceptionWhenConvertingDoubleBodyToInputStream() {
|
||||||
rich(message(1.4)).getBodyAs(InputStream.class);
|
message(1.4).getBodyAs(InputStream.class,camel.context());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test public void shouldReturnDoubleHeader() {
|
@Test public void shouldReturnDoubleHeader() {
|
||||||
Message message = message("test" , createMap("test", 1.4));
|
CamelMessage message = message("test" , createMap("test", 1.4));
|
||||||
assertEquals(1.4, message.getHeader("test"));
|
assertEquals(1.4, message.getHeader("test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void shouldConvertDoubleHeaderToString() {
|
@Test public void shouldConvertDoubleHeaderToString() {
|
||||||
Message message = message("test" , createMap("test", 1.4));
|
CamelMessage message = message("test" , createMap("test", 1.4));
|
||||||
assertEquals("1.4", rich(message).getHeaderAs("test", String.class));
|
assertEquals("1.4", message.getHeaderAs("test", String.class,camel.context()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void shouldReturnSubsetOfHeaders() {
|
@Test public void shouldReturnSubsetOfHeaders() {
|
||||||
Message message = message("test" , createMap("A", "1", "B", "2"));
|
CamelMessage message = message("test" , createMap("A", "1", "B", "2"));
|
||||||
assertEquals(createMap("B", "2"), message.getHeaders(createSet("B")));
|
assertEquals(createMap("B", "2"), message.getHeaders(createSet("B")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected=UnsupportedOperationException.class)
|
@Test(expected=UnsupportedOperationException.class)
|
||||||
public void shouldReturnSubsetOfHeadersUnmodifiable() {
|
public void shouldReturnSubsetOfHeadersUnmodifiable() {
|
||||||
Message message = message("test" , createMap("A", "1", "B", "2"));
|
CamelMessage message = message("test" , createMap("A", "1", "B", "2"));
|
||||||
message.getHeaders(createSet("B")).put("x", "y");
|
message.getHeaders(createSet("B")).put("x", "y");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void shouldReturnAllHeaders() {
|
@Test public void shouldReturnAllHeaders() {
|
||||||
Message message = message("test" , createMap("A", "1", "B", "2"));
|
CamelMessage message = message("test" , createMap("A", "1", "B", "2"));
|
||||||
assertEquals(createMap("A", "1", "B", "2"), message.getHeaders());
|
assertEquals(createMap("A", "1", "B", "2"), message.getHeaders());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected=UnsupportedOperationException.class)
|
@Test(expected=UnsupportedOperationException.class)
|
||||||
public void shouldReturnAllHeadersUnmodifiable() {
|
public void shouldReturnAllHeadersUnmodifiable() {
|
||||||
Message message = message("test" , createMap("A", "1", "B", "2"));
|
CamelMessage message = message("test" , createMap("A", "1", "B", "2"));
|
||||||
message.getHeaders().put("x", "y");
|
message.getHeaders().put("x", "y");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,7 +90,7 @@ public class MessageJavaTestBase {
|
||||||
@Test public void shouldConvertBodyAndPreserveHeaders() {
|
@Test public void shouldConvertBodyAndPreserveHeaders() {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
message("1.4", createMap("A", "1")),
|
message("1.4", createMap("A", "1")),
|
||||||
rich(message(1.4 , createMap("A", "1"))).withBodyAs(String.class));
|
message(1.4 , createMap("A", "1")).withBodyAs(String.class,camel.context()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void shouldSetBodyAndPreserveHeaders() {
|
@Test public void shouldSetBodyAndPreserveHeaders() {
|
||||||
|
|
@ -109,13 +108,13 @@ public class MessageJavaTestBase {
|
||||||
@Test public void shouldAddHeaderAndPreserveBodyAndHeaders() {
|
@Test public void shouldAddHeaderAndPreserveBodyAndHeaders() {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
message("test1" , createMap("A", "1", "B", "2")),
|
message("test1" , createMap("A", "1", "B", "2")),
|
||||||
message("test1" , createMap("A", "1")).plusHeader("B", "2"));
|
message("test1" , createMap("A", "1")).addHeader("B", "2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void shouldAddHeadersAndPreserveBodyAndHeaders() {
|
@Test public void shouldAddHeadersAndPreserveBodyAndHeaders() {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
message("test1" , createMap("A", "1", "B", "2")),
|
message("test1" , createMap("A", "1", "B", "2")),
|
||||||
message("test1" , createMap("A", "1")).plusHeaders(createMap("B", "2")));
|
message("test1" , createMap("A", "1")).addHeaders(createMap("B", "2")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void shouldRemoveHeadersAndPreserveBodyAndRemainingHeaders() {
|
@Test public void shouldRemoveHeadersAndPreserveBodyAndRemainingHeaders() {
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,8 @@ public class SampleErrorHandlingConsumer extends UntypedConsumerActor {
|
||||||
|
|
||||||
|
|
||||||
public void onReceive(Object message) throws Exception {
|
public void onReceive(Object message) throws Exception {
|
||||||
Message msg = (Message) message;
|
CamelMessage msg = (CamelMessage) message;
|
||||||
String body = rich(msg).getBodyAs(String.class);
|
String body = msg.getBodyAs(String.class,this.getCamelContext());
|
||||||
throw new Exception(String.format("error: %s", body));
|
throw new Exception(String.format("error: %s", body));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@ public class SampleUntypedConsumer extends UntypedConsumerActor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onReceive(Object message) {
|
public void onReceive(Object message) {
|
||||||
RichMessage msg = rich((Message)message);
|
CamelMessage msg = (CamelMessage)message;
|
||||||
String body = msg.getBodyAs(String.class);
|
String body = msg.getBodyAs(String.class, getCamelContext());
|
||||||
String header = msg.getHeaderAs("test", String.class);
|
String header = msg.getHeaderAs("test", String.class,getCamelContext());
|
||||||
sender().tell(String.format("%s %s", body, header));
|
sender().tell(String.format("%s %s", body, header));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ public class SampleUntypedForwardingProducer extends UntypedProducerActor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceiveAfterProduce(Object message) {
|
public void onReceiveAfterProduce(Object message) {
|
||||||
RichMessage msg = rich((Message)message);
|
CamelMessage msg = (CamelMessage)message;
|
||||||
String body = msg.getBodyAs(String.class);
|
String body = msg.getBodyAs(String.class,getCamelContext());
|
||||||
getProducerTemplate().sendBody("direct:forward-test-1", body);
|
getProducerTemplate().sendBody("direct:forward-test-1", body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,10 @@ import akka.util.duration._
|
||||||
import org.apache.camel.ProducerTemplate
|
import org.apache.camel.ProducerTemplate
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import java.util.concurrent.{ TimeUnit, CountDownLatch }
|
|
||||||
import TestSupport._
|
import TestSupport._
|
||||||
import org.scalatest.WordSpec
|
import org.scalatest.WordSpec
|
||||||
|
import akka.testkit.TestLatch
|
||||||
|
import akka.dispatch.Await
|
||||||
|
|
||||||
class ActivationIntegrationTest extends WordSpec with MustMatchers with SharedCamelSystem {
|
class ActivationIntegrationTest extends WordSpec with MustMatchers with SharedCamelSystem {
|
||||||
implicit val timeout = Timeout(10 seconds)
|
implicit val timeout = Timeout(10 seconds)
|
||||||
|
|
@ -31,21 +32,21 @@ class ActivationIntegrationTest extends WordSpec with MustMatchers with SharedCa
|
||||||
}
|
}
|
||||||
|
|
||||||
"ActivationAware must be notified when endpoint is de-activated" in {
|
"ActivationAware must be notified when endpoint is de-activated" in {
|
||||||
val stopped = new CountDownLatch(1)
|
val latch = TestLatch()
|
||||||
val actor = start(new Consumer {
|
val actor = start(new Consumer {
|
||||||
def endpointUri = "direct:a3"
|
def endpointUri = "direct:a3"
|
||||||
def receive = { case _ ⇒ {} }
|
def receive = { case _ ⇒ {} }
|
||||||
|
|
||||||
override def postStop() = {
|
override def postStop() = {
|
||||||
super.postStop()
|
super.postStop()
|
||||||
stopped.countDown()
|
latch.countDown()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
camel.awaitActivation(actor, 1 second)
|
camel.awaitActivation(actor, 1 second)
|
||||||
|
|
||||||
system.stop(actor)
|
system.stop(actor)
|
||||||
camel.awaitDeactivation(actor, 1 second)
|
camel.awaitDeactivation(actor, 1 second)
|
||||||
if (!stopped.await(1, TimeUnit.SECONDS)) fail("Actor must have stopped quickly after deactivation!")
|
Await.ready(latch, 1 second)
|
||||||
}
|
}
|
||||||
|
|
||||||
"ActivationAware must time out when waiting for endpoint de-activation for too long" in {
|
"ActivationAware must time out when waiting for endpoint de-activation for too long" in {
|
||||||
|
|
@ -66,7 +67,7 @@ class ActivationIntegrationTest extends WordSpec with MustMatchers with SharedCa
|
||||||
class TestConsumer(uri: String) extends Consumer {
|
class TestConsumer(uri: String) extends Consumer {
|
||||||
def endpointUri = uri
|
def endpointUri = uri
|
||||||
override def receive = {
|
override def receive = {
|
||||||
case msg: Message ⇒ sender ! "received " + msg.body
|
case msg: CamelMessage ⇒ sender ! "received " + msg.body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,19 +26,23 @@ class CamelExchangeAdapterTest extends FunSuite with SharedCamelSystem with Mess
|
||||||
}
|
}
|
||||||
|
|
||||||
test("mustSetOutMessageFromResponseMessage") {
|
test("mustSetOutMessageFromResponseMessage") {
|
||||||
val e1 = sampleInOut; e1.setResponse(Message("y"))
|
val e1 = sampleInOut
|
||||||
|
e1.setResponse(Message("y"))
|
||||||
assert(e1.getOut.getBody === "y")
|
assert(e1.getOut.getBody === "y")
|
||||||
}
|
}
|
||||||
|
|
||||||
test("mustSetInMessageFromResponseMessage") {
|
test("mustSetInMessageFromResponseMessage") {
|
||||||
val e1 = sampleInOnly; e1.setResponse(Message("x"))
|
val e1 = sampleInOnly
|
||||||
|
e1.setResponse(Message("x"))
|
||||||
assert(e1.getIn.getBody === "x")
|
assert(e1.getIn.getBody === "x")
|
||||||
}
|
}
|
||||||
|
|
||||||
test("mustSetExceptionFromFailureMessage") {
|
test("mustSetExceptionFromFailureMessage") {
|
||||||
val e1 = sampleInOnly; e1.setFailure(Failure(new Exception("test1")))
|
val e1 = sampleInOnly
|
||||||
|
e1.setFailure(Failure(new Exception("test1")))
|
||||||
assert(e1.getException.getMessage === "test1")
|
assert(e1.getException.getMessage === "test1")
|
||||||
val e2 = sampleInOut; e2.setFailure(Failure(new Exception("test2")))
|
val e2 = sampleInOut
|
||||||
|
e2.setFailure(Failure(new Exception("test2")))
|
||||||
assert(e2.getException.getMessage === "test2")
|
assert(e2.getException.getMessage === "test2")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,32 +5,31 @@
|
||||||
package akka.camel
|
package akka.camel
|
||||||
|
|
||||||
import org.apache.camel.impl.{ DefaultExchange, DefaultMessage }
|
import org.apache.camel.impl.{ DefaultExchange, DefaultMessage }
|
||||||
import org.apache.camel.{ Message ⇒ CamelMessage }
|
|
||||||
import akka.camel.TestSupport.SharedCamelSystem
|
import akka.camel.TestSupport.SharedCamelSystem
|
||||||
import org.scalatest.matchers.MustMatchers
|
import org.scalatest.matchers.MustMatchers
|
||||||
import org.scalatest.WordSpec
|
import org.scalatest.WordSpec
|
||||||
|
|
||||||
//TODO merge it with MessageScalaTest
|
//TODO merge it with MessageScalaTest
|
||||||
class MessageTest extends MustMatchers with WordSpec with SharedCamelSystem {
|
class CamelMessageTest extends MustMatchers with WordSpec with SharedCamelSystem {
|
||||||
|
|
||||||
"Message" must {
|
"CamelMessage" must {
|
||||||
|
|
||||||
"overwrite body and add header" in {
|
"overwrite body and add header" in {
|
||||||
val msg = sampleMessage
|
val msg = sampleMessage
|
||||||
Message("blah", Map("key" -> "baz")).copyContentTo(msg)
|
CamelMessage("blah", Map("key" -> "baz")).copyContentTo(msg)
|
||||||
assert(msg.getBody === "blah")
|
assert(msg.getBody === "blah")
|
||||||
assert(msg.getHeader("foo") === "bar")
|
assert(msg.getHeader("foo") === "bar")
|
||||||
assert(msg.getHeader("key") === "baz")
|
assert(msg.getHeader("key") === "baz")
|
||||||
}
|
}
|
||||||
|
|
||||||
"create message with body and header" in {
|
"create message with body and header" in {
|
||||||
val m = Message.from(sampleMessage)
|
val m = CamelMessage.from(sampleMessage)
|
||||||
assert(m.body === "test")
|
assert(m.body === "test")
|
||||||
assert(m.headers("foo") === "bar")
|
assert(m.headers("foo") === "bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
"create message with body and header and custom header" in {
|
"create message with body and header and custom header" in {
|
||||||
val m = Message.from(sampleMessage, Map("key" -> "baz"))
|
val m = CamelMessage.from(sampleMessage, Map("key" -> "baz"))
|
||||||
assert(m.body === "test")
|
assert(m.body === "test")
|
||||||
assert(m.headers("foo") === "bar")
|
assert(m.headers("foo") === "bar")
|
||||||
assert(m.headers("key") === "baz")
|
assert(m.headers("key") === "baz")
|
||||||
|
|
@ -7,29 +7,30 @@ package akka.camel
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import org.scalatest.matchers.MustMatchers
|
import org.scalatest.matchers.MustMatchers
|
||||||
import akka.util.duration._
|
import akka.util.duration._
|
||||||
import java.util.concurrent.TimeUnit._
|
|
||||||
import TestSupport._
|
import TestSupport._
|
||||||
import org.scalatest.WordSpec
|
import org.scalatest.WordSpec
|
||||||
import org.apache.camel.model.RouteDefinition
|
import org.apache.camel.model.RouteDefinition
|
||||||
import org.apache.camel.builder.Builder
|
import org.apache.camel.builder.Builder
|
||||||
import org.apache.camel.{ FailedToCreateRouteException, CamelExecutionException }
|
import org.apache.camel.{ FailedToCreateRouteException, CamelExecutionException }
|
||||||
import java.util.concurrent.{ ExecutionException, TimeUnit, TimeoutException, CountDownLatch }
|
import java.util.concurrent.{ ExecutionException, TimeUnit, TimeoutException }
|
||||||
|
import akka.testkit.TestLatch
|
||||||
|
import akka.dispatch.Await
|
||||||
|
|
||||||
class ConsumerIntegrationTest extends WordSpec with MustMatchers with NonSharedCamelSystem {
|
class ConsumerIntegrationTest extends WordSpec with MustMatchers with NonSharedCamelSystem {
|
||||||
|
private val defaultTimeout = 10
|
||||||
"Consumer must throw FailedToCreateRouteException, while awaiting activation, if endpoint is invalid" in {
|
"Consumer must throw FailedToCreateRouteException, while awaiting activation, if endpoint is invalid" in {
|
||||||
val actorRef = system.actorOf(Props(new TestActor(uri = "some invalid uri")))
|
val actorRef = system.actorOf(Props(new TestActor(uri = "some invalid uri")))
|
||||||
|
|
||||||
intercept[FailedToCreateRouteException] {
|
intercept[FailedToCreateRouteException] {
|
||||||
camel.awaitActivation(actorRef, timeout = 1 second)
|
camel.awaitActivation(actorRef, timeout = defaultTimeout seconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"Consumer must support in-out messaging" in {
|
"Consumer must support in-out messaging" in {
|
||||||
start(new Consumer {
|
start(new Consumer {
|
||||||
def endpointUri = "direct:a1"
|
def endpointUri = "direct:a1"
|
||||||
protected def receive = {
|
def receive = {
|
||||||
case m: Message ⇒ sender ! "received " + m.bodyAs[String]
|
case m: CamelMessage ⇒ sender ! "received " + m.bodyAs[String]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
camel.sendTo("direct:a1", msg = "some message") must be("received some message")
|
camel.sendTo("direct:a1", msg = "some message") must be("received some message")
|
||||||
|
|
@ -43,7 +44,7 @@ class ConsumerIntegrationTest extends WordSpec with MustMatchers with NonSharedC
|
||||||
override def replyTimeout = SHORT_TIMEOUT
|
override def replyTimeout = SHORT_TIMEOUT
|
||||||
|
|
||||||
def endpointUri = "direct:a3"
|
def endpointUri = "direct:a3"
|
||||||
protected def receive = { case _ ⇒ { Thread.sleep(LONG_WAIT.toMillis); sender ! "done" } }
|
def receive = { case _ ⇒ { Thread.sleep(LONG_WAIT.toMillis); sender ! "done" } }
|
||||||
})
|
})
|
||||||
|
|
||||||
val exception = intercept[CamelExecutionException] {
|
val exception = intercept[CamelExecutionException] {
|
||||||
|
|
@ -53,13 +54,13 @@ class ConsumerIntegrationTest extends WordSpec with MustMatchers with NonSharedC
|
||||||
}
|
}
|
||||||
|
|
||||||
"Consumer must process messages even after actor restart" in {
|
"Consumer must process messages even after actor restart" in {
|
||||||
val restarted = new CountDownLatch(1)
|
val restarted = TestLatch()
|
||||||
val consumer = start(new Consumer {
|
val consumer = start(new Consumer {
|
||||||
def endpointUri = "direct:a2"
|
def endpointUri = "direct:a2"
|
||||||
|
|
||||||
protected def receive = {
|
def receive = {
|
||||||
case "throw" ⇒ throw new Exception
|
case "throw" ⇒ throw new Exception
|
||||||
case m: Message ⇒ sender ! "received " + m.bodyAs[String]
|
case m: CamelMessage ⇒ sender ! "received " + m.bodyAs[String]
|
||||||
}
|
}
|
||||||
|
|
||||||
override def postRestart(reason: Throwable) {
|
override def postRestart(reason: Throwable) {
|
||||||
|
|
@ -67,7 +68,7 @@ class ConsumerIntegrationTest extends WordSpec with MustMatchers with NonSharedC
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
consumer ! "throw"
|
consumer ! "throw"
|
||||||
if (!restarted.await(1, SECONDS)) fail("Actor failed to restart!")
|
Await.ready(restarted, defaultTimeout seconds)
|
||||||
|
|
||||||
val response = camel.sendTo("direct:a2", msg = "xyz")
|
val response = camel.sendTo("direct:a2", msg = "xyz")
|
||||||
response must be("received xyz")
|
response must be("received xyz")
|
||||||
|
|
@ -75,12 +76,12 @@ class ConsumerIntegrationTest extends WordSpec with MustMatchers with NonSharedC
|
||||||
|
|
||||||
"Consumer must unregister itself when stopped" in {
|
"Consumer must unregister itself when stopped" in {
|
||||||
val consumer = start(new TestActor())
|
val consumer = start(new TestActor())
|
||||||
camel.awaitActivation(consumer, 1 second)
|
camel.awaitActivation(consumer, defaultTimeout seconds)
|
||||||
|
|
||||||
camel.routeCount must be > (0)
|
camel.routeCount must be > (0)
|
||||||
|
|
||||||
system.stop(consumer)
|
system.stop(consumer)
|
||||||
camel.awaitDeactivation(consumer, 1 second)
|
camel.awaitDeactivation(consumer, defaultTimeout seconds)
|
||||||
|
|
||||||
camel.routeCount must be(0)
|
camel.routeCount must be(0)
|
||||||
}
|
}
|
||||||
|
|
@ -106,20 +107,20 @@ class ConsumerIntegrationTest extends WordSpec with MustMatchers with NonSharedC
|
||||||
"Consumer supports manual Ack" in {
|
"Consumer supports manual Ack" in {
|
||||||
start(new ManualAckConsumer() {
|
start(new ManualAckConsumer() {
|
||||||
def endpointUri = "direct:manual-ack"
|
def endpointUri = "direct:manual-ack"
|
||||||
protected def receive = { case _ ⇒ sender ! Ack }
|
def receive = { case _ ⇒ sender ! Ack }
|
||||||
})
|
})
|
||||||
camel.template.asyncSendBody("direct:manual-ack", "some message").get(1, TimeUnit.SECONDS) must be(null) //should not timeout
|
camel.template.asyncSendBody("direct:manual-ack", "some message").get(defaultTimeout, TimeUnit.SECONDS) must be(null) //should not timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
"Consumer handles manual Ack failure" in {
|
"Consumer handles manual Ack failure" in {
|
||||||
val someException = new Exception("e1")
|
val someException = new Exception("e1")
|
||||||
start(new ManualAckConsumer() {
|
start(new ManualAckConsumer() {
|
||||||
def endpointUri = "direct:manual-ack"
|
def endpointUri = "direct:manual-ack"
|
||||||
protected def receive = { case _ ⇒ sender ! Failure(someException) }
|
def receive = { case _ ⇒ sender ! Failure(someException) }
|
||||||
})
|
})
|
||||||
|
|
||||||
intercept[ExecutionException] {
|
intercept[ExecutionException] {
|
||||||
camel.template.asyncSendBody("direct:manual-ack", "some message").get(1, TimeUnit.SECONDS)
|
camel.template.asyncSendBody("direct:manual-ack", "some message").get(defaultTimeout, TimeUnit.SECONDS)
|
||||||
}.getCause.getCause must be(someException)
|
}.getCause.getCause must be(someException)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,25 +128,25 @@ class ConsumerIntegrationTest extends WordSpec with MustMatchers with NonSharedC
|
||||||
start(new ManualAckConsumer() {
|
start(new ManualAckConsumer() {
|
||||||
override def replyTimeout = 10 millis
|
override def replyTimeout = 10 millis
|
||||||
def endpointUri = "direct:manual-ack"
|
def endpointUri = "direct:manual-ack"
|
||||||
protected def receive = { case _ ⇒ }
|
def receive = { case _ ⇒ }
|
||||||
})
|
})
|
||||||
|
|
||||||
intercept[ExecutionException] {
|
intercept[ExecutionException] {
|
||||||
camel.template.asyncSendBody("direct:manual-ack", "some message").get(1, TimeUnit.SECONDS)
|
camel.template.asyncSendBody("direct:manual-ack", "some message").get(defaultTimeout, TimeUnit.SECONDS)
|
||||||
}.getCause.getCause.getMessage must include("Failed to get Ack")
|
}.getCause.getCause.getMessage must include("Failed to get Ack")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ErrorThrowingConsumer(override val endpointUri: String) extends Consumer {
|
class ErrorThrowingConsumer(override val endpointUri: String) extends Consumer {
|
||||||
def receive = {
|
def receive = {
|
||||||
case msg: Message ⇒ throw new Exception("error: %s" format msg.body)
|
case msg: CamelMessage ⇒ throw new Exception("error: %s" format msg.body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FailingOnceConsumer(override val endpointUri: String) extends Consumer {
|
class FailingOnceConsumer(override val endpointUri: String) extends Consumer {
|
||||||
|
|
||||||
def receive = {
|
def receive = {
|
||||||
case msg: Message ⇒
|
case msg: CamelMessage ⇒
|
||||||
if (msg.headerAs[Boolean]("CamelRedelivered").getOrElse(false))
|
if (msg.headerAs[Boolean]("CamelRedelivered").getOrElse(false))
|
||||||
sender ! ("accepted: %s" format msg.body)
|
sender ! ("accepted: %s" format msg.body)
|
||||||
else
|
else
|
||||||
|
|
@ -155,5 +156,16 @@ class FailingOnceConsumer(override val endpointUri: String) extends Consumer {
|
||||||
|
|
||||||
class TestActor(uri: String = "file://target/abcde") extends Consumer {
|
class TestActor(uri: String = "file://target/abcde") extends Consumer {
|
||||||
def endpointUri = uri
|
def endpointUri = uri
|
||||||
protected def receive = { case _ ⇒ /* do nothing */ }
|
def receive = { case _ ⇒ /* do nothing */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ErrorPassing {
|
||||||
|
self: Actor ⇒
|
||||||
|
final override def preRestart(reason: Throwable, message: Option[Any]) {
|
||||||
|
sender ! Failure(reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ManualAckConsumer extends Consumer {
|
||||||
|
override def autoack = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import org.scalatest.FunSuite
|
||||||
import org.scalatest.matchers.MustMatchers
|
import org.scalatest.matchers.MustMatchers
|
||||||
|
|
||||||
class MessageScalaTest extends FunSuite with MustMatchers with SharedCamelSystem with MessageSugar {
|
class MessageScalaTest extends FunSuite with MustMatchers with SharedCamelSystem with MessageSugar {
|
||||||
|
implicit def camelContext = camel.context
|
||||||
test("mustConvertDoubleBodyToString") {
|
test("mustConvertDoubleBodyToString") {
|
||||||
Message(1.4).bodyAs[String] must be("1.4")
|
Message(1.4).bodyAs[String] must be("1.4")
|
||||||
}
|
}
|
||||||
|
|
@ -59,13 +59,13 @@ class MessageScalaTest extends FunSuite with MustMatchers with SharedCamelSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
test("mustAddHeaderAndPreserveBodyAndHeaders") {
|
test("mustAddHeaderAndPreserveBodyAndHeaders") {
|
||||||
Message("test1", Map("A" -> "1")).plusHeader("B" -> "2") must be(
|
Message("test1", Map("A" -> "1")).addHeader("B" -> "2") must be(
|
||||||
Message("test1", Map("A" -> "1", "B" -> "2")))
|
Message("test1", Map("A" -> "1", "B" -> "2")))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("mustAddHeadersAndPreserveBodyAndHeaders") {
|
test("mustAddHeadersAndPreserveBodyAndHeaders") {
|
||||||
Message("test1", Map("A" -> "1")).plusHeaders(Map("B" -> "2")) must be(
|
Message("test1", Map("A" -> "1")).addHeaders(Map("B" -> "2")) must be(
|
||||||
Message("test1", Map("A" -> "1", "B" -> "2")))
|
Message("test1", Map("A" -> "1", "B" -> "2")))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,17 @@ package akka.camel
|
||||||
import org.apache.camel.{ Exchange, Processor }
|
import org.apache.camel.{ Exchange, Processor }
|
||||||
import org.apache.camel.builder.RouteBuilder
|
import org.apache.camel.builder.RouteBuilder
|
||||||
import org.apache.camel.component.mock.MockEndpoint
|
import org.apache.camel.component.mock.MockEndpoint
|
||||||
import org.scalatest.{ GivenWhenThen, BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec }
|
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import akka.pattern._
|
import akka.pattern._
|
||||||
import akka.dispatch.Await
|
import akka.dispatch.Await
|
||||||
import akka.util.duration._
|
import akka.util.duration._
|
||||||
import akka.camel.TestSupport.SharedCamelSystem
|
import akka.camel.TestSupport.SharedCamelSystem
|
||||||
|
import org.scalatest._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the features of the Camel Producer.
|
* Tests the features of the Camel Producer.
|
||||||
*/
|
*/
|
||||||
class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach with SharedCamelSystem with GivenWhenThen {
|
class ProducerFeatureTest extends WordSpec with BeforeAndAfterAll with BeforeAndAfterEach with SharedCamelSystem with GivenWhenThen {
|
||||||
|
|
||||||
import ProducerFeatureTest._
|
import ProducerFeatureTest._
|
||||||
|
|
||||||
|
|
@ -31,158 +31,153 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
||||||
|
|
||||||
override protected def afterEach { mockEndpoint.reset() }
|
override protected def afterEach { mockEndpoint.reset() }
|
||||||
|
|
||||||
feature("Producer on a sync Camel route") {
|
"A Producer on a sync Camel route" must {
|
||||||
|
|
||||||
scenario("produces a message and receives normal response") {
|
"produce a message and receive normal response" in {
|
||||||
given("a registered two-way producer")
|
given("a registered two-way producer")
|
||||||
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-2", true)))
|
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-2", true)))
|
||||||
when("a test message is sent to the producer with ?")
|
when("a test message is sent to the producer with ?")
|
||||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
val message = CamelMessage("test", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
val future = producer.ask(message)(timeout)
|
val future = producer.ask(message)(timeout)
|
||||||
then("a normal response must have been returned by the producer")
|
then("a normal response must have been returned by the producer")
|
||||||
val expected = Message("received TEST", Map(Message.MessageExchangeId -> "123"))
|
val expected = CamelMessage("received TEST", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
Await.result(future, timeout) match {
|
Await.result(future, timeout) match {
|
||||||
case result: Message ⇒ assert(result === expected)
|
case result: CamelMessage ⇒ assert(result === expected)
|
||||||
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces a message and receives failure response") {
|
"produce a message and receive failure response" in {
|
||||||
given("a registered two-way producer")
|
given("a registered two-way producer")
|
||||||
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-2")))
|
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-2")))
|
||||||
|
|
||||||
when("a test message causing an exception is sent to the producer with ?")
|
when("a test message causing an exception is sent to the producer with ?")
|
||||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
val message = CamelMessage("fail", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
val future = producer.ask(message)(timeout)
|
val future = producer.ask(message)(timeout)
|
||||||
Await.result(future, timeout) match {
|
Await.result(future, timeout) match {
|
||||||
case result: Failure ⇒ {
|
case result: Failure ⇒
|
||||||
then("a failure response must have been returned by the producer")
|
then("a failure response must have been returned by the producer")
|
||||||
val expectedFailureText = result.cause.getMessage
|
val expectedFailureText = result.cause.getMessage
|
||||||
val expectedHeaders = result.headers
|
val expectedHeaders = result.headers
|
||||||
assert(expectedFailureText === "failure")
|
assert(expectedFailureText === "failure")
|
||||||
assert(expectedHeaders === Map(Message.MessageExchangeId -> "123"))
|
assert(expectedHeaders === Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
}
|
|
||||||
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces message oneway") {
|
"produce a message oneway" in {
|
||||||
given("a registered one-way producer")
|
given("a registered one-way producer")
|
||||||
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-1", true) with Oneway))
|
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-1", true) with Oneway))
|
||||||
|
|
||||||
when("a test message is sent to the producer with !")
|
when("a test message is sent to the producer with !")
|
||||||
mockEndpoint.expectedBodiesReceived("TEST")
|
mockEndpoint.expectedBodiesReceived("TEST")
|
||||||
producer ! Message("test", Map())
|
producer ! CamelMessage("test", Map())
|
||||||
|
|
||||||
then("the test message must have been sent to mock:mock")
|
then("the test message must have been sent to mock:mock")
|
||||||
mockEndpoint.assertIsSatisfied()
|
mockEndpoint.assertIsSatisfied()
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces message twoway without sender reference") {
|
"produces message twoway without sender reference" in {
|
||||||
given("a registered two-way producer")
|
given("a registered two-way producer")
|
||||||
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-1")))
|
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-1")))
|
||||||
|
|
||||||
when("a test message is sent to the producer with !")
|
when("a test message is sent to the producer with !")
|
||||||
mockEndpoint.expectedBodiesReceived("test")
|
mockEndpoint.expectedBodiesReceived("test")
|
||||||
producer ! Message("test", Map())
|
producer ! CamelMessage("test", Map())
|
||||||
|
|
||||||
then("there must be only a warning that there's no sender reference")
|
then("there must be only a warning that there's no sender reference")
|
||||||
mockEndpoint.assertIsSatisfied()
|
mockEndpoint.assertIsSatisfied()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
feature("Producer on an async Camel route") {
|
"A Producer on an async Camel route" must {
|
||||||
|
|
||||||
scenario("produces message to direct:producer-test-3 and receives normal response") {
|
"produce message to direct:producer-test-3 and receive normal response" in {
|
||||||
given("a registered two-way producer")
|
given("a registered two-way producer")
|
||||||
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-3")))
|
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-3")))
|
||||||
|
|
||||||
when("a test message is sent to the producer with ?")
|
when("a test message is sent to the producer with ?")
|
||||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
val message = CamelMessage("test", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
val future = producer.ask(message)(timeout)
|
val future = producer.ask(message)(timeout)
|
||||||
|
|
||||||
Await.result(future, timeout) match {
|
Await.result(future, timeout) match {
|
||||||
case result: Message ⇒ {
|
case result: CamelMessage ⇒
|
||||||
then("a normal response must have been returned by the producer")
|
then("a normal response must have been returned by the producer")
|
||||||
val expected = Message("received test", Map(Message.MessageExchangeId -> "123"))
|
val expected = CamelMessage("received test", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
assert(result === expected)
|
assert(result === expected)
|
||||||
}
|
|
||||||
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces message to direct:producer-test-3 and receives failure response") {
|
"produce message to direct:producer-test-3 and receive failure response" in {
|
||||||
given("a registered two-way producer")
|
given("a registered two-way producer")
|
||||||
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-3")))
|
val producer = system.actorOf(Props(new TestProducer("direct:producer-test-3")))
|
||||||
|
|
||||||
when("a test message causing an exception is sent to the producer with ?")
|
when("a test message causing an exception is sent to the producer with ?")
|
||||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
val message = CamelMessage("fail", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
val future = producer.ask(message)(timeout)
|
val future = producer.ask(message)(timeout)
|
||||||
Await.result(future, timeout) match {
|
Await.result(future, timeout) match {
|
||||||
case result: Failure ⇒ {
|
case result: Failure ⇒
|
||||||
then("a failure response must have been returned by the producer")
|
then("a failure response must have been returned by the producer")
|
||||||
val expectedFailureText = result.cause.getMessage
|
val expectedFailureText = result.cause.getMessage
|
||||||
val expectedHeaders = result.headers
|
val expectedHeaders = result.headers
|
||||||
assert(expectedFailureText === "failure")
|
assert(expectedFailureText === "failure")
|
||||||
assert(expectedHeaders === Map(Message.MessageExchangeId -> "123"))
|
assert(expectedHeaders === Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
}
|
|
||||||
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces message, forwards normal response of direct:producer-test-2 to a replying target actor and receives response") {
|
"produce message, forward normal response of direct:producer-test-2 to a replying target actor and receive response" in {
|
||||||
given("a registered two-way producer configured with a forward target")
|
given("a registered two-way producer configured with a forward target")
|
||||||
val target = system.actorOf(Props[ReplyingForwardTarget])
|
val target = system.actorOf(Props[ReplyingForwardTarget])
|
||||||
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-2", target)))
|
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-2", target)))
|
||||||
|
|
||||||
when("a test message is sent to the producer with ?")
|
when("a test message is sent to the producer with ?")
|
||||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
val message = CamelMessage("test", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
val future = producer.ask(message)(timeout)
|
val future = producer.ask(message)(timeout)
|
||||||
|
|
||||||
Await.result(future, timeout) match {
|
Await.result(future, timeout) match {
|
||||||
case result: Message ⇒ {
|
case result: CamelMessage ⇒
|
||||||
then("a normal response must have been returned by the forward target")
|
then("a normal response must have been returned by the forward target")
|
||||||
val expected = Message("received test", Map(Message.MessageExchangeId -> "123", "test" -> "result"))
|
val expected = CamelMessage("received test", Map(CamelMessage.MessageExchangeId -> "123", "test" -> "result"))
|
||||||
assert(result === expected)
|
assert(result === expected)
|
||||||
}
|
|
||||||
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces message, forwards failure response of direct:producer-test-2 to a replying target actor and receives response") {
|
"produce message, forward failure response of direct:producer-test-2 to a replying target actor and receive response" in {
|
||||||
given("a registered two-way producer configured with a forward target")
|
given("a registered two-way producer configured with a forward target")
|
||||||
val target = system.actorOf(Props[ReplyingForwardTarget])
|
val target = system.actorOf(Props[ReplyingForwardTarget])
|
||||||
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-2", target)))
|
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-2", target)))
|
||||||
|
|
||||||
when("a test message causing an exception is sent to the producer with ?")
|
when("a test message causing an exception is sent to the producer with ?")
|
||||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
val message = CamelMessage("fail", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
val future = producer.ask(message)(timeout)
|
val future = producer.ask(message)(timeout)
|
||||||
Await.result(future, timeout) match {
|
Await.result(future, timeout) match {
|
||||||
case failure: Failure ⇒ {
|
case failure: Failure ⇒
|
||||||
then("a failure response must have been returned by the forward target")
|
then("a failure response must have been returned by the forward target")
|
||||||
val expectedFailureText = failure.cause.getMessage
|
val expectedFailureText = failure.cause.getMessage
|
||||||
val expectedHeaders = failure.headers
|
val expectedHeaders = failure.headers
|
||||||
assert(expectedFailureText === "failure")
|
assert(expectedFailureText === "failure")
|
||||||
assert(expectedHeaders === Map(Message.MessageExchangeId -> "123", "test" -> "failure"))
|
assert(expectedHeaders === Map(CamelMessage.MessageExchangeId -> "123", "test" -> "failure"))
|
||||||
}
|
|
||||||
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces message, forwards normal response to a producing target actor and produces response to direct:forward-test-1") {
|
"produce message, forward normal response to a producing target actor and produce response to direct:forward-test-1" in {
|
||||||
given("a registered one-way producer configured with a forward target")
|
given("a registered one-way producer configured with a forward target")
|
||||||
val target = system.actorOf(Props[ProducingForwardTarget])
|
val target = system.actorOf(Props[ProducingForwardTarget])
|
||||||
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-2", target)))
|
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-2", target)))
|
||||||
|
|
||||||
when("a test message is sent to the producer with !")
|
when("a test message is sent to the producer with !")
|
||||||
mockEndpoint.expectedBodiesReceived("received test")
|
mockEndpoint.expectedBodiesReceived("received test")
|
||||||
producer.tell(Message("test", Map()), producer)
|
producer.tell(CamelMessage("test", Map()), producer)
|
||||||
|
|
||||||
then("a normal response must have been produced by the forward target")
|
then("a normal response must have been produced by the forward target")
|
||||||
mockEndpoint.assertIsSatisfied()
|
mockEndpoint.assertIsSatisfied()
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces message, forwards failure response to a producing target actor and produces response to direct:forward-test-1") {
|
"produce message, forward failure response to a producing target actor and produce response to direct:forward-test-1" in {
|
||||||
given("a registered one-way producer configured with a forward target")
|
given("a registered one-way producer configured with a forward target")
|
||||||
|
|
||||||
val target = system.actorOf(Props[ProducingForwardTarget])
|
val target = system.actorOf(Props[ProducingForwardTarget])
|
||||||
|
|
@ -191,66 +186,64 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
||||||
when("a test message causing an exception is sent to the producer with !")
|
when("a test message causing an exception is sent to the producer with !")
|
||||||
mockEndpoint.expectedMessageCount(1)
|
mockEndpoint.expectedMessageCount(1)
|
||||||
mockEndpoint.message(0).body().isInstanceOf(classOf[Failure])
|
mockEndpoint.message(0).body().isInstanceOf(classOf[Failure])
|
||||||
producer.tell(Message("fail", Map()), producer)
|
producer.tell(CamelMessage("fail", Map()), producer)
|
||||||
|
|
||||||
then("a failure response must have been produced by the forward target")
|
then("a failure response must have been produced by the forward target")
|
||||||
mockEndpoint.assertIsSatisfied()
|
mockEndpoint.assertIsSatisfied()
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces message, forwards normal response from direct:producer-test-3 to a replying target actor and receives response") {
|
"produce message, forward normal response from direct:producer-test-3 to a replying target actor and receive response" in {
|
||||||
given("a registered two-way producer configured with a forward target")
|
given("a registered two-way producer configured with a forward target")
|
||||||
val target = system.actorOf(Props[ReplyingForwardTarget])
|
val target = system.actorOf(Props[ReplyingForwardTarget])
|
||||||
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-3", target)))
|
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-3", target)))
|
||||||
|
|
||||||
when("a test message is sent to the producer with ?")
|
when("a test message is sent to the producer with ?")
|
||||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
val message = CamelMessage("test", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
|
|
||||||
val future = producer.ask(message)(timeout)
|
val future = producer.ask(message)(timeout)
|
||||||
|
|
||||||
then("a normal response must have been returned by the forward target")
|
then("a normal response must have been returned by the forward target")
|
||||||
Await.result(future, timeout) match {
|
Await.result(future, timeout) match {
|
||||||
case message: Message ⇒ {
|
case message: CamelMessage ⇒
|
||||||
val expected = Message("received test", Map(Message.MessageExchangeId -> "123", "test" -> "result"))
|
val expected = CamelMessage("received test", Map(CamelMessage.MessageExchangeId -> "123", "test" -> "result"))
|
||||||
assert(message === expected)
|
assert(message === expected)
|
||||||
}
|
|
||||||
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces message, forwards failure response from direct:producer-test-3 to a replying target actor and receives response") {
|
"produce message, forward failure response from direct:producer-test-3 to a replying target actor and receive response" in {
|
||||||
given("a registered two-way producer configured with a forward target")
|
given("a registered two-way producer configured with a forward target")
|
||||||
val target = system.actorOf(Props[ReplyingForwardTarget])
|
val target = system.actorOf(Props[ReplyingForwardTarget])
|
||||||
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-3", target)))
|
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-3", target)))
|
||||||
|
|
||||||
when("a test message causing an exception is sent to the producer with !!")
|
when("a test message causing an exception is sent to the producer with !!")
|
||||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
val message = CamelMessage("fail", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
val future = producer.ask(message)(timeout)
|
val future = producer.ask(message)(timeout)
|
||||||
Await.result(future, timeout) match {
|
Await.result(future, timeout) match {
|
||||||
case failure: Failure ⇒ {
|
case failure: Failure ⇒
|
||||||
then("a failure response must have been returned by the forward target")
|
then("a failure response must have been returned by the forward target")
|
||||||
val expectedFailureText = failure.cause.getMessage
|
val expectedFailureText = failure.cause.getMessage
|
||||||
val expectedHeaders = failure.headers
|
val expectedHeaders = failure.headers
|
||||||
assert(expectedFailureText === "failure")
|
assert(expectedFailureText === "failure")
|
||||||
assert(expectedHeaders === Map(Message.MessageExchangeId -> "123", "test" -> "failure"))
|
assert(expectedHeaders === Map(CamelMessage.MessageExchangeId -> "123", "test" -> "failure"))
|
||||||
}
|
|
||||||
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces message, forwards normal response from direct:producer-test-3 to a producing target actor and produces response to direct:forward-test-1") {
|
"produce message, forward normal response from direct:producer-test-3 to a producing target actor and produce response to direct:forward-test-1" in {
|
||||||
given("a registered one-way producer configured with a forward target")
|
given("a registered one-way producer configured with a forward target")
|
||||||
val target = system.actorOf(Props[ProducingForwardTarget])
|
val target = system.actorOf(Props[ProducingForwardTarget])
|
||||||
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-3", target)))
|
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-3", target)))
|
||||||
|
|
||||||
when("a test message is sent to the producer with !")
|
when("a test message is sent to the producer with !")
|
||||||
mockEndpoint.expectedBodiesReceived("received test")
|
mockEndpoint.expectedBodiesReceived("received test")
|
||||||
producer.tell(Message("test", Map()), producer)
|
producer.tell(CamelMessage("test", Map()), producer)
|
||||||
|
|
||||||
then("a normal response must have been produced by the forward target")
|
then("a normal response must have been produced by the forward target")
|
||||||
mockEndpoint.assertIsSatisfied()
|
mockEndpoint.assertIsSatisfied()
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario("produces message, forwards failure response from direct:producer-test-3 to a producing target actor and produces response to direct:forward-test-1") {
|
"produce message, forward failure response from direct:producer-test-3 to a producing target actor and produce response to direct:forward-test-1" in {
|
||||||
given("a registered one-way producer configured with a forward target")
|
given("a registered one-way producer configured with a forward target")
|
||||||
val target = system.actorOf(Props[ProducingForwardTarget])
|
val target = system.actorOf(Props[ProducingForwardTarget])
|
||||||
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-3", target)))
|
val producer = system.actorOf(Props(new TestForwarder("direct:producer-test-3", target)))
|
||||||
|
|
@ -258,7 +251,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
||||||
when("a test message causing an exception is sent to the producer with !")
|
when("a test message causing an exception is sent to the producer with !")
|
||||||
mockEndpoint.expectedMessageCount(1)
|
mockEndpoint.expectedMessageCount(1)
|
||||||
mockEndpoint.message(0).body().isInstanceOf(classOf[Failure])
|
mockEndpoint.message(0).body().isInstanceOf(classOf[Failure])
|
||||||
producer.tell(Message("fail", Map()), producer)
|
producer.tell(CamelMessage("fail", Map()), producer)
|
||||||
|
|
||||||
then("a failure response must have been produced by the forward target")
|
then("a failure response must have been produced by the forward target")
|
||||||
mockEndpoint.assertIsSatisfied()
|
mockEndpoint.assertIsSatisfied()
|
||||||
|
|
@ -274,7 +267,7 @@ object ProducerFeatureTest {
|
||||||
def endpointUri = uri
|
def endpointUri = uri
|
||||||
|
|
||||||
override protected def receiveBeforeProduce = {
|
override protected def receiveBeforeProduce = {
|
||||||
case msg: Message ⇒ if (upper) msg.mapBody {
|
case msg: CamelMessage ⇒ if (upper) msg.mapBody {
|
||||||
body: String ⇒ body.toUpperCase
|
body: String ⇒ body.toUpperCase
|
||||||
}
|
}
|
||||||
else msg
|
else msg
|
||||||
|
|
@ -291,23 +284,20 @@ object ProducerFeatureTest {
|
||||||
|
|
||||||
class TestResponder extends Actor {
|
class TestResponder extends Actor {
|
||||||
protected def receive = {
|
protected def receive = {
|
||||||
case msg: Message ⇒ msg.body match {
|
case msg: CamelMessage ⇒ msg.body match {
|
||||||
case "fail" ⇒ {
|
case "fail" ⇒ context.sender ! (Failure(new Exception("failure"), msg.headers))
|
||||||
context.sender ! (Failure(new Exception("failure"), msg.headers))
|
case _ ⇒
|
||||||
}
|
|
||||||
case bod: Any ⇒ {
|
|
||||||
context.sender ! (msg.mapBody {
|
context.sender ! (msg.mapBody {
|
||||||
body: String ⇒ "received %s" format body
|
body: String ⇒ "received %s" format body
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReplyingForwardTarget extends Actor {
|
class ReplyingForwardTarget extends Actor {
|
||||||
protected def receive = {
|
protected def receive = {
|
||||||
case msg: Message ⇒
|
case msg: CamelMessage ⇒
|
||||||
context.sender ! (msg.plusHeader("test" -> "result"))
|
context.sender ! (msg.addHeader("test" -> "result"))
|
||||||
case msg: Failure ⇒
|
case msg: Failure ⇒
|
||||||
context.sender ! (Failure(msg.cause, msg.headers + ("test" -> "failure")))
|
context.sender ! (Failure(msg.cause, msg.headers + ("test" -> "failure")))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ private[camel] object TestSupport {
|
||||||
|
|
||||||
@deprecated
|
@deprecated
|
||||||
trait MessageSugar {
|
trait MessageSugar {
|
||||||
def Message(body: Any) = akka.camel.Message(body, Map.empty)
|
def Message(body: Any) = akka.camel.CamelMessage(body, Map.empty)
|
||||||
def Message(body: Any, headers: Map[String, Any]) = akka.camel.Message(body, headers)
|
def Message(body: Any, headers: Map[String, Any]) = akka.camel.CamelMessage(body, headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
trait SharedCamelSystem extends BeforeAndAfterAll { this: Suite ⇒
|
trait SharedCamelSystem extends BeforeAndAfterAll { this: Suite ⇒
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,13 @@ class UntypedProducerTest extends WordSpec with BeforeAndAfterAll with BeforeAnd
|
||||||
val producer = system.actorOf(Props[SampleUntypedReplyingProducer])
|
val producer = system.actorOf(Props[SampleUntypedReplyingProducer])
|
||||||
|
|
||||||
when("a test message is sent to the producer with !!")
|
when("a test message is sent to the producer with !!")
|
||||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
val message = CamelMessage("test", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
val future = producer.ask(message)(timeout)
|
val future = producer.ask(message)(timeout)
|
||||||
then("a normal response should have been returned by the producer")
|
then("a normal response should have been returned by the producer")
|
||||||
val expected = Message("received test", Map(Message.MessageExchangeId -> "123"))
|
val expected = CamelMessage("received test", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
Await.result(future, timeout) match {
|
Await.result(future, timeout) match {
|
||||||
case result: Message ⇒ assert(result === expected)
|
case result: CamelMessage ⇒ assert(result === expected)
|
||||||
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ class UntypedProducerTest extends WordSpec with BeforeAndAfterAll with BeforeAnd
|
||||||
val producer = system.actorOf(Props[SampleUntypedReplyingProducer])
|
val producer = system.actorOf(Props[SampleUntypedReplyingProducer])
|
||||||
|
|
||||||
when("a test message causing an exception is sent to the producer with !!")
|
when("a test message causing an exception is sent to the producer with !!")
|
||||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
val message = CamelMessage("fail", Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
val future = producer.ask(message)(timeout)
|
val future = producer.ask(message)(timeout)
|
||||||
then("a failure response should have been returned by the producer")
|
then("a failure response should have been returned by the producer")
|
||||||
Await.result(future, timeout) match {
|
Await.result(future, timeout) match {
|
||||||
|
|
@ -57,7 +57,7 @@ class UntypedProducerTest extends WordSpec with BeforeAndAfterAll with BeforeAnd
|
||||||
val expectedFailureText = result.cause.getMessage
|
val expectedFailureText = result.cause.getMessage
|
||||||
val expectedHeaders = result.headers
|
val expectedHeaders = result.headers
|
||||||
assert(expectedFailureText === "failure")
|
assert(expectedFailureText === "failure")
|
||||||
assert(expectedHeaders === Map(Message.MessageExchangeId -> "123"))
|
assert(expectedHeaders === Map(CamelMessage.MessageExchangeId -> "123"))
|
||||||
}
|
}
|
||||||
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
case unexpected ⇒ fail("Actor responded with unexpected message:" + unexpected)
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +73,7 @@ class UntypedProducerTest extends WordSpec with BeforeAndAfterAll with BeforeAnd
|
||||||
|
|
||||||
when("a test message is sent to the producer with !")
|
when("a test message is sent to the producer with !")
|
||||||
mockEndpoint.expectedBodiesReceived("received test")
|
mockEndpoint.expectedBodiesReceived("received test")
|
||||||
val result = producer.tell(Message("test", Map[String, Any]()), producer)
|
val result = producer.tell(CamelMessage("test", Map[String, Any]()), producer)
|
||||||
|
|
||||||
then("a normal response should have been sent")
|
then("a normal response should have been sent")
|
||||||
mockEndpoint.assertIsSatisfied
|
mockEndpoint.assertIsSatisfied
|
||||||
|
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
package akka.camel.internal
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import akka.camel.internal.Try._
|
|
||||||
import org.scalatest.matchers.MustMatchers
|
|
||||||
import org.scalatest.mock.MockitoSugar
|
|
||||||
import akka.event.LoggingAdapter
|
|
||||||
import org.scalatest.{ BeforeAndAfterEach, WordSpec }
|
|
||||||
|
|
||||||
class TryTest extends WordSpec with MustMatchers with MockitoSugar with BeforeAndAfterEach {
|
|
||||||
|
|
||||||
import org.mockito.Mockito._
|
|
||||||
import org.mockito.Matchers._
|
|
||||||
import org.mockito.Matchers.{ eq ⇒ the }
|
|
||||||
|
|
||||||
implicit var log: LoggingAdapter = _
|
|
||||||
|
|
||||||
override def beforeEach() {
|
|
||||||
log = mock[LoggingAdapter]
|
|
||||||
}
|
|
||||||
|
|
||||||
"Safe executes block" in {
|
|
||||||
var executed = false
|
|
||||||
safe {
|
|
||||||
executed = true
|
|
||||||
}
|
|
||||||
executed must be(true)
|
|
||||||
verifyNoMoreInteractions(log)
|
|
||||||
}
|
|
||||||
|
|
||||||
"Safe swallows exception and logs it" in {
|
|
||||||
safe(throw new Exception)
|
|
||||||
verify(log).warning(any[String], any[Any])
|
|
||||||
}
|
|
||||||
|
|
||||||
"Try-otherwise runs otherwise and throws exception when the first block fails" in {
|
|
||||||
var otherwiseCalled = false
|
|
||||||
intercept[Exception] {
|
|
||||||
Try(throw new Exception) otherwise (otherwiseCalled = true)
|
|
||||||
}
|
|
||||||
otherwiseCalled must be(true)
|
|
||||||
verifyNoMoreInteractions(log)
|
|
||||||
}
|
|
||||||
|
|
||||||
"Try-otherwise swallows exception thrown in otherwise clause and logs it" in {
|
|
||||||
val exceptionFromOtherwise = new RuntimeException("e2")
|
|
||||||
intercept[RuntimeException] {
|
|
||||||
Try(throw new RuntimeException("e1")) otherwise (throw exceptionFromOtherwise)
|
|
||||||
}.getMessage must be("e1")
|
|
||||||
verify(log, only()).warning(any[String], the(exceptionFromOtherwise))
|
|
||||||
}
|
|
||||||
|
|
||||||
"Try-otherwise doesnt run otherwise if first block doesnt fail" in {
|
|
||||||
var otherwiseCalled = false
|
|
||||||
Try(2 + 2) otherwise (otherwiseCalled = true)
|
|
||||||
otherwiseCalled must be(false)
|
|
||||||
verifyNoMoreInteractions(log)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -31,7 +31,7 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
"consumer actor doesnt exist" must {
|
"consumer actor doesnt exist" must {
|
||||||
"set failure message on exchange" in {
|
"set failure message on exchange" in {
|
||||||
producer = given(actor = null)
|
producer = given(actor = null)
|
||||||
producer.process(exchange)
|
producer.processExchangeAdapter(exchange)
|
||||||
|
|
||||||
verify(exchange).setFailure(any[Failure])
|
verify(exchange).setFailure(any[Failure])
|
||||||
}
|
}
|
||||||
|
|
@ -41,12 +41,12 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
def producer = given(outCapable = false)
|
def producer = given(outCapable = false)
|
||||||
|
|
||||||
"pass the message to the consumer" in {
|
"pass the message to the consumer" in {
|
||||||
producer.process(exchange)
|
producer.processExchangeAdapter(exchange)
|
||||||
within(1 second)(probe.expectMsg(message))
|
within(1 second)(probe.expectMsg(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
"not expect response and not block" in {
|
"not expect response and not block" in {
|
||||||
time(producer.process(exchange)) must be < (30 millis)
|
time(producer.processExchangeAdapter(exchange)) must be < (30 millis)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
"get a response" in {
|
"get a response" in {
|
||||||
producer = given(actor = echoActor, outCapable = true)
|
producer = given(actor = echoActor, outCapable = true)
|
||||||
|
|
||||||
producer.process(exchange)
|
producer.processExchangeAdapter(exchange)
|
||||||
|
|
||||||
verify(exchange).setResponse(msg("received " + message))
|
verify(exchange).setResponse(msg("received " + message))
|
||||||
}
|
}
|
||||||
|
|
@ -67,17 +67,17 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
|
|
||||||
def process() = {
|
def process() = {
|
||||||
producer = given(outCapable = true, replyTimeout = 100 millis)
|
producer = given(outCapable = true, replyTimeout = 100 millis)
|
||||||
time(producer.process(exchange))
|
time(producer.processExchangeAdapter(exchange))
|
||||||
}
|
}
|
||||||
|
|
||||||
"timeout after replyTimeout" in {
|
"timeout after replyTimeout" in {
|
||||||
val duration = process()
|
val duration = process()
|
||||||
duration must (be >= (100 millis) and be < (200 millis))
|
duration must (be >= (100 millis) and be < (300 millis))
|
||||||
}
|
}
|
||||||
|
|
||||||
"never set the response on exchange" in {
|
"never set the response on exchange" in {
|
||||||
process()
|
process()
|
||||||
verify(exchange, Mockito.never()).setResponse(any[Message])
|
verify(exchange, Mockito.never()).setResponse(any[CamelMessage])
|
||||||
}
|
}
|
||||||
|
|
||||||
"set failure message to timeout" in {
|
"set failure message to timeout" in {
|
||||||
|
|
@ -95,7 +95,7 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
"asynchronous" when {
|
"asynchronous" when {
|
||||||
|
|
||||||
def verifyFailureIsSet {
|
def verifyFailureIsSet {
|
||||||
producer.process(exchange, asyncCallback)
|
producer.processExchangeAdapter(exchange, asyncCallback)
|
||||||
asyncCallback.awaitCalled()
|
asyncCallback.awaitCalled()
|
||||||
verify(exchange).setFailure(any[Failure])
|
verify(exchange).setFailure(any[Failure])
|
||||||
}
|
}
|
||||||
|
|
@ -113,12 +113,12 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
"get a response and async callback as soon as it gets the response (but not before)" in {
|
"get a response and async callback as soon as it gets the response (but not before)" in {
|
||||||
producer = given(outCapable = true)
|
producer = given(outCapable = true)
|
||||||
|
|
||||||
val doneSync = producer.process(exchange, asyncCallback)
|
val doneSync = producer.processExchangeAdapter(exchange, asyncCallback)
|
||||||
|
|
||||||
asyncCallback.expectNoCallWithin(100 millis); info("no async callback before response")
|
asyncCallback.expectNoCallWithin(100 millis); info("no async callback before response")
|
||||||
|
|
||||||
within(1 second) {
|
within(1 second) {
|
||||||
probe.expectMsgType[Message]
|
probe.expectMsgType[CamelMessage]
|
||||||
probe.sender ! "some message"
|
probe.sender ! "some message"
|
||||||
}
|
}
|
||||||
doneSync must be(false); info("done async")
|
doneSync must be(false); info("done async")
|
||||||
|
|
@ -134,10 +134,10 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
|
|
||||||
producer = given(outCapable = true)
|
producer = given(outCapable = true)
|
||||||
|
|
||||||
producer.process(exchange, asyncCallback)
|
producer.processExchangeAdapter(exchange, asyncCallback)
|
||||||
|
|
||||||
within(1 second) {
|
within(1 second) {
|
||||||
probe.expectMsgType[Message]
|
probe.expectMsgType[CamelMessage]
|
||||||
probe.sender ! failure
|
probe.sender ! failure
|
||||||
asyncCallback.awaitCalled(remaining)
|
asyncCallback.awaitCalled(remaining)
|
||||||
}
|
}
|
||||||
|
|
@ -149,7 +149,7 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
"no response is sent within timeout" must {
|
"no response is sent within timeout" must {
|
||||||
"set TimeoutException on exchange" in {
|
"set TimeoutException on exchange" in {
|
||||||
producer = given(outCapable = true, replyTimeout = 10 millis)
|
producer = given(outCapable = true, replyTimeout = 10 millis)
|
||||||
producer.process(exchange, asyncCallback)
|
producer.processExchangeAdapter(exchange, asyncCallback)
|
||||||
asyncCallback.awaitCalled(100 millis)
|
asyncCallback.awaitCalled(100 millis)
|
||||||
verify(exchange).setFailure(Matchers.argThat(new ArgumentMatcher[Failure] {
|
verify(exchange).setFailure(Matchers.argThat(new ArgumentMatcher[Failure] {
|
||||||
def matches(failure: AnyRef) = { failure.asInstanceOf[Failure].getCause must be(anInstanceOf[TimeoutException]); true }
|
def matches(failure: AnyRef) = { failure.asInstanceOf[Failure].getCause must be(anInstanceOf[TimeoutException]); true }
|
||||||
|
|
@ -174,11 +174,11 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
"get sync callback as soon as it sends a message" in {
|
"get sync callback as soon as it sends a message" in {
|
||||||
|
|
||||||
producer = given(outCapable = false, autoAck = true)
|
producer = given(outCapable = false, autoAck = true)
|
||||||
val doneSync = producer.process(exchange, asyncCallback)
|
val doneSync = producer.processExchangeAdapter(exchange, asyncCallback)
|
||||||
|
|
||||||
doneSync must be(true); info("done sync")
|
doneSync must be(true); info("done sync")
|
||||||
asyncCallback.expectDoneSyncWithin(1 second); info("async callback called")
|
asyncCallback.expectDoneSyncWithin(1 second); info("async callback called")
|
||||||
verify(exchange, never()).setResponse(any[Message]); info("no response forwarded to exchange")
|
verify(exchange, never()).setResponse(any[CamelMessage]); info("no response forwarded to exchange")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -189,15 +189,15 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
"get async callback" in {
|
"get async callback" in {
|
||||||
producer = given(outCapable = false, autoAck = false)
|
producer = given(outCapable = false, autoAck = false)
|
||||||
|
|
||||||
val doneSync = producer.process(exchange, asyncCallback)
|
val doneSync = producer.processExchangeAdapter(exchange, asyncCallback)
|
||||||
|
|
||||||
doneSync must be(false)
|
doneSync must be(false)
|
||||||
within(1 second) {
|
within(1 second) {
|
||||||
probe.expectMsgType[Message]; info("message sent to consumer")
|
probe.expectMsgType[CamelMessage]; info("message sent to consumer")
|
||||||
probe.sender ! Ack
|
probe.sender ! Ack
|
||||||
asyncCallback.expectDoneAsyncWithin(remaining); info("async callback called")
|
asyncCallback.expectDoneAsyncWithin(remaining); info("async callback called")
|
||||||
}
|
}
|
||||||
verify(exchange, never()).setResponse(any[Message]); info("no response forwarded to exchange")
|
verify(exchange, never()).setResponse(any[CamelMessage]); info("no response forwarded to exchange")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,14 +205,14 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
"fail" in {
|
"fail" in {
|
||||||
producer = given(outCapable = false, autoAck = false)
|
producer = given(outCapable = false, autoAck = false)
|
||||||
|
|
||||||
producer.process(exchange, asyncCallback)
|
producer.processExchangeAdapter(exchange, asyncCallback)
|
||||||
|
|
||||||
within(1 second) {
|
within(1 second) {
|
||||||
probe.expectMsgType[Message]; info("message sent to consumer")
|
probe.expectMsgType[CamelMessage]; info("message sent to consumer")
|
||||||
probe.sender ! "some neither Ack nor Failure response"
|
probe.sender ! "some neither Ack nor Failure response"
|
||||||
asyncCallback.expectDoneAsyncWithin(remaining); info("async callback called")
|
asyncCallback.expectDoneAsyncWithin(remaining); info("async callback called")
|
||||||
}
|
}
|
||||||
verify(exchange, never()).setResponse(any[Message]); info("no response forwarded to exchange")
|
verify(exchange, never()).setResponse(any[CamelMessage]); info("no response forwarded to exchange")
|
||||||
verify(exchange).setFailure(any[Failure]); info("failure set")
|
verify(exchange).setFailure(any[Failure]); info("failure set")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -222,7 +222,7 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
"set failure on exchange" in {
|
"set failure on exchange" in {
|
||||||
producer = given(outCapable = false, replyTimeout = 10 millis, autoAck = false)
|
producer = given(outCapable = false, replyTimeout = 10 millis, autoAck = false)
|
||||||
|
|
||||||
producer.process(exchange, asyncCallback)
|
producer.processExchangeAdapter(exchange, asyncCallback)
|
||||||
asyncCallback.awaitCalled(100 millis)
|
asyncCallback.awaitCalled(100 millis)
|
||||||
verify(exchange).setFailure(any[Failure])
|
verify(exchange).setFailure(any[Failure])
|
||||||
|
|
||||||
|
|
@ -233,27 +233,22 @@ class ActorProducerTest extends TestKit(ActorSystem("test")) with WordSpec with
|
||||||
"set an exception on exchange" in {
|
"set an exception on exchange" in {
|
||||||
producer = given(outCapable = false, autoAck = false)
|
producer = given(outCapable = false, autoAck = false)
|
||||||
|
|
||||||
val doneSync = producer.process(exchange, asyncCallback)
|
val doneSync = producer.processExchangeAdapter(exchange, asyncCallback)
|
||||||
|
|
||||||
doneSync must be(false)
|
doneSync must be(false)
|
||||||
within(1 second) {
|
within(1 second) {
|
||||||
probe.expectMsgType[Message]; info("message sent to consumer")
|
probe.expectMsgType[CamelMessage]; info("message sent to consumer")
|
||||||
probe.sender ! Failure(new Exception)
|
probe.sender ! Failure(new Exception)
|
||||||
asyncCallback.awaitCalled(remaining);
|
asyncCallback.awaitCalled(remaining);
|
||||||
}
|
}
|
||||||
verify(exchange, never()).setResponse(any[Message]); info("no response forwarded to exchange")
|
verify(exchange, never()).setResponse(any[CamelMessage]); info("no response forwarded to exchange")
|
||||||
verify(exchange).setFailure(any[Failure]); info("failure set")
|
verify(exchange).setFailure(any[Failure]); info("failure set")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ActorProducerFixture extends MockitoSugar with BeforeAndAfterAll with BeforeAndAfterEach { self: TestKit with MustMatchers with Suite ⇒
|
trait ActorProducerFixture extends MockitoSugar with BeforeAndAfterAll with BeforeAndAfterEach { self: TestKit with MustMatchers with Suite ⇒
|
||||||
|
|
@ -261,11 +256,12 @@ trait ActorProducerFixture extends MockitoSugar with BeforeAndAfterAll with Befo
|
||||||
var exchange: CamelExchangeAdapter = _
|
var exchange: CamelExchangeAdapter = _
|
||||||
var callback: AsyncCallback = _
|
var callback: AsyncCallback = _
|
||||||
|
|
||||||
var producer: ConsumerAsyncProcessor = _
|
var producer: ActorProducer = _
|
||||||
var message: Message = _
|
var message: CamelMessage = _
|
||||||
var probe: TestProbe = _
|
var probe: TestProbe = _
|
||||||
var asyncCallback: TestAsyncCallback = _
|
var asyncCallback: TestAsyncCallback = _
|
||||||
var actorEndpointPath: ActorEndpointPath = _
|
var actorEndpointPath: ActorEndpointPath = _
|
||||||
|
var actorComponent: ActorComponent = _
|
||||||
|
|
||||||
override protected def beforeEach() {
|
override protected def beforeEach() {
|
||||||
asyncCallback = createAsyncCallback
|
asyncCallback = createAsyncCallback
|
||||||
|
|
@ -275,20 +271,20 @@ trait ActorProducerFixture extends MockitoSugar with BeforeAndAfterAll with Befo
|
||||||
exchange = mock[CamelExchangeAdapter]
|
exchange = mock[CamelExchangeAdapter]
|
||||||
callback = mock[AsyncCallback]
|
callback = mock[AsyncCallback]
|
||||||
actorEndpointPath = mock[ActorEndpointPath]
|
actorEndpointPath = mock[ActorEndpointPath]
|
||||||
|
actorComponent = mock[ActorComponent]
|
||||||
producer = new ConsumerAsyncProcessor(config(), camel)
|
producer = new ActorProducer(config(), camel)
|
||||||
message = Message(null, null)
|
message = CamelMessage(null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def afterAll() {
|
override protected def afterAll() {
|
||||||
system.shutdown()
|
system.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
def msg(s: String) = Message(s, Map.empty)
|
def msg(s: String) = CamelMessage(s, Map.empty)
|
||||||
|
|
||||||
def given(actor: ActorRef = probe.ref, outCapable: Boolean = true, autoAck: Boolean = true, replyTimeout: Duration = Int.MaxValue seconds) = {
|
def given(actor: ActorRef = probe.ref, outCapable: Boolean = true, autoAck: Boolean = true, replyTimeout: Duration = Int.MaxValue seconds) = {
|
||||||
prepareMocks(actor, outCapable = outCapable)
|
prepareMocks(actor, outCapable = outCapable)
|
||||||
new ConsumerAsyncProcessor(config(isAutoAck = autoAck, _replyTimeout = replyTimeout), camel)
|
new ActorProducer(config(isAutoAck = autoAck, _replyTimeout = replyTimeout), camel)
|
||||||
}
|
}
|
||||||
|
|
||||||
def createAsyncCallback = new TestAsyncCallback
|
def createAsyncCallback = new TestAsyncCallback
|
||||||
|
|
@ -323,15 +319,13 @@ trait ActorProducerFixture extends MockitoSugar with BeforeAndAfterAll with Befo
|
||||||
}
|
}
|
||||||
|
|
||||||
def config(endpointUri: String = "test-uri", isAutoAck: Boolean = true, _replyTimeout: Duration = Int.MaxValue seconds) = {
|
def config(endpointUri: String = "test-uri", isAutoAck: Boolean = true, _replyTimeout: Duration = Int.MaxValue seconds) = {
|
||||||
new ActorEndpointConfig {
|
val endpoint = new ActorEndpoint(endpointUri, actorComponent, actorEndpointPath, camel)
|
||||||
val path = actorEndpointPath
|
endpoint.autoack = isAutoAck
|
||||||
val getEndpointUri = endpointUri
|
endpoint.replyTimeout = _replyTimeout
|
||||||
autoack = isAutoAck
|
endpoint
|
||||||
replyTimeout = _replyTimeout
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def prepareMocks(actor: ActorRef, message: Message = message, outCapable: Boolean) {
|
def prepareMocks(actor: ActorRef, message: CamelMessage = message, outCapable: Boolean) {
|
||||||
when(actorEndpointPath.findActorIn(any[ActorSystem])) thenReturn Option(actor)
|
when(actorEndpointPath.findActorIn(any[ActorSystem])) thenReturn Option(actor)
|
||||||
when(exchange.toRequestMessage(any[Map[String, Any]])) thenReturn message
|
when(exchange.toRequestMessage(any[Map[String, Any]])) thenReturn message
|
||||||
when(exchange.isOutCapable) thenReturn outCapable
|
when(exchange.isOutCapable) thenReturn outCapable
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue