2010-02-25 17:19:50 +01:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package se.scalablesolutions.akka.camel.component
|
|
|
|
|
|
|
|
|
|
import java.lang.{RuntimeException, String}
|
|
|
|
|
import java.util.{Map => JavaMap}
|
|
|
|
|
|
|
|
|
|
import org.apache.camel.{Exchange, Consumer, Processor}
|
|
|
|
|
import org.apache.camel.impl.{DefaultProducer, DefaultEndpoint, DefaultComponent}
|
|
|
|
|
|
|
|
|
|
import se.scalablesolutions.akka.actor.{ActorRegistry, Actor}
|
2010-03-05 19:38:23 +01:00
|
|
|
import se.scalablesolutions.akka.camel.{CamelMessageConversion, Message}
|
2010-02-25 17:19:50 +01:00
|
|
|
|
|
|
|
|
/**
|
2010-03-05 19:38:23 +01:00
|
|
|
* Camel component for sending messages to and receiving replies from actors.
|
2010-02-25 17:19:50 +01:00
|
|
|
*
|
|
|
|
|
* @see se.scalablesolutions.akka.camel.component.ActorEndpoint
|
|
|
|
|
* @see se.scalablesolutions.akka.camel.component.ActorProducer
|
|
|
|
|
*
|
|
|
|
|
* @author Martin Krasser
|
|
|
|
|
*/
|
|
|
|
|
class ActorComponent extends DefaultComponent {
|
|
|
|
|
def createEndpoint(uri: String, remaining: String, parameters: JavaMap[String, Object]): ActorEndpoint = {
|
|
|
|
|
val idAndUuid = idAndUuidPair(remaining)
|
|
|
|
|
new ActorEndpoint(uri, this, idAndUuid._1, idAndUuid._2)
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-01 06:07:32 +01:00
|
|
|
private def idAndUuidPair(remaining: String): Tuple2[Option[String], Option[String]] = {
|
|
|
|
|
remaining split ":" toList match {
|
|
|
|
|
case id :: Nil => (Some(id), None)
|
|
|
|
|
case "id" :: id :: Nil => (Some(id), None)
|
|
|
|
|
case "uuid" :: uuid :: Nil => (None, Some(uuid))
|
2010-02-25 17:19:50 +01:00
|
|
|
case _ => throw new IllegalArgumentException(
|
2010-03-01 06:07:32 +01:00
|
|
|
"invalid path format: %s - should be <actorid> or id:<actorid> or uuid:<actoruuid>" format remaining)
|
2010-02-25 17:19:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-03-05 19:38:23 +01:00
|
|
|
* Camel endpoint for referencing an actor. The actor reference is given by the endpoint URI.
|
|
|
|
|
* An actor can be referenced by its <code>Actor.getId</code> or its <code>Actor.uuid</code>.
|
|
|
|
|
* Supported endpoint URI formats are
|
2010-03-01 06:07:32 +01:00
|
|
|
* <code>actor:<actorid></code>,
|
|
|
|
|
* <code>actor:id:<actorid></code> and
|
|
|
|
|
* <code>actor:uuid:<actoruuid></code>.
|
2010-02-25 17:19:50 +01:00
|
|
|
*
|
|
|
|
|
* @see se.scalablesolutions.akka.camel.component.ActorComponent
|
|
|
|
|
* @see se.scalablesolutions.akka.camel.component.ActorProducer
|
|
|
|
|
|
|
|
|
|
* @author Martin Krasser
|
|
|
|
|
*/
|
2010-03-01 06:07:32 +01:00
|
|
|
class ActorEndpoint(uri: String, comp: ActorComponent, val id: Option[String], val uuid: Option[String]) extends DefaultEndpoint(uri, comp) {
|
2010-02-25 17:19:50 +01:00
|
|
|
/**
|
|
|
|
|
* @throws UnsupportedOperationException
|
|
|
|
|
*/
|
|
|
|
|
def createConsumer(processor: Processor): Consumer =
|
|
|
|
|
throw new UnsupportedOperationException("actor consumer not supported yet")
|
|
|
|
|
|
2010-03-05 19:38:23 +01:00
|
|
|
/**
|
|
|
|
|
* Creates a new ActorProducer instance initialized with this endpoint.
|
|
|
|
|
*/
|
2010-02-25 17:19:50 +01:00
|
|
|
def createProducer: ActorProducer = new ActorProducer(this)
|
|
|
|
|
|
2010-03-05 19:38:23 +01:00
|
|
|
/**
|
|
|
|
|
* Returns true.
|
|
|
|
|
*/
|
2010-02-25 17:19:50 +01:00
|
|
|
def isSingleton: Boolean = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sends the in-message of an exchange to an actor. If the exchange pattern is out-capable,
|
|
|
|
|
* the producer waits for a reply (using the !! operator), otherwise the ! operator is used
|
2010-03-05 19:38:23 +01:00
|
|
|
* for sending the message.
|
2010-02-25 17:19:50 +01:00
|
|
|
*
|
|
|
|
|
* @see se.scalablesolutions.akka.camel.component.ActorComponent
|
|
|
|
|
* @see se.scalablesolutions.akka.camel.component.ActorEndpoint
|
|
|
|
|
*
|
|
|
|
|
* @author Martin Krasser
|
|
|
|
|
*/
|
|
|
|
|
class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) {
|
2010-03-05 19:38:23 +01:00
|
|
|
import CamelMessageConversion.toExchangeAdapter
|
2010-02-25 17:19:50 +01:00
|
|
|
|
2010-03-05 19:38:23 +01:00
|
|
|
implicit val sender = None
|
2010-02-25 17:19:50 +01:00
|
|
|
|
2010-03-05 19:38:23 +01:00
|
|
|
/**
|
|
|
|
|
* Depending on the exchange pattern, this method either calls processInOut or
|
|
|
|
|
* processInOnly for interacting with an actor. This methods looks up the actor
|
|
|
|
|
* from the ActorRegistry according to this producer's endpoint URI.
|
|
|
|
|
*
|
|
|
|
|
* @param exchange represents the message exchange with the actor.
|
|
|
|
|
*/
|
2010-02-25 17:19:50 +01:00
|
|
|
def process(exchange: Exchange) {
|
2010-03-01 06:07:32 +01:00
|
|
|
val actor = target getOrElse (throw new ActorNotRegisteredException(ep.getEndpointUri))
|
2010-02-25 17:19:50 +01:00
|
|
|
if (exchange.getPattern.isOutCapable)
|
|
|
|
|
processInOut(exchange, actor)
|
|
|
|
|
else
|
|
|
|
|
processInOnly(exchange, actor)
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-05 19:38:23 +01:00
|
|
|
/**
|
|
|
|
|
* Send the exchange in-message to the given actor using the ! operator. The message
|
|
|
|
|
* send to the actor is of type se.scalablesolutions.akka.camel.Message.
|
|
|
|
|
*/
|
2010-02-25 17:19:50 +01:00
|
|
|
protected def processInOnly(exchange: Exchange, actor: Actor) {
|
2010-03-05 19:38:23 +01:00
|
|
|
actor ! exchange.toRequestMessage(Map(Message.MessageExchangeId -> exchange.getExchangeId))
|
2010-02-25 17:19:50 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-05 19:38:23 +01:00
|
|
|
/**
|
|
|
|
|
* Send the exchange in-message to the given actor using the !! operator. The exchange
|
|
|
|
|
* out-message is populated from the actor's reply message. The message sent to the
|
|
|
|
|
* actor is of type se.scalablesolutions.akka.camel.Message.
|
|
|
|
|
*/
|
2010-02-25 17:19:50 +01:00
|
|
|
protected def processInOut(exchange: Exchange, actor: Actor) {
|
2010-03-01 13:35:11 +01:00
|
|
|
|
2010-02-25 17:19:50 +01:00
|
|
|
// TODO: support asynchronous communication
|
2010-03-05 19:38:23 +01:00
|
|
|
val result: Any = actor !! exchange.toRequestMessage(Map(Message.MessageExchangeId -> exchange.getExchangeId))
|
2010-02-25 17:19:50 +01:00
|
|
|
|
|
|
|
|
result match {
|
2010-03-05 19:38:23 +01:00
|
|
|
case Some(msg) => exchange.fromResponseMessage(Message.canonicalize(msg))
|
|
|
|
|
case None => {
|
|
|
|
|
// TODO: handle timeout properly
|
|
|
|
|
// TODO: make timeout configurable
|
2010-02-25 17:19:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def target: Option[Actor] = {
|
2010-03-01 06:07:32 +01:00
|
|
|
if (ep.id.isDefined) targetById(ep.id.get)
|
|
|
|
|
else targetByUuid(ep.uuid.get)
|
2010-02-25 17:19:50 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-01 06:07:32 +01:00
|
|
|
private def targetById(id: String) = ActorRegistry.actorsFor(id) match {
|
|
|
|
|
case Nil => None
|
|
|
|
|
case actor :: Nil => Some(actor)
|
|
|
|
|
case actors => Some(actors.first)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def targetByUuid(uuid: String) = ActorRegistry.actorFor(uuid)
|
2010-02-25 17:19:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-03-05 19:38:23 +01:00
|
|
|
* Thrown to indicate that an actor referenced by an endpoint URI cannot be
|
2010-02-25 17:19:50 +01:00
|
|
|
* found in the ActorRegistry.
|
|
|
|
|
*
|
|
|
|
|
* @author Martin Krasser
|
|
|
|
|
*/
|
2010-03-01 06:07:32 +01:00
|
|
|
class ActorNotRegisteredException(uri: String) extends RuntimeException {
|
|
|
|
|
override def getMessage = "%s not registered" format uri
|
2010-02-25 17:19:50 +01:00
|
|
|
}
|