From 94c9b5ad45cf96f623274ce2d507743b38761dd5 Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 6 Aug 2010 17:31:37 +0200 Subject: [PATCH 01/24] - moved all into package folder structure - added simple protobuf based rpc convenience --- .../scalablesolutions/akka/amqp}/AMQP.scala | 53 ++------- .../akka/amqp}/AMQPMessage.scala | 0 .../akka/amqp}/ConsumerActor.scala | 0 .../akka/amqp}/ExampleSession.scala | 6 +- .../akka/amqp}/ExchangeType.scala | 0 .../amqp}/FaultTolerantChannelActor.scala | 0 .../amqp}/FaultTolerantConnectionActor.scala | 0 .../akka/amqp}/ProducerActor.scala | 0 .../scalablesolutions/akka/amqp/rpc/RPC.scala | 110 ++++++++++++++++++ .../akka/amqp/rpc}/RpcClientActor.scala | 6 +- .../akka/amqp/rpc}/RpcServerActor.scala | 2 +- .../test}/AMQPConnectionRecoveryTest.scala | 17 +-- .../AMQPConsumerChannelRecoveryTest.scala | 18 +-- .../AMQPConsumerConnectionRecoveryTest.scala | 20 +--- .../AMQPConsumerManualAcknowledgeTest.scala | 18 +-- .../amqp/test}/AMQPConsumerMessageTest.scala | 18 +-- .../AMQPProducerChannelRecoveryTest.scala | 18 +-- .../AMQPProducerConnectionRecoveryTest.scala | 18 +-- .../amqp/test}/AMQPProducerMessageTest.scala | 18 +-- .../amqp/test}/AMQPRpcClientServerTest.scala | 25 ++-- .../akka/amqp/test/AMQPRpcProtobufTest.scala | 35 ++++++ .../akka/amqp/test}/AMQPTest.scala | 2 +- project/build/AkkaProject.scala | 50 ++++---- 23 files changed, 238 insertions(+), 196 deletions(-) rename akka-amqp/src/main/scala/{ => se/scalablesolutions/akka/amqp}/AMQP.scala (69%) rename akka-amqp/src/main/scala/{ => se/scalablesolutions/akka/amqp}/AMQPMessage.scala (100%) rename akka-amqp/src/main/scala/{ => se/scalablesolutions/akka/amqp}/ConsumerActor.scala (100%) rename akka-amqp/src/main/scala/{ => se/scalablesolutions/akka/amqp}/ExampleSession.scala (94%) rename akka-amqp/src/main/scala/{ => se/scalablesolutions/akka/amqp}/ExchangeType.scala (100%) rename akka-amqp/src/main/scala/{ => se/scalablesolutions/akka/amqp}/FaultTolerantChannelActor.scala (100%) rename akka-amqp/src/main/scala/{ => se/scalablesolutions/akka/amqp}/FaultTolerantConnectionActor.scala (100%) rename akka-amqp/src/main/scala/{ => se/scalablesolutions/akka/amqp}/ProducerActor.scala (100%) create mode 100644 akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala rename akka-amqp/src/main/scala/{ => se/scalablesolutions/akka/amqp/rpc}/RpcClientActor.scala (89%) rename akka-amqp/src/main/scala/{ => se/scalablesolutions/akka/amqp/rpc}/RpcServerActor.scala (94%) rename akka-amqp/src/test/scala/{ => se/scalablesolutions/akka/amqp/test}/AMQPConnectionRecoveryTest.scala (85%) rename akka-amqp/src/test/scala/{ => se/scalablesolutions/akka/amqp/test}/AMQPConsumerChannelRecoveryTest.scala (88%) rename akka-amqp/src/test/scala/{ => se/scalablesolutions/akka/amqp/test}/AMQPConsumerConnectionRecoveryTest.scala (88%) rename akka-amqp/src/test/scala/{ => se/scalablesolutions/akka/amqp/test}/AMQPConsumerManualAcknowledgeTest.scala (88%) rename akka-amqp/src/test/scala/{ => se/scalablesolutions/akka/amqp/test}/AMQPConsumerMessageTest.scala (86%) rename akka-amqp/src/test/scala/{ => se/scalablesolutions/akka/amqp/test}/AMQPProducerChannelRecoveryTest.scala (86%) rename akka-amqp/src/test/scala/{ => se/scalablesolutions/akka/amqp/test}/AMQPProducerConnectionRecoveryTest.scala (86%) rename akka-amqp/src/test/scala/{ => se/scalablesolutions/akka/amqp/test}/AMQPProducerMessageTest.scala (84%) rename akka-amqp/src/test/scala/{ => se/scalablesolutions/akka/amqp/test}/AMQPRpcClientServerTest.scala (77%) create mode 100644 akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTest.scala rename akka-amqp/src/test/scala/{ => se/scalablesolutions/akka/amqp/test}/AMQPTest.scala (87%) diff --git a/akka-amqp/src/main/scala/AMQP.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala similarity index 69% rename from akka-amqp/src/main/scala/AMQP.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala index b1e08ae752..0823f306cd 100644 --- a/akka-amqp/src/main/scala/AMQP.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala @@ -1,15 +1,16 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - package se.scalablesolutions.akka.amqp -import se.scalablesolutions.akka.actor.{Actor, ActorRef} -import se.scalablesolutions.akka.actor.Actor._ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + import se.scalablesolutions.akka.config.OneForOneStrategy import com.rabbitmq.client.{ReturnListener, ShutdownListener, ConnectionFactory} import java.lang.IllegalArgumentException import se.scalablesolutions.akka.util.Logging +import se.scalablesolutions.akka.actor.{Actor, ActorRef} +import Actor._ + /** * AMQP Actor API. Implements Connection, Producer and Consumer materialized as Actors. * @@ -80,33 +81,6 @@ object AMQP { consumer } - def newRpcClient[O,I](connection: ActorRef, - exchangeParameters: ExchangeParameters, - routingKey: String, - serializer: RpcClientSerializer[O,I], - channelParameters: Option[ChannelParameters] = None): ActorRef = { - val rpcActor: ActorRef = actorOf(new RpcClientActor[O,I](exchangeParameters, routingKey, serializer, channelParameters)) - connection.startLink(rpcActor) - rpcActor ! Start - rpcActor - } - - def newRpcServer[I,O](connection: ActorRef, - exchangeParameters: ExchangeParameters, - routingKey: String, - serializer: RpcServerSerializer[I,O], - requestHandler: I => O, - queueName: Option[String] = None, - channelParameters: Option[ChannelParameters] = None) = { - val producer = newProducer(connection, new ProducerParameters(new ExchangeParameters("", ExchangeType.Direct), channelParameters = channelParameters)) - val rpcServer = actorOf(new RpcServerActor[I,O](producer, serializer, requestHandler)) - val consumer = newConsumer(connection, new ConsumerParameters(exchangeParameters, routingKey, rpcServer - , channelParameters = channelParameters - , selfAcknowledging = false - , queueName = queueName)) - - } - private val supervisor = new AMQPSupervisor class AMQPSupervisor extends Logging { @@ -129,17 +103,4 @@ object AMQP { connectionActor } } - - trait FromBinary[T] { - def fromBinary(bytes: Array[Byte]): T - } - - trait ToBinary[T] { - def toBinary(t: T): Array[Byte] - } - - - case class RpcClientSerializer[O,I](toBinary: ToBinary[O], fromBinary: FromBinary[I]) - - case class RpcServerSerializer[I,O](fromBinary: FromBinary[I], toBinary: ToBinary[O]) } diff --git a/akka-amqp/src/main/scala/AMQPMessage.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala similarity index 100% rename from akka-amqp/src/main/scala/AMQPMessage.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala diff --git a/akka-amqp/src/main/scala/ConsumerActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala similarity index 100% rename from akka-amqp/src/main/scala/ConsumerActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala diff --git a/akka-amqp/src/main/scala/ExampleSession.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala similarity index 94% rename from akka-amqp/src/main/scala/ExampleSession.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala index 4fa1358a29..f2ae5e8295 100644 --- a/akka-amqp/src/main/scala/ExampleSession.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala @@ -4,6 +4,8 @@ package se.scalablesolutions.akka.amqp +import rpc.RPC +import rpc.RPC.{RpcClientSerializer, RpcServerSerializer, ToBinary, FromBinary} import se.scalablesolutions.akka.actor.{Actor, ActorRegistry} import Actor._ import java.util.concurrent.{CountDownLatch, TimeUnit} @@ -146,7 +148,7 @@ object ExampleSession { def requestHandler(request: String) = 3 - val rpcServer = AMQP.newRpcServer[String,Int](connection, exchangeParameters, "rpc.in.key", rpcServerSerializer, + val rpcServer = RPC.newRpcServer[String,Int](connection, exchangeParameters, "rpc.in.key", rpcServerSerializer, requestHandler, queueName = Some("rpc.in.key.queue")) @@ -159,7 +161,7 @@ object ExampleSession { } val rpcClientSerializer = new RpcClientSerializer[String, Int](clientToBinary, clientFromBinary) - val rpcClient = AMQP.newRpcClient[String,Int](connection, exchangeParameters, "rpc.in.key", rpcClientSerializer) + val rpcClient = RPC.newRpcClient[String,Int](connection, exchangeParameters, "rpc.in.key", rpcClientSerializer) val response = (rpcClient !! "rpc_request") log.info("Response: " + response) diff --git a/akka-amqp/src/main/scala/ExchangeType.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala similarity index 100% rename from akka-amqp/src/main/scala/ExchangeType.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala diff --git a/akka-amqp/src/main/scala/FaultTolerantChannelActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala similarity index 100% rename from akka-amqp/src/main/scala/FaultTolerantChannelActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala diff --git a/akka-amqp/src/main/scala/FaultTolerantConnectionActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala similarity index 100% rename from akka-amqp/src/main/scala/FaultTolerantConnectionActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala diff --git a/akka-amqp/src/main/scala/ProducerActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala similarity index 100% rename from akka-amqp/src/main/scala/ProducerActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala new file mode 100644 index 0000000000..a711a01cc5 --- /dev/null +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala @@ -0,0 +1,110 @@ +package se.scalablesolutions.akka.amqp.rpc + +import se.scalablesolutions.akka.amqp.AMQP._ +import com.google.protobuf.Message +import se.scalablesolutions.akka.actor.{Actor, ActorRef} +import Actor._ +import se.scalablesolutions.akka.amqp._ + +object RPC { + + def newRpcClient[O, I](connection: ActorRef, + exchangeParameters: ExchangeParameters, + routingKey: String, + serializer: RpcClientSerializer[O, I], + channelParameters: Option[ChannelParameters] = None): ActorRef = { + val rpcActor: ActorRef = actorOf(new RpcClientActor[O, I](exchangeParameters, routingKey, serializer, channelParameters)) + connection.startLink(rpcActor) + rpcActor ! Start + rpcActor + } + + def newRpcServer[I, O](connection: ActorRef, + exchangeParameters: ExchangeParameters, + routingKey: String, + serializer: RpcServerSerializer[I, O], + requestHandler: I => O, + queueName: Option[String] = None, + channelParameters: Option[ChannelParameters] = None) = { + val producer = newProducer(connection, new ProducerParameters(new ExchangeParameters("", ExchangeType.Direct), channelParameters = channelParameters)) + val rpcServer = actorOf(new RpcServerActor[I, O](producer, serializer, requestHandler)) + val consumer = newConsumer(connection, new ConsumerParameters(exchangeParameters, routingKey, rpcServer + , channelParameters = channelParameters + , selfAcknowledging = false + , queueName = queueName)) + + } + + trait FromBinary[T] { + def fromBinary(bytes: Array[Byte]): T + } + + trait ToBinary[T] { + def toBinary(t: T): Array[Byte] + } + + + case class RpcClientSerializer[O, I](toBinary: ToBinary[O], fromBinary: FromBinary[I]) + + case class RpcServerSerializer[I, O](fromBinary: FromBinary[I], toBinary: ToBinary[O]) + + + /** + * RPC convenience + */ + trait RpcClient[O, I] { + def callService(request: O, timeout: Long = 5000): Option[I] + } + + private class RpcServiceClient[O, I](client: ActorRef) extends RpcClient[O, I] { + def callService(request: O, timeout: Long = 5000): Option[I] = { + (client.!!(request, timeout)).as[I] + } + } + + private val ARRAY_OF_BYTE_ARRAY = Array[Class[_]](classOf[Array[Byte]]) + + def startProtobufServer[I <: Message, O <: Message]( + connection: ActorRef, serviceName: String, requestHandler: I => O)(implicit manifest: Manifest[I]) = { + + val serializer = new RpcServerSerializer[I, O]( + new FromBinary[I] { + def fromBinary(bytes: Array[Byte]): I = { + createProtobufFromBytes[I](bytes) + } + }, new ToBinary[O] { + def toBinary(t: O) = t.toByteArray + }) + + val exchangeParameters = new ExchangeParameters(serviceName, ExchangeType.Topic) + val routingKey = "%s.request".format(serviceName) + val queueName = "%s.in".format(routingKey) + + newRpcServer[I, O](connection, exchangeParameters, routingKey, serializer, requestHandler, + queueName = Some(queueName)) + } + + def startProtobufClient[O <: Message, I <: Message]( + connection: ActorRef, serviceName: String)(implicit manifest: Manifest[I]): RpcClient[O, I] = { + + val serializer = new RpcClientSerializer[O, I]( + new ToBinary[O] { + def toBinary(t: O) = t.toByteArray + }, new FromBinary[I] { + def fromBinary(bytes: Array[Byte]): I = { + createProtobufFromBytes[I](bytes) + } + }) + + val exchangeParameters = new ExchangeParameters(serviceName, ExchangeType.Topic) + val routingKey = "%s.request".format(serviceName) + val queueName = "%s.in".format(routingKey) + + val client = newRpcClient[O, I](connection, exchangeParameters, routingKey, serializer) + new RpcServiceClient[O, I](client) + } + + private def createProtobufFromBytes[I](bytes: Array[Byte])(implicit manifest: Manifest[I]): I = { + manifest.erasure.getDeclaredMethod("parseFrom", ARRAY_OF_BYTE_ARRAY: _*).invoke(null, bytes).asInstanceOf[I] + } +} \ No newline at end of file diff --git a/akka-amqp/src/main/scala/RpcClientActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcClientActor.scala similarity index 89% rename from akka-amqp/src/main/scala/RpcClientActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcClientActor.scala index 2935982a67..40665194fc 100644 --- a/akka-amqp/src/main/scala/RpcClientActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcClientActor.scala @@ -4,11 +4,9 @@ package se.scalablesolutions.akka.amqp -import se.scalablesolutions.akka.serialization.Serializer -import se.scalablesolutions.akka.amqp.AMQP.{ChannelParameters, ExchangeParameters} - import com.rabbitmq.client.{Channel, RpcClient} -import se.scalablesolutions.akka.amqp.AMQP.{RpcClientSerializer, ChannelParameters, ExchangeParameters} +import rpc.RPC.RpcClientSerializer +import se.scalablesolutions.akka.amqp.AMQP.{ChannelParameters, ExchangeParameters} class RpcClientActor[I,O](exchangeParameters: ExchangeParameters, routingKey: String, diff --git a/akka-amqp/src/main/scala/RpcServerActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcServerActor.scala similarity index 94% rename from akka-amqp/src/main/scala/RpcServerActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcServerActor.scala index 99d74d9b56..7c1cc01d01 100644 --- a/akka-amqp/src/main/scala/RpcServerActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcServerActor.scala @@ -4,9 +4,9 @@ package se.scalablesolutions.akka.amqp +import rpc.RPC.RpcServerSerializer import se.scalablesolutions.akka.actor.{ActorRef, Actor} import com.rabbitmq.client.AMQP.BasicProperties -import se.scalablesolutions.akka.amqp.AMQP.RpcServerSerializer class RpcServerActor[I,O](producer: ActorRef, serializer: RpcServerSerializer[I,O], requestHandler: I => O) extends Actor { diff --git a/akka-amqp/src/test/scala/AMQPConnectionRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTest.scala similarity index 85% rename from akka-amqp/src/test/scala/AMQPConnectionRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTest.scala index c1af35546a..ef2ee5b80f 100644 --- a/akka-amqp/src/test/scala/AMQPConnectionRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTest.scala @@ -1,12 +1,9 @@ +package se.scalablesolutions.akka.amqp.test + /** * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.amqp.test - -import se.scalablesolutions.akka.util.Logging -import org.scalatest.junit.JUnitSuite -import org.junit.Test import java.util.concurrent.TimeUnit import se.scalablesolutions.akka.actor.{Actor, ActorRef} import org.multiverse.api.latches.StandardLatch @@ -14,8 +11,10 @@ import com.rabbitmq.client.ShutdownSignalException import se.scalablesolutions.akka.amqp._ import se.scalablesolutions.akka.amqp.AMQP.ConnectionParameters import org.scalatest.matchers.MustMatchers +import org.scalatest.junit.JUnitSuite +import org.junit.Test -class AMQPConnectionRecoveryTest extends JUnitSuite with MustMatchers with Logging { +class AMQPConnectionRecoveryTest extends JUnitSuite with MustMatchers { @Test def connectionAndRecovery = if (AMQPTest.enabled) { @@ -50,10 +49,4 @@ class AMQPConnectionRecoveryTest extends JUnitSuite with MustMatchers with Loggi } } - @Test - def dummy { - // amqp tests need local rabbitmq server running, so a disabled by default. - // this dummy test makes sure that the whole test class doesn't fail because of missing tests - assert(true) - } } diff --git a/akka-amqp/src/test/scala/AMQPConsumerChannelRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala similarity index 88% rename from akka-amqp/src/test/scala/AMQPConsumerChannelRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala index a0b44f4739..3c21fa36af 100644 --- a/akka-amqp/src/test/scala/AMQPConsumerChannelRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala @@ -1,12 +1,9 @@ +package se.scalablesolutions.akka.amqp.test + /** * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.amqp.test - -import se.scalablesolutions.akka.util.Logging -import org.scalatest.junit.JUnitSuite -import se.scalablesolutions.akka.actor.Actor._ import org.multiverse.api.latches.StandardLatch import com.rabbitmq.client.ShutdownSignalException import se.scalablesolutions.akka.amqp._ @@ -15,8 +12,10 @@ import java.util.concurrent.TimeUnit import se.scalablesolutions.akka.actor.ActorRef import org.junit.Test import se.scalablesolutions.akka.amqp.AMQP._ +import org.scalatest.junit.JUnitSuite +import se.scalablesolutions.akka.actor.Actor._ -class AMQPConsumerChannelRecoveryTest extends JUnitSuite with MustMatchers with Logging { +class AMQPConsumerChannelRecoveryTest extends JUnitSuite with MustMatchers { @Test def consumerChannelRecovery = if (AMQPTest.enabled) { @@ -60,11 +59,4 @@ class AMQPConsumerChannelRecoveryTest extends JUnitSuite with MustMatchers with connection.stop } } - - @Test - def dummy { - // amqp tests need local rabbitmq server running, so a disabled by default. - // this dummy test makes sure that the whole test class doesn't fail because of missing tests - assert(true) - } } diff --git a/akka-amqp/src/test/scala/AMQPConsumerConnectionRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala similarity index 88% rename from akka-amqp/src/test/scala/AMQPConsumerConnectionRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala index bf4885fea5..301489bd8f 100644 --- a/akka-amqp/src/test/scala/AMQPConsumerConnectionRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala @@ -1,22 +1,21 @@ +package se.scalablesolutions.akka.amqp.test + /** * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.amqp.test - -import se.scalablesolutions.akka.util.Logging -import org.scalatest.junit.JUnitSuite -import se.scalablesolutions.akka.actor.Actor._ import org.multiverse.api.latches.StandardLatch import com.rabbitmq.client.ShutdownSignalException import se.scalablesolutions.akka.amqp._ import org.scalatest.matchers.MustMatchers import java.util.concurrent.TimeUnit -import se.scalablesolutions.akka.actor.ActorRef import org.junit.Test import se.scalablesolutions.akka.amqp.AMQP._ +import org.scalatest.junit.JUnitSuite +import se.scalablesolutions.akka.actor.{Actor, ActorRef} +import Actor._ -class AMQPConsumerConnectionRecoveryTest extends JUnitSuite with MustMatchers with Logging { +class AMQPConsumerConnectionRecoveryTest extends JUnitSuite with MustMatchers { @Test def consumerConnectionRecovery = if (AMQPTest.enabled) { @@ -79,11 +78,4 @@ class AMQPConsumerConnectionRecoveryTest extends JUnitSuite with MustMatchers wi connection.stop } } - - @Test - def dummy { - // amqp tests need local rabbitmq server running, so a disabled by default. - // this dummy test makes sure that the whole test class doesn't fail because of missing tests - assert(true) - } } diff --git a/akka-amqp/src/test/scala/AMQPConsumerManualAcknowledgeTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala similarity index 88% rename from akka-amqp/src/test/scala/AMQPConsumerManualAcknowledgeTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala index 2dc4ee939b..a18db40d5f 100644 --- a/akka-amqp/src/test/scala/AMQPConsumerManualAcknowledgeTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala @@ -1,12 +1,9 @@ +package se.scalablesolutions.akka.amqp.test + /** * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.amqp.test - -import se.scalablesolutions.akka.util.Logging -import org.scalatest.junit.JUnitSuite -import org.multiverse.api.latches.StandardLatch import se.scalablesolutions.akka.actor.Actor._ import org.scalatest.matchers.MustMatchers import se.scalablesolutions.akka.amqp._ @@ -14,8 +11,10 @@ import org.junit.Test import se.scalablesolutions.akka.actor.ActorRef import java.util.concurrent.{CountDownLatch, TimeUnit} import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ConsumerParameters, ChannelParameters, ProducerParameters} +import org.multiverse.api.latches.StandardLatch +import org.scalatest.junit.JUnitSuite -class AMQPConsumerManualAcknowledgeTest extends JUnitSuite with MustMatchers with Logging { +class AMQPConsumerManualAcknowledgeTest extends JUnitSuite with MustMatchers { @Test def consumerMessageManualAcknowledge = if (AMQPTest.enabled) { @@ -57,11 +56,4 @@ class AMQPConsumerManualAcknowledgeTest extends JUnitSuite with MustMatchers wit connection.stop } } - - @Test - def dummy { - // amqp tests need local rabbitmq server running, so a disabled by default. - // this dummy test makes sure that the whole test class doesn't fail because of missing tests - assert(true) - } } diff --git a/akka-amqp/src/test/scala/AMQPConsumerMessageTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala similarity index 86% rename from akka-amqp/src/test/scala/AMQPConsumerMessageTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala index 5d34f867d6..9eb7aa9158 100644 --- a/akka-amqp/src/test/scala/AMQPConsumerMessageTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala @@ -1,20 +1,19 @@ +package se.scalablesolutions.akka.amqp.test + /** * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.amqp.test - -import se.scalablesolutions.akka.util.Logging -import org.scalatest.junit.JUnitSuite -import org.junit.Test import se.scalablesolutions.akka.amqp._ import org.multiverse.api.latches.StandardLatch import se.scalablesolutions.akka.actor.Actor._ import org.scalatest.matchers.MustMatchers import java.util.concurrent.{CountDownLatch, TimeUnit} import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ConsumerParameters, ChannelParameters, ProducerParameters} +import org.scalatest.junit.JUnitSuite +import org.junit.Test -class AMQPConsumerMessageTest extends JUnitSuite with MustMatchers with Logging { +class AMQPConsumerMessageTest extends JUnitSuite with MustMatchers { @Test def consumerMessage = if (AMQPTest.enabled) { @@ -46,11 +45,4 @@ class AMQPConsumerMessageTest extends JUnitSuite with MustMatchers with Logging connection.stop } } - - @Test - def dummy { - // amqp tests need local rabbitmq server running, so a disabled by default. - // this dummy test makes sure that the whole test class doesn't fail because of missing tests - assert(true) - } } diff --git a/akka-amqp/src/test/scala/AMQPProducerChannelRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTest.scala similarity index 86% rename from akka-amqp/src/test/scala/AMQPProducerChannelRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTest.scala index 26b2d78393..3aec05c122 100644 --- a/akka-amqp/src/test/scala/AMQPProducerChannelRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTest.scala @@ -1,12 +1,9 @@ +package se.scalablesolutions.akka.amqp.test + /** * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.amqp.test - -import se.scalablesolutions.akka.util.Logging -import org.scalatest.junit.JUnitSuite -import org.junit.Test import java.util.concurrent.TimeUnit import se.scalablesolutions.akka.actor.{Actor, ActorRef} import org.multiverse.api.latches.StandardLatch @@ -14,8 +11,10 @@ import com.rabbitmq.client.ShutdownSignalException import se.scalablesolutions.akka.amqp._ import org.scalatest.matchers.MustMatchers import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ChannelParameters, ProducerParameters, ConnectionParameters} +import org.scalatest.junit.JUnitSuite +import org.junit.Test -class AMQPProducerChannelRecoveryTest extends JUnitSuite with MustMatchers with Logging { +class AMQPProducerChannelRecoveryTest extends JUnitSuite with MustMatchers { @Test def producerChannelRecovery = if (AMQPTest.enabled) { @@ -53,11 +52,4 @@ class AMQPProducerChannelRecoveryTest extends JUnitSuite with MustMatchers with connection.stop } } - - @Test - def dummy { - // amqp tests need local rabbitmq server running, so a disabled by default. - // this dummy test makes sure that the whole test class doesn't fail because of missing tests - assert(true) - } } diff --git a/akka-amqp/src/test/scala/AMQPProducerConnectionRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTest.scala similarity index 86% rename from akka-amqp/src/test/scala/AMQPProducerConnectionRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTest.scala index fe8259b208..b1a4cc1027 100644 --- a/akka-amqp/src/test/scala/AMQPProducerConnectionRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTest.scala @@ -1,12 +1,9 @@ +package se.scalablesolutions.akka.amqp.test + /** * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.amqp.test - -import se.scalablesolutions.akka.util.Logging -import org.scalatest.junit.JUnitSuite -import org.junit.Test import java.util.concurrent.TimeUnit import se.scalablesolutions.akka.actor.{Actor, ActorRef} import org.multiverse.api.latches.StandardLatch @@ -14,8 +11,10 @@ import com.rabbitmq.client.ShutdownSignalException import se.scalablesolutions.akka.amqp._ import org.scalatest.matchers.MustMatchers import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ChannelParameters, ProducerParameters, ConnectionParameters} +import org.scalatest.junit.JUnitSuite +import org.junit.Test -class AMQPProducerConnectionRecoveryTest extends JUnitSuite with MustMatchers with Logging { +class AMQPProducerConnectionRecoveryTest extends JUnitSuite with MustMatchers { @Test def producerConnectionRecovery = if (AMQPTest.enabled) { @@ -52,11 +51,4 @@ class AMQPProducerConnectionRecoveryTest extends JUnitSuite with MustMatchers wi connection.stop } } - - @Test - def dummy { - // amqp tests need local rabbitmq server running, so a disabled by default. - // this dummy test makes sure that the whole test class doesn't fail because of missing tests - assert(true) - } } diff --git a/akka-amqp/src/test/scala/AMQPProducerMessageTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTest.scala similarity index 84% rename from akka-amqp/src/test/scala/AMQPProducerMessageTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTest.scala index 5b19df351f..587f03ec92 100644 --- a/akka-amqp/src/test/scala/AMQPProducerMessageTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTest.scala @@ -1,12 +1,9 @@ +package se.scalablesolutions.akka.amqp.test + /** * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.amqp.test - -import se.scalablesolutions.akka.util.Logging -import org.scalatest.junit.JUnitSuite -import org.junit.Test import java.util.concurrent.TimeUnit import se.scalablesolutions.akka.actor.ActorRef import org.multiverse.api.latches.StandardLatch @@ -16,8 +13,10 @@ import com.rabbitmq.client.AMQP.BasicProperties import java.lang.String import org.scalatest.matchers.MustMatchers import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ProducerParameters} +import org.scalatest.junit.JUnitSuite +import org.junit.Test -class AMQPProducerMessageTest extends JUnitSuite with MustMatchers with Logging { +class AMQPProducerMessageTest extends JUnitSuite with MustMatchers { @Test def producerMessage = if (AMQPTest.enabled) { @@ -41,11 +40,4 @@ class AMQPProducerMessageTest extends JUnitSuite with MustMatchers with Logging connection.stop } } - - @Test - def dummy { - // amqp tests need local rabbitmq server running, so a disabled by default. - // this dummy test makes sure that the whole test class doesn't fail because of missing tests - assert(true) - } } diff --git a/akka-amqp/src/test/scala/AMQPRpcClientServerTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTest.scala similarity index 77% rename from akka-amqp/src/test/scala/AMQPRpcClientServerTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTest.scala index c585675098..bf0ee6610d 100644 --- a/akka-amqp/src/test/scala/AMQPRpcClientServerTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTest.scala @@ -1,19 +1,21 @@ +package se.scalablesolutions.akka.amqp.test + /** * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.amqp.test - -import se.scalablesolutions.akka.util.Logging -import org.scalatest.junit.JUnitSuite -import org.junit.Test import se.scalablesolutions.akka.amqp._ +import rpc.RPC +import rpc.RPC.{RpcClientSerializer, RpcServerSerializer, FromBinary, ToBinary} import se.scalablesolutions.akka.actor.Actor._ import org.scalatest.matchers.MustMatchers import java.util.concurrent.{CountDownLatch, TimeUnit} import se.scalablesolutions.akka.amqp.AMQP._ +import org.scalatest.junit.JUnitSuite +import org.junit.Test -class AMQPRpcClientServerTest extends JUnitSuite with MustMatchers with Logging { +class AMQPRpcClientServerTest extends JUnitSuite with MustMatchers { + @Test def consumerMessage = if (AMQPTest.enabled) { val connection = AMQP.newConnection() @@ -39,7 +41,7 @@ class AMQPRpcClientServerTest extends JUnitSuite with MustMatchers with Logging def requestHandler(request: String) = 3 - val rpcServer = AMQP.newRpcServer[String, Int](connection, exchangeParameters, "rpc.routing", rpcServerSerializer, + val rpcServer = RPC.newRpcServer[String, Int](connection, exchangeParameters, "rpc.routing", rpcServerSerializer, requestHandler, channelParameters = Some(channelParameters)) val rpcClientSerializer = new RpcClientSerializer[String, Int]( @@ -49,7 +51,7 @@ class AMQPRpcClientServerTest extends JUnitSuite with MustMatchers with Logging def fromBinary(bytes: Array[Byte]) = bytes.head.toInt }) - val rpcClient = AMQP.newRpcClient[String, Int](connection, exchangeParameters, "rpc.routing", rpcClientSerializer, + val rpcClient = RPC.newRpcClient[String, Int](connection, exchangeParameters, "rpc.routing", rpcClientSerializer, channelParameters = Some(channelParameters)) countDown.await(2, TimeUnit.SECONDS) must be(true) @@ -59,11 +61,4 @@ class AMQPRpcClientServerTest extends JUnitSuite with MustMatchers with Logging connection.stop } } - - @Test - def dummy { - // amqp tests need local rabbitmq server running, so a disabled by default. - // this dummy test makes sure that the whole test class doesn't fail because of missing tests - assert(true) - } } diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTest.scala new file mode 100644 index 0000000000..b0ca3f8ffe --- /dev/null +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTest.scala @@ -0,0 +1,35 @@ +package se.scalablesolutions.akka.amqp.test + +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ +import org.scalatest.matchers.MustMatchers +import org.scalatest.junit.JUnitSuite +import se.scalablesolutions.akka.amqp.AMQP +import se.scalablesolutions.akka.remote.protocol.RemoteProtocol.AddressProtocol +import org.junit.Test +import se.scalablesolutions.akka.amqp.rpc.RPC + +class AMQPRpcProtobufTest extends JUnitSuite with MustMatchers { + + @Test + def consumerMessage = if (AMQPTest.enabled) { + + val connection = AMQP.newConnection() + + RPC.startProtobufServer(connection, "protoservice", requestHandler) + + val protobufClient = RPC.startProtobufClient[AddressProtocol, AddressProtocol](connection, "protoservice") + + val request = AddressProtocol.newBuilder.setHostname("testhost").setPort(4321).build + + protobufClient.callService(request) match { + case Some(response) => assert(response.getHostname == request.getHostname.reverse) + case None => fail("no response") + } + } + + def requestHandler(request: AddressProtocol): AddressProtocol = { + AddressProtocol.newBuilder.setHostname(request.getHostname.reverse).setPort(request.getPort).build + } +} \ No newline at end of file diff --git a/akka-amqp/src/test/scala/AMQPTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala similarity index 87% rename from akka-amqp/src/test/scala/AMQPTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala index 5ff9157bc5..8cad27ba60 100644 --- a/akka-amqp/src/test/scala/AMQPTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala @@ -5,5 +5,5 @@ package se.scalablesolutions.akka.amqp.test object AMQPTest { - def enabled = false + def enabled = true } diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 02f72d7f0e..7e3477027f 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -41,15 +41,15 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- object Repositories { - lazy val AkkaRepo = MavenRepository("Akka Repository", "http://scalablesolutions.se/akka/repository") - lazy val CodehausSnapshotRepo = MavenRepository("Codehaus Snapshots", "http://snapshots.repository.codehaus.org") +// lazy val AkkaRepo = MavenRepository("Akka Repository", "http://scalablesolutions.se/akka/repository") +// lazy val CodehausSnapshotRepo = MavenRepository("Codehaus Snapshots", "http://snapshots.repository.codehaus.org") lazy val EmbeddedRepo = MavenRepository("Embedded Repo", (info.projectPath / "embedded-repo").asURL.toString) - lazy val FusesourceSnapshotRepo = MavenRepository("Fusesource Snapshots", "http://repo.fusesource.com/nexus/content/repositories/snapshots") - lazy val GuiceyFruitRepo = MavenRepository("GuiceyFruit Repo", "http://guiceyfruit.googlecode.com/svn/repo/releases/") - lazy val JBossRepo = MavenRepository("JBoss Repo", "https://repository.jboss.org/nexus/content/groups/public/") - lazy val JavaNetRepo = MavenRepository("java.net Repo", "http://download.java.net/maven/2") - lazy val SonatypeSnapshotRepo = MavenRepository("Sonatype OSS Repo", "http://oss.sonatype.org/content/repositories/releases") - lazy val SunJDMKRepo = MavenRepository("Sun JDMK Repo", "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo") +// lazy val FusesourceSnapshotRepo = MavenRepository("Fusesource Snapshots", "http://repo.fusesource.com/nexus/content/repositories/snapshots") +// lazy val GuiceyFruitRepo = MavenRepository("GuiceyFruit Repo", "http://guiceyfruit.googlecode.com/svn/repo/releases/") +// lazy val JBossRepo = MavenRepository("JBoss Repo", "https://repository.jboss.org/nexus/content/groups/public/") +// lazy val JavaNetRepo = MavenRepository("java.net Repo", "http://download.java.net/maven/2") +// lazy val SonatypeSnapshotRepo = MavenRepository("Sonatype OSS Repo", "http://oss.sonatype.org/content/repositories/releases") +// lazy val SunJDMKRepo = MavenRepository("Sun JDMK Repo", "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo") } // ------------------------------------------------------------------------------------------------------------------- @@ -60,23 +60,26 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- import Repositories._ - lazy val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", SonatypeSnapshotRepo) - lazy val grizzlyModuleConfig = ModuleConfiguration("com.sun.grizzly", JavaNetRepo) - lazy val guiceyFruitModuleConfig = ModuleConfiguration("org.guiceyfruit", GuiceyFruitRepo) - // lazy val hawtdispatchModuleConfig = ModuleConfiguration("org.fusesource.hawtdispatch", FusesourceSnapshotRepo) - lazy val jbossModuleConfig = ModuleConfiguration("org.jboss", JBossRepo) - lazy val jdmkModuleConfig = ModuleConfiguration("com.sun.jdmk", SunJDMKRepo) - lazy val jerseyContrModuleConfig = ModuleConfiguration("com.sun.jersey.contribs", JavaNetRepo) - lazy val jerseyModuleConfig = ModuleConfiguration("com.sun.jersey", JavaNetRepo) - lazy val jgroupsModuleConfig = ModuleConfiguration("jgroups", JBossRepo) - lazy val jmsModuleConfig = ModuleConfiguration("javax.jms", SunJDMKRepo) - lazy val jmxModuleConfig = ModuleConfiguration("com.sun.jmx", SunJDMKRepo) - lazy val liftModuleConfig = ModuleConfiguration("net.liftweb", ScalaToolsSnapshots) - lazy val multiverseModuleConfig = ModuleConfiguration("org.multiverse", CodehausSnapshotRepo) - lazy val nettyModuleConfig = ModuleConfiguration("org.jboss.netty", JBossRepo) - lazy val scalaTestModuleConfig = ModuleConfiguration("org.scalatest", ScalaToolsSnapshots) +// lazy val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", SonatypeSnapshotRepo) +// lazy val grizzlyModuleConfig = ModuleConfiguration("com.sun.grizzly", JavaNetRepo) +// lazy val guiceyFruitModuleConfig = ModuleConfiguration("org.guiceyfruit", GuiceyFruitRepo) +// // lazy val hawtdispatchModuleConfig = ModuleConfiguration("org.fusesource.hawtdispatch", FusesourceSnapshotRepo) +// lazy val jbossModuleConfig = ModuleConfiguration("org.jboss", JBossRepo) +// lazy val jdmkModuleConfig = ModuleConfiguration("com.sun.jdmk", SunJDMKRepo) +// lazy val jerseyContrModuleConfig = ModuleConfiguration("com.sun.jersey.contribs", JavaNetRepo) +// lazy val jerseyModuleConfig = ModuleConfiguration("com.sun.jersey", JavaNetRepo) +// lazy val jgroupsModuleConfig = ModuleConfiguration("jgroups", JBossRepo) +// lazy val jmsModuleConfig = ModuleConfiguration("javax.jms", SunJDMKRepo) +// lazy val jmxModuleConfig = ModuleConfiguration("com.sun.jmx", SunJDMKRepo) +// lazy val liftModuleConfig = ModuleConfiguration("net.liftweb", ScalaToolsSnapshots) +// lazy val multiverseModuleConfig = ModuleConfiguration("org.multiverse", CodehausSnapshotRepo) +// lazy val nettyModuleConfig = ModuleConfiguration("org.jboss.netty", JBossRepo) +// lazy val scalaTestModuleConfig = ModuleConfiguration("org.scalatest", ScalaToolsSnapshots) lazy val embeddedRepo = EmbeddedRepo // This is the only exception, because the embedded repo is fast! + val mavenLocal = "Local Maven Repository" at "file:/e:/maven-repository" + val efgfpNexusReleasesRepository = "Nexus Releases" at "http://nexus/nexus/content/groups/public" + val efgfpNexusSnaphotsRepository = "Nexus Snapshots" at "http://nexus/nexus/content/groups/public-snapshots" // ------------------------------------------------------------------------------------------------------------------- // Versions // ------------------------------------------------------------------------------------------------------------------- @@ -358,6 +361,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { class AkkaAMQPProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { val commons_io = Dependencies.commons_io val rabbit = Dependencies.rabbit + val protobuf = Dependencies.protobuf // testing val junit = Dependencies.junit From ab5c4f8ab52389a99e40e8ffab2c759a79e744c8 Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 6 Aug 2010 17:32:01 +0200 Subject: [PATCH 02/24] disable ampq tests --- .../scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala index 8cad27ba60..5ff9157bc5 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala @@ -5,5 +5,5 @@ package se.scalablesolutions.akka.amqp.test object AMQPTest { - def enabled = true + def enabled = false } From 6e6f1f32d2b188259ea9136bece1a0dd0df18ea5 Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 6 Aug 2010 17:33:55 +0200 Subject: [PATCH 03/24] remove rpcclient trait... --- .../scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala index a711a01cc5..3097e64cd9 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala @@ -52,11 +52,7 @@ object RPC { /** * RPC convenience */ - trait RpcClient[O, I] { - def callService(request: O, timeout: Long = 5000): Option[I] - } - - private class RpcServiceClient[O, I](client: ActorRef) extends RpcClient[O, I] { + class RpcClient[O, I](client: ActorRef){ def callService(request: O, timeout: Long = 5000): Option[I] = { (client.!!(request, timeout)).as[I] } @@ -101,7 +97,7 @@ object RPC { val queueName = "%s.in".format(routingKey) val client = newRpcClient[O, I](connection, exchangeParameters, routingKey, serializer) - new RpcServiceClient[O, I](client) + new RpcClient[O, I](client) } private def createProtobufFromBytes[I](bytes: Array[Byte])(implicit manifest: Manifest[I]): I = { From 32ece975c550ac1cbc486ebd2960db4e9d87e6a8 Mon Sep 17 00:00:00 2001 From: momania Date: Mon, 9 Aug 2010 10:28:19 +0200 Subject: [PATCH 04/24] added optional routingkey and queuename to parameters --- .../scalablesolutions/akka/amqp/rpc/RPC.scala | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala index 3097e64cd9..26887dbff4 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala @@ -61,7 +61,9 @@ object RPC { private val ARRAY_OF_BYTE_ARRAY = Array[Class[_]](classOf[Array[Byte]]) def startProtobufServer[I <: Message, O <: Message]( - connection: ActorRef, serviceName: String, requestHandler: I => O)(implicit manifest: Manifest[I]) = { + connection: ActorRef, exchange: String, requestHandler: I => O, + routingKey: Option[String] = None, + queueName: Option[String] = None)(implicit manifest: Manifest[I]) = { val serializer = new RpcServerSerializer[I, O]( new FromBinary[I] { @@ -72,16 +74,18 @@ object RPC { def toBinary(t: O) = t.toByteArray }) - val exchangeParameters = new ExchangeParameters(serviceName, ExchangeType.Topic) - val routingKey = "%s.request".format(serviceName) - val queueName = "%s.in".format(routingKey) + val exchangeParameters = new ExchangeParameters(exchange, ExchangeType.Topic) + val rKey = routingKey.getOrElse("%s.request".format(exchange)) + val qName = queueName.getOrElse("%s.in".format(rKey)) - newRpcServer[I, O](connection, exchangeParameters, routingKey, serializer, requestHandler, - queueName = Some(queueName)) + newRpcServer[I, O](connection, exchangeParameters, rKey, serializer, requestHandler, + queueName = Some(qName)) } def startProtobufClient[O <: Message, I <: Message]( - connection: ActorRef, serviceName: String)(implicit manifest: Manifest[I]): RpcClient[O, I] = { + connection: ActorRef, exchange: String, + routingKey: Option[String] = None)(implicit manifest: Manifest[I]): RpcClient[O, I] = { + val serializer = new RpcClientSerializer[O, I]( new ToBinary[O] { @@ -92,11 +96,10 @@ object RPC { } }) - val exchangeParameters = new ExchangeParameters(serviceName, ExchangeType.Topic) - val routingKey = "%s.request".format(serviceName) - val queueName = "%s.in".format(routingKey) + val exchangeParameters = new ExchangeParameters(exchange, ExchangeType.Topic) + val rKey = routingKey.getOrElse("%s.request".format(exchange)) - val client = newRpcClient[O, I](connection, exchangeParameters, routingKey, serializer) + val client = newRpcClient[O, I](connection, exchangeParameters, rKey, serializer) new RpcClient[O, I](client) } From 2692e6fbfe6bf5f3a4a09e90d3dd04fd2956bb5d Mon Sep 17 00:00:00 2001 From: momania Date: Mon, 9 Aug 2010 10:42:00 +0200 Subject: [PATCH 05/24] undo local repo settings (for the 25953467296th time :S ) --- project/build/AkkaProject.scala | 49 ++++++++++++++++----------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 7e3477027f..bf1e9e6478 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -41,15 +41,15 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- object Repositories { -// lazy val AkkaRepo = MavenRepository("Akka Repository", "http://scalablesolutions.se/akka/repository") -// lazy val CodehausSnapshotRepo = MavenRepository("Codehaus Snapshots", "http://snapshots.repository.codehaus.org") + lazy val AkkaRepo = MavenRepository("Akka Repository", "http://scalablesolutions.se/akka/repository") + lazy val CodehausSnapshotRepo = MavenRepository("Codehaus Snapshots", "http://snapshots.repository.codehaus.org") lazy val EmbeddedRepo = MavenRepository("Embedded Repo", (info.projectPath / "embedded-repo").asURL.toString) -// lazy val FusesourceSnapshotRepo = MavenRepository("Fusesource Snapshots", "http://repo.fusesource.com/nexus/content/repositories/snapshots") -// lazy val GuiceyFruitRepo = MavenRepository("GuiceyFruit Repo", "http://guiceyfruit.googlecode.com/svn/repo/releases/") -// lazy val JBossRepo = MavenRepository("JBoss Repo", "https://repository.jboss.org/nexus/content/groups/public/") -// lazy val JavaNetRepo = MavenRepository("java.net Repo", "http://download.java.net/maven/2") -// lazy val SonatypeSnapshotRepo = MavenRepository("Sonatype OSS Repo", "http://oss.sonatype.org/content/repositories/releases") -// lazy val SunJDMKRepo = MavenRepository("Sun JDMK Repo", "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo") + lazy val FusesourceSnapshotRepo = MavenRepository("Fusesource Snapshots", "http://repo.fusesource.com/nexus/content/repositories/snapshots") + lazy val GuiceyFruitRepo = MavenRepository("GuiceyFruit Repo", "http://guiceyfruit.googlecode.com/svn/repo/releases/") + lazy val JBossRepo = MavenRepository("JBoss Repo", "https://repository.jboss.org/nexus/content/groups/public/") + lazy val JavaNetRepo = MavenRepository("java.net Repo", "http://download.java.net/maven/2") + lazy val SonatypeSnapshotRepo = MavenRepository("Sonatype OSS Repo", "http://oss.sonatype.org/content/repositories/releases") + lazy val SunJDMKRepo = MavenRepository("Sun JDMK Repo", "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo") } // ------------------------------------------------------------------------------------------------------------------- @@ -60,26 +60,23 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- import Repositories._ -// lazy val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", SonatypeSnapshotRepo) -// lazy val grizzlyModuleConfig = ModuleConfiguration("com.sun.grizzly", JavaNetRepo) -// lazy val guiceyFruitModuleConfig = ModuleConfiguration("org.guiceyfruit", GuiceyFruitRepo) -// // lazy val hawtdispatchModuleConfig = ModuleConfiguration("org.fusesource.hawtdispatch", FusesourceSnapshotRepo) -// lazy val jbossModuleConfig = ModuleConfiguration("org.jboss", JBossRepo) -// lazy val jdmkModuleConfig = ModuleConfiguration("com.sun.jdmk", SunJDMKRepo) -// lazy val jerseyContrModuleConfig = ModuleConfiguration("com.sun.jersey.contribs", JavaNetRepo) -// lazy val jerseyModuleConfig = ModuleConfiguration("com.sun.jersey", JavaNetRepo) -// lazy val jgroupsModuleConfig = ModuleConfiguration("jgroups", JBossRepo) -// lazy val jmsModuleConfig = ModuleConfiguration("javax.jms", SunJDMKRepo) -// lazy val jmxModuleConfig = ModuleConfiguration("com.sun.jmx", SunJDMKRepo) -// lazy val liftModuleConfig = ModuleConfiguration("net.liftweb", ScalaToolsSnapshots) -// lazy val multiverseModuleConfig = ModuleConfiguration("org.multiverse", CodehausSnapshotRepo) -// lazy val nettyModuleConfig = ModuleConfiguration("org.jboss.netty", JBossRepo) -// lazy val scalaTestModuleConfig = ModuleConfiguration("org.scalatest", ScalaToolsSnapshots) + lazy val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", SonatypeSnapshotRepo) + lazy val grizzlyModuleConfig = ModuleConfiguration("com.sun.grizzly", JavaNetRepo) + lazy val guiceyFruitModuleConfig = ModuleConfiguration("org.guiceyfruit", GuiceyFruitRepo) + // lazy val hawtdispatchModuleConfig = ModuleConfiguration("org.fusesource.hawtdispatch", FusesourceSnapshotRepo) + lazy val jbossModuleConfig = ModuleConfiguration("org.jboss", JBossRepo) + lazy val jdmkModuleConfig = ModuleConfiguration("com.sun.jdmk", SunJDMKRepo) + lazy val jerseyContrModuleConfig = ModuleConfiguration("com.sun.jersey.contribs", JavaNetRepo) + lazy val jerseyModuleConfig = ModuleConfiguration("com.sun.jersey", JavaNetRepo) + lazy val jgroupsModuleConfig = ModuleConfiguration("jgroups", JBossRepo) + lazy val jmsModuleConfig = ModuleConfiguration("javax.jms", SunJDMKRepo) + lazy val jmxModuleConfig = ModuleConfiguration("com.sun.jmx", SunJDMKRepo) + lazy val liftModuleConfig = ModuleConfiguration("net.liftweb", ScalaToolsSnapshots) + lazy val multiverseModuleConfig = ModuleConfiguration("org.multiverse", CodehausSnapshotRepo) + lazy val nettyModuleConfig = ModuleConfiguration("org.jboss.netty", JBossRepo) + lazy val scalaTestModuleConfig = ModuleConfiguration("org.scalatest", ScalaToolsSnapshots) lazy val embeddedRepo = EmbeddedRepo // This is the only exception, because the embedded repo is fast! - val mavenLocal = "Local Maven Repository" at "file:/e:/maven-repository" - val efgfpNexusReleasesRepository = "Nexus Releases" at "http://nexus/nexus/content/groups/public" - val efgfpNexusSnaphotsRepository = "Nexus Snapshots" at "http://nexus/nexus/content/groups/public-snapshots" // ------------------------------------------------------------------------------------------------------------------- // Versions // ------------------------------------------------------------------------------------------------------------------- From 9ef76c409b8251e6cc91aa67c94a5c7712274cfb Mon Sep 17 00:00:00 2001 From: momania Date: Tue, 10 Aug 2010 10:39:56 +0200 Subject: [PATCH 06/24] add durablility and auto-delete with defaults to rpc and with passive = true for client --- .../scalablesolutions/akka/amqp/rpc/RPC.scala | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala index 26887dbff4..60a1a6733c 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala @@ -13,7 +13,8 @@ object RPC { routingKey: String, serializer: RpcClientSerializer[O, I], channelParameters: Option[ChannelParameters] = None): ActorRef = { - val rpcActor: ActorRef = actorOf(new RpcClientActor[O, I](exchangeParameters, routingKey, serializer, channelParameters)) + val rpcActor: ActorRef = actorOf(new RpcClientActor[O, I]( + exchangeParameters, routingKey, serializer, channelParameters)) connection.startLink(rpcActor) rpcActor ! Start rpcActor @@ -26,9 +27,10 @@ object RPC { requestHandler: I => O, queueName: Option[String] = None, channelParameters: Option[ChannelParameters] = None) = { - val producer = newProducer(connection, new ProducerParameters(new ExchangeParameters("", ExchangeType.Direct), channelParameters = channelParameters)) + val producer = newProducer(connection, ProducerParameters( + ExchangeParameters("", ExchangeType.Direct), channelParameters = channelParameters)) val rpcServer = actorOf(new RpcServerActor[I, O](producer, serializer, requestHandler)) - val consumer = newConsumer(connection, new ConsumerParameters(exchangeParameters, routingKey, rpcServer + val consumer = newConsumer(connection, ConsumerParameters(exchangeParameters, routingKey, rpcServer , channelParameters = channelParameters , selfAcknowledging = false , queueName = queueName)) @@ -63,7 +65,8 @@ object RPC { def startProtobufServer[I <: Message, O <: Message]( connection: ActorRef, exchange: String, requestHandler: I => O, routingKey: Option[String] = None, - queueName: Option[String] = None)(implicit manifest: Manifest[I]) = { + queueName: Option[String] = None, + durable = false, autoDelete = true)(implicit manifest: Manifest[I]) = { val serializer = new RpcServerSerializer[I, O]( new FromBinary[I] { @@ -74,7 +77,8 @@ object RPC { def toBinary(t: O) = t.toByteArray }) - val exchangeParameters = new ExchangeParameters(exchange, ExchangeType.Topic) + val exchangeParameters = ExchangeParameters(exchange, ExchangeType.Topic, + exchangeDurable = durable, exchangeAutoDelete = autoDelete) val rKey = routingKey.getOrElse("%s.request".format(exchange)) val qName = queueName.getOrElse("%s.in".format(rKey)) @@ -84,7 +88,8 @@ object RPC { def startProtobufClient[O <: Message, I <: Message]( connection: ActorRef, exchange: String, - routingKey: Option[String] = None)(implicit manifest: Manifest[I]): RpcClient[O, I] = { + routingKey: Option[String] = None, + durable = false, autoDelete = true, passive = true)(implicit manifest: Manifest[I]): RpcClient[O, I] = { val serializer = new RpcClientSerializer[O, I]( @@ -96,7 +101,8 @@ object RPC { } }) - val exchangeParameters = new ExchangeParameters(exchange, ExchangeType.Topic) + val exchangeParameters = ExchangeParameters(exchange, ExchangeType.Topic, + exchangeDurable = durable, exchangeAutoDelete = autoDelete, exchangePassive = passive) val rKey = routingKey.getOrElse("%s.request".format(exchange)) val client = newRpcClient[O, I](connection, exchangeParameters, rKey, serializer) From 0a0e5465128609f827af4ca8e0a0e105ae631920 Mon Sep 17 00:00:00 2001 From: momania Date: Tue, 10 Aug 2010 11:30:56 +0200 Subject: [PATCH 07/24] types seem to help the parameter declaration :S --- .../main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala index 60a1a6733c..9a40139596 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala @@ -66,7 +66,7 @@ object RPC { connection: ActorRef, exchange: String, requestHandler: I => O, routingKey: Option[String] = None, queueName: Option[String] = None, - durable = false, autoDelete = true)(implicit manifest: Manifest[I]) = { + durable: Boolean = false, autoDelete: Boolean = true)(implicit manifest: Manifest[I]) = { val serializer = new RpcServerSerializer[I, O]( new FromBinary[I] { @@ -89,7 +89,8 @@ object RPC { def startProtobufClient[O <: Message, I <: Message]( connection: ActorRef, exchange: String, routingKey: Option[String] = None, - durable = false, autoDelete = true, passive = true)(implicit manifest: Manifest[I]): RpcClient[O, I] = { + durable: Boolean = false, autoDelete: Boolean = true, + passive: Boolean = true)(implicit manifest: Manifest[I]): RpcClient[O, I] = { val serializer = new RpcClientSerializer[O, I]( From 6f8750bd88689889e9f98c7c73082c181a5b1757 Mon Sep 17 00:00:00 2001 From: momania Date: Wed, 11 Aug 2010 10:09:04 +0200 Subject: [PATCH 08/24] manual rejection of delivery (for now by making it fail until new rabbitmq version has basicReject) --- .../se/scalablesolutions/akka/amqp/AMQP.scala | 13 ----- .../akka/amqp/AMQPMessage.scala | 3 ++ .../akka/amqp/ConsumerActor.scala | 14 +++++ .../test/AMQPConsumerManualRejectTest.scala | 53 +++++++++++++++++++ 4 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualRejectTest.scala diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala index df9c6ee79f..0ebfca40c3 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala @@ -105,17 +105,4 @@ object AMQP { connectionActor } } - - trait FromBinary[T] { - def fromBinary(bytes: Array[Byte]): T - } - - trait ToBinary[T] { - def toBinary(t: T): Array[Byte] - } - - - case class RpcClientSerializer[O,I](toBinary: ToBinary[O], fromBinary: FromBinary[I]) - - case class RpcServerSerializer[I,O](fromBinary: FromBinary[I], toBinary: ToBinary[O]) } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala index 92cd95906a..34eb37aa14 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala @@ -44,6 +44,9 @@ case object Stopped extends AMQPMessage // delivery messages case class Acknowledge(deliveryTag: Long) extends AMQPMessage case class Acknowledged(deliveryTag: Long) extends AMQPMessage +case class Reject(deliveryTag: Long) extends AMQPMessage +case class Rejected(deliveryTag: Long) extends AMQPMessage +class RejectionException(deliveryTag: Long) extends RuntimeException // internal messages private[akka] case class Failure(cause: Throwable) extends InternalAMQPMessage diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala index 90c8d7deec..39aa346274 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala @@ -23,6 +23,7 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) def specificMessageHandler = { case Acknowledge(deliveryTag) => acknowledgeDeliveryTag(deliveryTag, true) + case Reject(deliveryTag) => rejectDeliveryTag(deliveryTag, true) case message: Message => handleIllegalMessage("%s can't be used to send messages, ignoring message [%s]".format(this, message)) case unknown => @@ -82,6 +83,19 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) } } + private def rejectDeliveryTag(deliveryTag: Long, remoteAcknowledgement: Boolean) = { + log.debug("Rejecting message with delivery tag [%s]", deliveryTag) + // FIXME: when rabbitmq 1.9 arrives, basicReject should be available on the API and implemented instead of this + log.warning("Consumer is rejecting delivery with tag [%s] - " + + "for now this means we have to self terminate and kill the channel - see you in a second.") + channel.foreach{ch => + if (remoteAcknowledgement) { + deliveryHandler ! Rejected(deliveryTag) + } + } + throw new RejectionException(deliveryTag) + } + private def handleIllegalMessage(errorMessage: String) = { log.error(errorMessage) throw new IllegalArgumentException(errorMessage) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualRejectTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualRejectTest.scala new file mode 100644 index 0000000000..e928cb8ea9 --- /dev/null +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualRejectTest.scala @@ -0,0 +1,53 @@ +package se.scalablesolutions.akka.amqp.test + +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +import se.scalablesolutions.akka.actor.Actor._ +import org.scalatest.matchers.MustMatchers +import se.scalablesolutions.akka.amqp._ +import org.junit.Test +import se.scalablesolutions.akka.actor.ActorRef +import java.util.concurrent.{CountDownLatch, TimeUnit} +import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ConsumerParameters, ChannelParameters, ProducerParameters} +import org.multiverse.api.latches.StandardLatch +import org.scalatest.junit.JUnitSuite + +class AMQPConsumerManualRejectTest extends JUnitSuite with MustMatchers { + + @Test + def consumerMessageManualAcknowledge = if (AMQPTest.enabled) { + val connection = AMQP.newConnection() + try { + val countDown = new CountDownLatch(2) + val restartingLatch = new StandardLatch + val channelCallback = actor { + case Started => countDown.countDown + case Restarting => restartingLatch.open + case Stopped => () + } + val exchangeParameters = ExchangeParameters("text_exchange",ExchangeType.Direct) + val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) + + val rejectedLatch = new StandardLatch + val consumer:ActorRef = AMQP.newConsumer(connection, ConsumerParameters(exchangeParameters, "manual.reject.this", actor { + case Delivery(payload, _, deliveryTag, _, sender) => { + sender.foreach(_ ! Reject(deliveryTag)) + } + case Rejected(deliveryTag) => rejectedLatch.open + }, queueName = Some("self.reject.queue"), selfAcknowledging = false, queueAutoDelete = false, channelParameters = Some(channelParameters))) + + val producer = AMQP.newProducer(connection, + ProducerParameters(exchangeParameters, channelParameters = Some(channelParameters))) + + countDown.await(2, TimeUnit.SECONDS) must be (true) + producer ! Message("some_payload".getBytes, "manual.reject.this") + + rejectedLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) + restartingLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) + } finally { + connection.stop + } + } +} \ No newline at end of file From 7760a48a64a31a37cfd4a1d7982390345c1e612c Mon Sep 17 00:00:00 2001 From: momania Date: Wed, 11 Aug 2010 14:49:21 +0200 Subject: [PATCH 09/24] added async call with partial function callback to rpcclient --- .../se/scalablesolutions/akka/amqp/rpc/RPC.scala | 9 ++++++++- .../akka/amqp/test/AMQPRpcProtobufTest.scala | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala index 9a40139596..cb25444185 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala @@ -55,9 +55,16 @@ object RPC { * RPC convenience */ class RpcClient[O, I](client: ActorRef){ - def callService(request: O, timeout: Long = 5000): Option[I] = { + def call(request: O, timeout: Long = 5000): Option[I] = { (client.!!(request, timeout)).as[I] } + + def callAsync(request: O, timeout: Long = 5000)(responseHandler: PartialFunction[Option[I],Unit]) = { + spawn { + val result = call(request, timeout) + responseHandler.apply(result) + } + } } private val ARRAY_OF_BYTE_ARRAY = Array[Class[_]](classOf[Array[Byte]]) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTest.scala index b0ca3f8ffe..b936b821fe 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTest.scala @@ -9,6 +9,8 @@ import se.scalablesolutions.akka.amqp.AMQP import se.scalablesolutions.akka.remote.protocol.RemoteProtocol.AddressProtocol import org.junit.Test import se.scalablesolutions.akka.amqp.rpc.RPC +import org.multiverse.api.latches.StandardLatch +import java.util.concurrent.TimeUnit class AMQPRpcProtobufTest extends JUnitSuite with MustMatchers { @@ -23,10 +25,21 @@ class AMQPRpcProtobufTest extends JUnitSuite with MustMatchers { val request = AddressProtocol.newBuilder.setHostname("testhost").setPort(4321).build - protobufClient.callService(request) match { + protobufClient.call(request) match { case Some(response) => assert(response.getHostname == request.getHostname.reverse) case None => fail("no response") } + + val aSyncLatch = new StandardLatch + protobufClient.callAsync(request) { + case Some(response) => { + assert(response.getHostname == request.getHostname.reverse) + aSyncLatch.open + } + case None => fail("no response") + } + + aSyncLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) } def requestHandler(request: AddressProtocol): AddressProtocol = { From 26d8abf214b4f1be96f79dc67f3cd100ae90ab05 Mon Sep 17 00:00:00 2001 From: momania Date: Thu, 12 Aug 2010 10:53:39 +0200 Subject: [PATCH 10/24] added shutdownAll to be able to kill the whole actor tree, incl the amqp supervisor --- .../se/scalablesolutions/akka/amqp/AMQP.scala | 35 ++++++++----------- .../test/AMQPConnectionRecoveryTest.scala | 2 +- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala index 0ebfca40c3..9651054b90 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala @@ -9,7 +9,7 @@ import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.config.OneForOneStrategy import com.rabbitmq.client.{ReturnListener, ShutdownListener, ConnectionFactory} import java.lang.IllegalArgumentException -import se.scalablesolutions.akka.util.Logging + /** * AMQP Actor API. Implements Connection, Producer and Consumer materialized as Actors. * @@ -62,7 +62,8 @@ object AMQP { } def newConnection(connectionParameters: ConnectionParameters = new ConnectionParameters): ActorRef = { - val connection: ActorRef = supervisor.newConnection(connectionParameters) + val connection = actorOf(new FaultTolerantConnectionActor(connectionParameters)) + supervisor.startLink(connection) connection ! Connect connection } @@ -83,26 +84,20 @@ object AMQP { consumer } - private val supervisor = new AMQPSupervisor + class AMQPSupervisorActor extends Actor { + import self._ - class AMQPSupervisor extends Logging { - class AMQPSupervisorActor extends Actor { - import self._ + faultHandler = Some(OneForOneStrategy(5, 5000)) + trapExit = List(classOf[Throwable]) - faultHandler = Some(OneForOneStrategy(5, 5000)) - trapExit = List(classOf[Throwable]) - - def receive = { - case _ => {} // ignore all messages - } - } - - private val supervisor = actorOf(new AMQPSupervisorActor).start - - def newConnection(connectionParameters: ConnectionParameters): ActorRef = { - val connectionActor = actorOf(new FaultTolerantConnectionActor(connectionParameters)) - supervisor.startLink(connectionActor) - connectionActor + def receive = { + case _ => {} // ignore all messages } } + + private val supervisor = actorOf(new AMQPSupervisorActor).start + + def shutdownAll = { + supervisor.stop + } } diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTest.scala index ef2ee5b80f..a0dc21b7dc 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTest.scala @@ -44,7 +44,7 @@ class AMQPConnectionRecoveryTest extends JUnitSuite with MustMatchers { reconnectedLatch.tryAwait(2, TimeUnit.SECONDS) must be(true) } finally { - connection.stop + AMQP.shutdownAll disconnectedLatch.tryAwait(2, TimeUnit.SECONDS) must be(true) } } From b96f957dea00e29fd6c6465c13c8c01b7c27258f Mon Sep 17 00:00:00 2001 From: momania Date: Thu, 12 Aug 2010 11:07:36 +0200 Subject: [PATCH 11/24] shutdown linked actors too when shutting down supervisor --- .../src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala | 4 ++++ .../scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala | 2 +- .../akka/amqp/FaultTolerantConnectionActor.scala | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala index 9651054b90..b265cb38a1 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala @@ -93,6 +93,10 @@ object AMQP { def receive = { case _ => {} // ignore all messages } + + override def shutdown = { + self.shutdownLinkedActors + } } private val supervisor = actorOf(new AMQPSupervisorActor).start diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala index 39aa346274..b01f79f949 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala @@ -108,7 +108,7 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) override def shutdown = { listenerTag.foreach(tag => channel.foreach(_.basicCancel(tag))) - self.linkedActorsAsList.foreach(_.stop) + self.shutdownLinkedActors super.shutdown } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala index 97c3074700..1e50a985be 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala @@ -107,7 +107,7 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio override def shutdown = { reconnectionTimer.cancel // make sure shutdown is called on all linked actors so they can do channel cleanup before connection is killed - self.linkedActorsAsList.foreach(_.stop) + self.shutdownLinkedActors disconnect } From 5605895ea64aada5fbfdb9c864692df02bf6584f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Thu, 12 Aug 2010 15:36:05 +0200 Subject: [PATCH 12/24] Fixed #305. Invoking 'stop' on client-managed remote actors does not shut down remote instance (but only local) --- akka-core/src/main/scala/actor/ActorRef.scala | 13 +++++- .../main/scala/config/SupervisionConfig.scala | 9 +++- .../main/scala/remote/MessageSerializer.scala | 1 - .../src/main/scala/remote/RemoteClient.scala | 25 +++++++---- .../src/main/scala/remote/RemoteServer.scala | 42 ++++++++++--------- 5 files changed, 59 insertions(+), 31 deletions(-) diff --git a/akka-core/src/main/scala/actor/ActorRef.scala b/akka-core/src/main/scala/actor/ActorRef.scala index d4700b9ba0..ce954e41de 100644 --- a/akka-core/src/main/scala/actor/ActorRef.scala +++ b/akka-core/src/main/scala/actor/ActorRef.scala @@ -13,7 +13,7 @@ import se.scalablesolutions.akka.stm.TransactionManagement._ import se.scalablesolutions.akka.stm.{TransactionManagement, TransactionSetAbortedException} import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._ import se.scalablesolutions.akka.remote.{RemoteNode, RemoteServer, RemoteClient, MessageSerializer, RemoteRequestProtocolIdFactory} -import se.scalablesolutions.akka.serialization.Serializer +import se.scalablesolutions.akka.serialization.{Serializer, BinaryString} import se.scalablesolutions.akka.util.{HashCode, Logging, UUID, ReentrantGuard} import RemoteActorSerialization._ @@ -1253,6 +1253,15 @@ class LocalActorRef private[akka]( } else message } +/** + * System messages for RemoteActorRef. + * + * @author Jonas Bonér + */ +object RemoteActorSystemMessage { + val Stop = BinaryString("RemoteActorRef:stop") +} + /** * Remote ActorRef that is used when referencing the Actor on a different node than its "home" node. * This reference is network-aware (remembers its origin) and immutable. @@ -1263,6 +1272,7 @@ private[akka] case class RemoteActorRef private[akka] ( uuuid: String, val className: String, val hostname: String, val port: Int, _timeout: Long, loader: Option[ClassLoader]) // uuid: String, className: String, hostname: String, port: Int, timeOut: Long, isOnRemoteHost: Boolean) extends ActorRef { extends ActorRef { + _uuid = uuuid timeout = _timeout @@ -1291,6 +1301,7 @@ private[akka] case class RemoteActorRef private[akka] ( def stop: Unit = { _isRunning = false _isShutDown = true + postMessageToMailbox(RemoteActorSystemMessage.Stop, None) } /** diff --git a/akka-core/src/main/scala/config/SupervisionConfig.scala b/akka-core/src/main/scala/config/SupervisionConfig.scala index cb0829704d..071fdf0c12 100644 --- a/akka-core/src/main/scala/config/SupervisionConfig.scala +++ b/akka-core/src/main/scala/config/SupervisionConfig.scala @@ -4,7 +4,7 @@ package se.scalablesolutions.akka.config -import se.scalablesolutions.akka.actor.{Actor, ActorRef} +import se.scalablesolutions.akka.actor.{ActorRef, UntypedActorRef} import se.scalablesolutions.akka.dispatch.MessageDispatcher sealed abstract class FaultHandlingStrategy @@ -25,11 +25,15 @@ object ScalaConfig { case class SupervisorConfig(restartStrategy: RestartStrategy, worker: List[Server]) extends Server class Supervise(val actorRef: ActorRef, val lifeCycle: LifeCycle, _remoteAddress: RemoteAddress) extends Server { + def this(actorRef: UntypedActorRef, lifeCycle: LifeCycle, _remoteAddress: RemoteAddress) = + this(actorRef.actorRef, lifeCycle, _remoteAddress) val remoteAddress: Option[RemoteAddress] = if (_remoteAddress eq null) None else Some(_remoteAddress) } object Supervise { def apply(actorRef: ActorRef, lifeCycle: LifeCycle, remoteAddress: RemoteAddress) = new Supervise(actorRef, lifeCycle, remoteAddress) def apply(actorRef: ActorRef, lifeCycle: LifeCycle) = new Supervise(actorRef, lifeCycle, null) + def apply(actorRef: UntypedActorRef, lifeCycle: LifeCycle, remoteAddress: RemoteAddress) = new Supervise(actorRef, lifeCycle, remoteAddress) + def apply(actorRef: UntypedActorRef, lifeCycle: LifeCycle) = new Supervise(actorRef, lifeCycle, null) def unapply(supervise: Supervise) = Some((supervise.actorRef, supervise.lifeCycle, supervise.remoteAddress)) } @@ -215,6 +219,9 @@ object JavaConfig { def newSupervised(actorRef: ActorRef) = se.scalablesolutions.akka.config.ScalaConfig.Supervise(actorRef, lifeCycle.transform) + + def newSupervised(actorRef: UntypedActorRef) = + se.scalablesolutions.akka.config.ScalaConfig.Supervise(actorRef, lifeCycle.transform) } } diff --git a/akka-core/src/main/scala/remote/MessageSerializer.scala b/akka-core/src/main/scala/remote/MessageSerializer.scala index 24269c7f8e..8ef6f5d590 100644 --- a/akka-core/src/main/scala/remote/MessageSerializer.scala +++ b/akka-core/src/main/scala/remote/MessageSerializer.scala @@ -25,7 +25,6 @@ object MessageSerializer extends Logging { } def deserialize(messageProtocol: MessageProtocol): Any = { - log.debug("scheme = " + messageProtocol.getSerializationScheme) messageProtocol.getSerializationScheme match { case SerializationSchemeType.JAVA => unbox(SERIALIZER_JAVA.fromBinary(messageProtocol.getMessage.toByteArray, None)) diff --git a/akka-core/src/main/scala/remote/RemoteClient.scala b/akka-core/src/main/scala/remote/RemoteClient.scala index c1bd574c3e..fefb1521ed 100644 --- a/akka-core/src/main/scala/remote/RemoteClient.scala +++ b/akka-core/src/main/scala/remote/RemoteClient.scala @@ -44,9 +44,16 @@ object RemoteRequestProtocolIdFactory { * Life-cycle events for RemoteClient. */ sealed trait RemoteClientLifeCycleEvent -case class RemoteClientError(@BeanProperty val cause: Throwable, @BeanProperty val host: String, @BeanProperty val port: Int) extends RemoteClientLifeCycleEvent -case class RemoteClientDisconnected(@BeanProperty val host: String, @BeanProperty val port: Int) extends RemoteClientLifeCycleEvent -case class RemoteClientConnected(@BeanProperty val host: String, @BeanProperty val port: Int) extends RemoteClientLifeCycleEvent +case class RemoteClientError( + @BeanProperty val cause: Throwable, + @BeanProperty val host: String, + @BeanProperty val port: Int) extends RemoteClientLifeCycleEvent +case class RemoteClientDisconnected( + @BeanProperty val host: String, + @BeanProperty val port: Int) extends RemoteClientLifeCycleEvent +case class RemoteClientConnected( + @BeanProperty val host: String, + @BeanProperty val port: Int) extends RemoteClientLifeCycleEvent class RemoteClientException private[akka](message: String) extends RuntimeException(message) @@ -259,23 +266,23 @@ class RemoteClientPipelineFactory( remoteAddress: SocketAddress, timer: HashedWheelTimer, client: RemoteClient) extends ChannelPipelineFactory { - def getPipeline: ChannelPipeline = { - def join(ch: ChannelHandler*) = Array[ChannelHandler](ch:_*) + def getPipeline: ChannelPipeline = { + def join(ch: ChannelHandler*) = Array[ChannelHandler](ch: _*) val engine = RemoteServerSslContext.client.createSSLEngine() engine.setEnabledCipherSuites(engine.getSupportedCipherSuites) //TODO is this sensible? engine.setUseClientMode(true) - val ssl = if(RemoteServer.SECURE) join(new SslHandler(engine)) else join() + val ssl = if (RemoteServer.SECURE) join(new SslHandler(engine)) else join() val timeout = new ReadTimeoutHandler(timer, RemoteClient.READ_TIMEOUT.toMillis.toInt) val lenDec = new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4) val lenPrep = new LengthFieldPrepender(4) val protobufDec = new ProtobufDecoder(RemoteReplyProtocol.getDefaultInstance) val protobufEnc = new ProtobufEncoder - val(enc,dec) = RemoteServer.COMPRESSION_SCHEME match { - case "zlib" => (join(new ZlibEncoder(RemoteServer.ZLIB_COMPRESSION_LEVEL)),join(new ZlibDecoder)) - case _ => (join(),join()) + val (enc, dec) = RemoteServer.COMPRESSION_SCHEME match { + case "zlib" => (join(new ZlibEncoder(RemoteServer.ZLIB_COMPRESSION_LEVEL)), join(new ZlibDecoder)) + case _ => (join(), join()) } val remoteClient = new RemoteClientHandler(name, futures, supervisors, bootstrap, remoteAddress, timer, client) diff --git a/akka-core/src/main/scala/remote/RemoteServer.scala b/akka-core/src/main/scala/remote/RemoteServer.scala index 89c0a6e437..ec33bd8600 100644 --- a/akka-core/src/main/scala/remote/RemoteServer.scala +++ b/akka-core/src/main/scala/remote/RemoteServer.scala @@ -309,10 +309,10 @@ object RemoteServerSslContext { //val algorithm = Option(Security.getProperty("ssl.KeyManagerFactory.algorithm")).getOrElse("SunX509") //val store = KeyStore.getInstance("JKS") val s = SSLContext.getInstance(protocol) - s.init(null,null,null) + s.init(null, null, null) val c = SSLContext.getInstance(protocol) - c.init(null,null,null) - (c,s) + c.init(null, null, null) + (c, s) } } @@ -429,25 +429,29 @@ class RemoteServerHandler( if (request.hasSender) Some(RemoteActorSerialization.fromProtobufToRemoteActorRef(request.getSender, applicationLoader)) else None - if (request.getIsOneWay) actorRef.!(message)(sender) - else { - try { - val resultOrNone = (actorRef.!!(message)(sender)).as[AnyRef] - val result = if (resultOrNone.isDefined) resultOrNone.get else null + message match { // first match on system messages + case RemoteActorSystemMessage.Stop => actorRef.stop + case _ => // then match on user defined messages + if (request.getIsOneWay) actorRef.!(message)(sender) + else { + try { + val resultOrNone = (actorRef.!!(message)(sender)).as[AnyRef] + val result = if (resultOrNone.isDefined) resultOrNone.get else null - log.debug("Returning result from actor invocation [%s]", result) - val replyBuilder = RemoteReplyProtocol.newBuilder - .setId(request.getId) - .setMessage(MessageSerializer.serialize(result)) - .setIsSuccessful(true) - .setIsActor(true) + log.debug("Returning result from actor invocation [%s]", result) + val replyBuilder = RemoteReplyProtocol.newBuilder + .setId(request.getId) + .setMessage(MessageSerializer.serialize(result)) + .setIsSuccessful(true) + .setIsActor(true) - if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid) - channel.write(replyBuilder.build) + if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid) + channel.write(replyBuilder.build) - } catch { - case e: Throwable => channel.write(createErrorReplyMessage(e, request, true)) - } + } catch { + case e: Throwable => channel.write(createErrorReplyMessage(e, request, true)) + } + } } } From a16e909521a6c75d1ed493982f2b19c671eb20af Mon Sep 17 00:00:00 2001 From: Michael Kober Date: Wed, 11 Aug 2010 19:59:32 +0200 Subject: [PATCH 13/24] ported unit tests for spring config from java to scala, removed akka-spring-test-java --- akka-spring/akka-spring-test-java/pom.xml | 339 ------------------ .../spring/DispatcherConfigurationTest.java | 143 -------- .../spring/SupervisorConfigurationTest.java | 135 ------- .../spring/TypedActorConfigurationTest.java | 99 ----- .../akka/spring/akka-0.10.xsd | 2 + .../scala/AkkaSpringConfigurationTags.scala | 2 + .../main/scala/DispatcherFactoryBean.scala | 2 + .../src/main/scala/DispatcherParser.scala | 3 +- .../akka/spring/foo/Bar.java | 3 +- .../akka/spring/foo/Foo.java | 4 +- .../akka/spring/foo/IBar.java | 0 .../akka/spring/foo/IFoo.java | 12 + .../akka/spring/foo/IMyPojo.java | 21 ++ .../akka/spring/foo/MyPojo.java | 4 +- .../akka/spring/foo/StatefulPojo.java | 12 +- .../test/resources}/dispatcher-config.xml | 52 ++- .../test/resources}/supervisor-config.xml | 32 +- .../test/resources/typed-actor-config.xml} | 34 +- .../DispatcherBeanDefinitionParserTest.scala | 14 +- .../scala/DispatcherSpringFeatureTest.scala | 134 +++++++ .../scala/SupervisorSpringFeatureTest.scala | 105 ++++++ .../scala/TypedActorSpringFeatureTest.scala | 78 ++++ 22 files changed, 466 insertions(+), 764 deletions(-) delete mode 100644 akka-spring/akka-spring-test-java/pom.xml delete mode 100644 akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/DispatcherConfigurationTest.java delete mode 100644 akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/SupervisorConfigurationTest.java delete mode 100644 akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/TypedActorConfigurationTest.java rename akka-spring/{akka-spring-test-java/src/main => src/test}/java/se/scalablesolutions/akka/spring/foo/Bar.java (76%) rename akka-spring/{akka-spring-test-java/src/main => src/test}/java/se/scalablesolutions/akka/spring/foo/Foo.java (50%) rename akka-spring/{akka-spring-test-java/src/main => src/test}/java/se/scalablesolutions/akka/spring/foo/IBar.java (100%) create mode 100644 akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/IFoo.java create mode 100644 akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/IMyPojo.java rename akka-spring/{akka-spring-test-java/src/main => src/test}/java/se/scalablesolutions/akka/spring/foo/MyPojo.java (88%) rename akka-spring/{akka-spring-test-java/src/main => src/test}/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java (88%) rename akka-spring/{akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo => src/test/resources}/dispatcher-config.xml (60%) rename akka-spring/{akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo => src/test/resources}/supervisor-config.xml (73%) rename akka-spring/{akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/test-config.xml => src/test/resources/typed-actor-config.xml} (67%) create mode 100644 akka-spring/src/test/scala/DispatcherSpringFeatureTest.scala create mode 100644 akka-spring/src/test/scala/SupervisorSpringFeatureTest.scala create mode 100644 akka-spring/src/test/scala/TypedActorSpringFeatureTest.scala diff --git a/akka-spring/akka-spring-test-java/pom.xml b/akka-spring/akka-spring-test-java/pom.xml deleted file mode 100644 index 2d03b53032..0000000000 --- a/akka-spring/akka-spring-test-java/pom.xml +++ /dev/null @@ -1,339 +0,0 @@ - - 4.0.0 - - Akka Spring Tests in Java - akka-spring-test-java - se.scalablesolutions.akka - 0.9 - jar - - - 2.8.0.Beta1 - 0.5.2 - 1.1.5 - 1.9.18-i - - - - - akka - Akka Repo - http://www.scalablesolutions.se/akka/repository/ - - - project.embedded.module - Project Embedded Repository - file://${env.AKKA_HOME}/embedded-repo - - - repo1.maven - Maven Main Repository - http://repo1.maven.org/maven2 - - - scala-tools-snapshots - Scala-Tools Maven2 Snapshot Repository - http://scala-tools.org/repo-snapshots - - - scala-tools - Scala-Tools Maven2 Repository - http://scala-tools.org/repo-releases - - - lag - Configgy's' Repository - http://www.lag.net/repo - - - multiverse-releases - http://multiverse.googlecode.com/svn/maven-repository/releases - - false - - - - multiverse-snaphosts - http://multiverse.googlecode.com/svn/maven-repository/snapshots - - - maven2-repository.dev.java.net - Java.net Repository for Maven - http://download.java.net/maven/2 - - - java.net - Java.net Legacy Repository for Maven - http://download.java.net/maven/1 - legacy - - - guiceyfruit.release - GuiceyFruit Release Repository - http://guiceyfruit.googlecode.com/svn/repo/releases/ - - false - - - true - - - - guiceyfruit.snapshot - GuiceyFruit Snapshot Repository - http://guiceyfruit.googlecode.com/svn/repo/snapshots/ - - true - - - false - - - - guice-maven - guice maven - http://guice-maven.googlecode.com/svn/trunk - - - google-maven-repository - Google Maven Repository - http://google-maven-repository.googlecode.com/svn/repository/ - - - repository.codehaus.org - Codehaus Maven Repository - http://repository.codehaus.org - - true - - - - repository.jboss.org - JBoss Repository for Maven - http://repository.jboss.org/maven2 - - false - - - - nexus.griddynamics.net - Grid Dynamics Maven Repository - https://nexus.griddynamics.net/nexus/content/groups/public - - false - - - - databinder.net/repo/ - dbDispatch Repository for Maven - http://databinder.net/repo - - false - - - - - - - - org.scala-lang - scala-compiler - 2.8.0.Beta1 - - - org.scala-lang - scala-library - 2.8.0.Beta1 - - - - - se.scalablesolutions.akka - akka-core_2.8.0.RC3 - 0.9.1 - - - se.scalablesolutions.akka - akka-util_2.8.0.Beta1 - 0.8.1 - - - se.scalablesolutions.akka - akka-util-java_2.8.0.Beta1 - 0.8.1 - - - se.scalablesolutions.akka - akka-spring_2.8.0.RC3 - 0.9.1 - - - org.springframework - spring - - - - - - - org.springframework - spring-beans - 3.0.1.RELEASE - - - org.springframework - spring-context - 3.0.1.RELEASE - - - net.lag - configgy - 2.8.0.Beta1-1.5-SNAPSHOT - - - org.codehaus.aspectwerkz - aspectwerkz-nodeps-jdk5 - 2.1 - - - org.codehaus.aspectwerkz - aspectwerkz-jdk5 - 2.1 - - - org.guiceyfruit - guice-core - 2.0-beta-4 - - - com.google.protobuf - protobuf-java - 2.2.0 - - - com.google.protobuf - protobuf-java - 2.2.0 - - - org.multiverse - multiverse-alpha - 0.4-SNAPSHOT - - - commons-io - commons-io - 1.4 - - - org.jboss.netty - netty - 3.2.0.BETA1 - - - net.databinder - dispatch-json_2.8.0.Beta1 - 0.6.6 - - - net.databinder - dispatch-http_2.8.0.Beta1 - 0.6.6 - - - sjson.json - sjson - 0.5-SNAPSHOT-2.8.Beta1 - - - - sbinary - sbinary - 2.8.0.Beta1-2.8.0.Beta1-0.3.1-SNAPSHOT - - - org.codehaus.jackson - jackson-mapper-asl - 1.2.1 - - - org.codehaus.jackson - jackson-core-asl - 1.2.1 - - - voldemort.store.compress - h2-lzf - 1.0 - - - - jsr166x - jsr166x - 1.0 - - - org.apache.geronimo.specs - geronimo-jta_1.1_spec - 1.1.1 - - - - - junit - junit - 4.5 - test - - - - - src/main/java - src/test/java - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.5 - 1.5 - - **/* - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - **/*Persistent* - - - - - - - false - src/test/resources - - - false - src/main/resources - - - false - src/test/java - - ** - - - **/*.java - - - - - diff --git a/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/DispatcherConfigurationTest.java b/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/DispatcherConfigurationTest.java deleted file mode 100644 index 862d781802..0000000000 --- a/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/DispatcherConfigurationTest.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ -package se.scalablesolutions.akka.spring; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -import se.scalablesolutions.akka.dispatch.ExecutorBasedEventDrivenDispatcher; -import se.scalablesolutions.akka.dispatch.ReactorBasedThreadPoolEventDrivenDispatcher; -import se.scalablesolutions.akka.dispatch.ReactorBasedSingleThreadEventDrivenDispatcher; -import se.scalablesolutions.akka.dispatch.MessageDispatcher; -import se.scalablesolutions.akka.dispatch.ThreadPoolBuilder; - -import se.scalablesolutions.akka.spring.foo.MyPojo; - -/** - * Tests for spring configuration of dispatcher configuration. - * @author michaelkober - */ -public class DispatcherConfigurationTest { - - private ApplicationContext context = null; - - @Before - public void setUp() { - context = new ClassPathXmlApplicationContext("se/scalablesolutions/akka/spring/foo/dispatcher-config.xml"); - } - - /** - * test for executor-event-driven-dispatcher with array-blocking-queue - */ - @Test - public void testDispatcher() { - MessageDispatcher dispatcher = (MessageDispatcher) context.getBean("executor-event-driven-dispatcher-1"); - ThreadPoolExecutor executor = getThreadPoolExecutorAndAssert(dispatcher); - assertEquals("wrong core pool size", 1, executor.getCorePoolSize()); - assertEquals("wrong max pool size", 20, executor.getMaximumPoolSize()); - assertEquals("wrong keep alive", 3000, executor.getKeepAliveTime(TimeUnit.MILLISECONDS)); - assertTrue("wrong queue type",executor.getQueue() instanceof ArrayBlockingQueue); - assertEquals("wrong capacity", 100, executor.getQueue().remainingCapacity()); - } - - /** - * test for dispatcher via ref - */ - @Test - public void testDispatcherRef() { - MyPojo pojo = (MyPojo) context.getBean("typed-actor-with-dispatcher-ref"); - assertNotNull(pojo); - } - - /** - * test for executor-event-driven-dispatcher with bounded-linked-blocking-queue with unbounded capacity - */ - @Test - public void testDispatcherWithBoundedLinkedBlockingQueueWithUnboundedCapacity() { - MessageDispatcher dispatcher = (MessageDispatcher) context.getBean("executor-event-driven-dispatcher-2"); - ThreadPoolExecutor executor = getThreadPoolExecutorAndAssert(dispatcher); - assertTrue("wrong queue type", executor.getQueue() instanceof LinkedBlockingQueue); - assertEquals("wrong capacity", Integer.MAX_VALUE, executor.getQueue().remainingCapacity()); - } - - /** - * test for executor-event-driven-dispatcher with unbounded-linked-blocking-queue with bounded capacity - */ - @Test - public void testDispatcherWithLinkedBlockingQueueWithBoundedCapacity() { - MessageDispatcher dispatcher = (MessageDispatcher) context.getBean("executor-event-driven-dispatcher-4"); - ThreadPoolExecutor executor = getThreadPoolExecutorAndAssert(dispatcher); - assertTrue("wrong queue type", executor.getQueue() instanceof LinkedBlockingQueue); - assertEquals("wrong capacity", 55, executor.getQueue().remainingCapacity()); - } - - /** - * test for executor-event-driven-dispatcher with unbounded-linked-blocking-queue with unbounded capacity - */ - @Test - public void testDispatcherWithLinkedBlockingQueueWithUnboundedCapacity() { - MessageDispatcher dispatcher = (MessageDispatcher) context.getBean("executor-event-driven-dispatcher-5"); - ThreadPoolExecutor executor = getThreadPoolExecutorAndAssert(dispatcher); - assertTrue("wrong queue type", executor.getQueue() instanceof LinkedBlockingQueue); - assertEquals("wrong capacity", Integer.MAX_VALUE, executor.getQueue().remainingCapacity()); - } - - /** - * test for executor-event-driven-dispatcher with synchronous-queue - */ - @Test - public void testDispatcherWithSynchronousQueue() { - MessageDispatcher dispatcher = (MessageDispatcher) context.getBean("executor-event-driven-dispatcher-6"); - ThreadPoolExecutor executor = getThreadPoolExecutorAndAssert(dispatcher); - assertTrue("wrong queue type", executor.getQueue() instanceof SynchronousQueue); - } - - /** - * test for reactor-based-thread-pool-event-driven-dispatcher with synchronous-queue - */ - @Test - public void testReactorBasedThreadPoolDispatcherWithSynchronousQueue() { - MessageDispatcher dispatcher = (MessageDispatcher) context.getBean("reactor-based-thread-pool-event-driven-dispatcher"); - assertTrue(dispatcher instanceof ReactorBasedThreadPoolEventDrivenDispatcher); - assertTrue(dispatcher instanceof ThreadPoolBuilder); - ThreadPoolBuilder pool = (ThreadPoolBuilder) dispatcher; - ThreadPoolExecutor executor = pool.se$scalablesolutions$akka$dispatch$ThreadPoolBuilder$$threadPoolBuilder(); - assertNotNull(executor); - assertTrue("wrong queue type", executor.getQueue() instanceof SynchronousQueue); - } - - /** - * test for reactor-based-single-thread-event-driven-dispatcher with synchronous-queue - */ - @Test - public void testReactorBasedSingleThreadDispatcherWithSynchronousQueue() { - MessageDispatcher dispatcher = (MessageDispatcher) context.getBean("reactor-based-single-thread-event-driven-dispatcher"); - assertTrue(dispatcher instanceof ReactorBasedSingleThreadEventDrivenDispatcher); - } - - /** - * Assert that dispatcher is correct type and get executor. - */ - private ThreadPoolExecutor getThreadPoolExecutorAndAssert(MessageDispatcher dispatcher) { - assertTrue(dispatcher instanceof ExecutorBasedEventDrivenDispatcher); - assertTrue(dispatcher instanceof ThreadPoolBuilder); - ThreadPoolBuilder pool = (ThreadPoolBuilder) dispatcher; - ThreadPoolExecutor executor = pool.se$scalablesolutions$akka$dispatch$ThreadPoolBuilder$$threadPoolBuilder(); - assertNotNull(executor); - return executor; - } - -} diff --git a/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/SupervisorConfigurationTest.java b/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/SupervisorConfigurationTest.java deleted file mode 100644 index c90fd56b72..0000000000 --- a/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/SupervisorConfigurationTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ -package se.scalablesolutions.akka.spring; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import net.lag.configgy.Config; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -import se.scalablesolutions.akka.actor.TypedActor; -import se.scalablesolutions.akka.config.TypedActorConfigurator; -import se.scalablesolutions.akka.config.JavaConfig.AllForOne; -import se.scalablesolutions.akka.config.JavaConfig.Component; -import se.scalablesolutions.akka.config.JavaConfig.LifeCycle; -import se.scalablesolutions.akka.config.JavaConfig.Permanent; -import se.scalablesolutions.akka.config.JavaConfig.RemoteAddress; -import se.scalablesolutions.akka.config.JavaConfig.RestartStrategy; -import se.scalablesolutions.akka.remote.RemoteNode; -import se.scalablesolutions.akka.spring.foo.Foo; -import se.scalablesolutions.akka.spring.foo.IBar; -import se.scalablesolutions.akka.spring.foo.MyPojo; -import se.scalablesolutions.akka.spring.foo.StatefulPojo; - -/** - * Testclass for supervisor configuration. - * - * @author michaelkober - * - */ -public class SupervisorConfigurationTest { - - private ApplicationContext context = null; - - @Before - public void setUp() { - context = new ClassPathXmlApplicationContext( - "se/scalablesolutions/akka/spring/foo/supervisor-config.xml"); - } - - @Test - public void testSupervision() { - // get TypedActorConfigurator bean from spring context - TypedActorConfigurator myConfigurator = (TypedActorConfigurator) context - .getBean("supervision1"); - // get TypedActors - Foo foo = myConfigurator.getInstance(Foo.class); - assertNotNull(foo); - IBar bar = myConfigurator.getInstance(IBar.class); - assertNotNull(bar); - MyPojo pojo = myConfigurator.getInstance(MyPojo.class); - assertNotNull(pojo); - } - - @Test - public void testTransactionalState() { - TypedActorConfigurator conf = (TypedActorConfigurator) context - .getBean("supervision2"); - StatefulPojo stateful = conf.getInstance(StatefulPojo.class); - stateful.setMapState("testTransactionalState", "some map state"); - stateful.setVectorState("some vector state"); - stateful.setRefState("some ref state"); - assertEquals("some map state", stateful - .getMapState("testTransactionalState")); - assertEquals("some vector state", stateful.getVectorState()); - assertEquals("some ref state", stateful.getRefState()); - } - - @Test - public void testInitTransactionalState() { - StatefulPojo stateful = TypedActor.newInstance(StatefulPojo.class, - 1000, true); - assertTrue("should be inititalized", stateful.isInitialized()); - } - - @Test - public void testSupervisionWithDispatcher() { - TypedActorConfigurator myConfigurator = (TypedActorConfigurator) context - .getBean("supervision-with-dispatcher"); - // get TypedActors - Foo foo = myConfigurator.getInstance(Foo.class); - assertNotNull(foo); - // TODO how to check dispatcher? - } - - @Test - public void testRemoteTypedActor() { - new Thread(new Runnable() { - public void run() { - RemoteNode.start(); - } - }).start(); - try { - Thread.currentThread().sleep(1000); - } catch (Exception e) { - } - Foo instance = TypedActor.newRemoteInstance(Foo.class, 2000, "localhost", 9999); - System.out.println(instance.foo()); - } - - - @Test - public void testSupervisedRemoteTypedActor() { - new Thread(new Runnable() { - public void run() { - RemoteNode.start(); - } - }).start(); - try { - Thread.currentThread().sleep(1000); - } catch (Exception e) { - } - - TypedActorConfigurator conf = new TypedActorConfigurator(); - conf.configure( - new RestartStrategy(new AllForOne(), 3, 10000, new Class[] { Exception.class }), - new Component[] { - new Component( - Foo.class, - new LifeCycle(new Permanent()), - 10000, - new RemoteAddress("localhost", 9999)) - }).supervise(); - - Foo instance = conf.getInstance(Foo.class); - assertEquals("foo", instance.foo()); - } - - -} diff --git a/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/TypedActorConfigurationTest.java b/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/TypedActorConfigurationTest.java deleted file mode 100644 index e8931fd1a2..0000000000 --- a/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/TypedActorConfigurationTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ -package se.scalablesolutions.akka.spring; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; - -import se.scalablesolutions.akka.config.Config; -import se.scalablesolutions.akka.dispatch.FutureTimeoutException; -import se.scalablesolutions.akka.remote.RemoteNode; -import se.scalablesolutions.akka.spring.foo.MyPojo; - -/** - * Tests for spring configuration of typed actors and supervisor configuration. - * @author michaelkober - */ -public class TypedActorConfigurationTest { - - private ApplicationContext context = null; - - @Before - public void setUp() { - context = new ClassPathXmlApplicationContext("se/scalablesolutions/akka/spring/foo/test-config.xml"); - } - - /** - * Tests that the <akka:typed-actor/> and <akka:supervision/> and <akka:dispatcher/> element - * can be used as a top level element. - */ - @Test - public void testParse() throws Exception { - final Resource CONTEXT = new ClassPathResource("se/scalablesolutions/akka/spring/foo/test-config.xml"); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); - reader.loadBeanDefinitions(CONTEXT); - assertTrue(beanFactory.containsBeanDefinition("simple-typed-actor")); - assertTrue(beanFactory.containsBeanDefinition("remote-typed-actor")); - assertTrue(beanFactory.containsBeanDefinition("supervision1")); - assertTrue(beanFactory.containsBeanDefinition("dispatcher1")); - } - - @Test - public void testSimpleTypedActor() { - MyPojo myPojo = (MyPojo) context.getBean("simple-typed-actor"); - String msg = myPojo.getFoo(); - msg += myPojo.getBar(); - assertEquals("wrong invocation order", "foobar", msg); - } - - @Test(expected = FutureTimeoutException.class) - public void testSimpleTypedActor_Timeout() { - MyPojo myPojo = (MyPojo) context.getBean("simple-typed-actor"); - myPojo.longRunning(); - } - - @Test - public void testSimpleTypedActor_NoTimeout() { - MyPojo myPojo = (MyPojo) context.getBean("simple-typed-actor-long-timeout"); - String msg = myPojo.longRunning(); - assertEquals("this took long", msg); - } - - @Test - public void testTransactionalTypedActor() { - MyPojo myPojo = (MyPojo) context.getBean("transactional-typed-actor"); - String msg = myPojo.getFoo(); - msg += myPojo.getBar(); - assertEquals("wrong invocation order", "foobar", msg); - } - - @Test - public void testRemoteTypedActor() { - new Thread(new Runnable() { - public void run() { - RemoteNode.start(); - } - }).start(); - try { - Thread.currentThread().sleep(1000); - } catch (Exception e) { - } - Config.config(); - - MyPojo myPojo = (MyPojo) context.getBean("remote-typed-actor"); - assertEquals("foo", myPojo.getFoo()); - } - - -} diff --git a/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd index 6dd0ee7681..76cd477a10 100644 --- a/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd +++ b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd @@ -38,9 +38,11 @@ + + diff --git a/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala b/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala index 857d20fa55..6b598a4a31 100644 --- a/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala +++ b/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala @@ -93,8 +93,10 @@ object AkkaSpringConfigurationTags { // dispatcher types val EXECUTOR_BASED_EVENT_DRIVEN = "executor-based-event-driven" + val EXECUTOR_BASED_EVENT_DRIVEN_WORK_STEALING = "executor-based-event-driven-work-stealing" val REACTOR_BASED_THREAD_POOL_EVENT_DRIVEN = "reactor-based-thread-pool-event-driven" val REACTOR_BASED_SINGLE_THREAD_EVENT_DRIVEN = "reactor-based-single-thread-event-driven" val THREAD_BASED = "thread-based" + val HAWT = "hawt" } diff --git a/akka-spring/src/main/scala/DispatcherFactoryBean.scala b/akka-spring/src/main/scala/DispatcherFactoryBean.scala index ac4172a696..5eafdda4f3 100644 --- a/akka-spring/src/main/scala/DispatcherFactoryBean.scala +++ b/akka-spring/src/main/scala/DispatcherFactoryBean.scala @@ -18,9 +18,11 @@ object DispatcherFactoryBean { def createNewInstance(properties: DispatcherProperties): MessageDispatcher = { var dispatcher = properties.dispatcherType match { case EXECUTOR_BASED_EVENT_DRIVEN => Dispatchers.newExecutorBasedEventDrivenDispatcher(properties.name) + case EXECUTOR_BASED_EVENT_DRIVEN_WORK_STEALING => Dispatchers.newExecutorBasedEventDrivenWorkStealingDispatcher(properties.name) case REACTOR_BASED_THREAD_POOL_EVENT_DRIVEN => Dispatchers.newReactorBasedThreadPoolEventDrivenDispatcher(properties.name) case REACTOR_BASED_SINGLE_THREAD_EVENT_DRIVEN => Dispatchers.newReactorBasedSingleThreadEventDrivenDispatcher(properties.name) case THREAD_BASED => throw new IllegalArgumentException("not implemented yet") //FIXME + case HAWT => throw new IllegalArgumentException("not implemented yet") //FIXME case _ => throw new IllegalArgumentException("unknown dispatcher type") } if ((properties.threadPool != null) && (properties.threadPool.queue != null)) { diff --git a/akka-spring/src/main/scala/DispatcherParser.scala b/akka-spring/src/main/scala/DispatcherParser.scala index fb9855102e..c3f802018e 100644 --- a/akka-spring/src/main/scala/DispatcherParser.scala +++ b/akka-spring/src/main/scala/DispatcherParser.scala @@ -31,7 +31,8 @@ trait DispatcherParser extends BeanParser { properties.name = mandatory(dispatcherElement, NAME) properties.dispatcherType = mandatory(dispatcherElement, TYPE) if (properties.dispatcherType == THREAD_BASED) { - if (dispatcherElement.getParentNode.getNodeName != "typed-actor") { + if ((dispatcherElement.getParentNode.getNodeName != "akka:typed-actor") && + (dispatcherElement.getParentNode.getNodeName != "typed-actor")) { throw new IllegalArgumentException("Thread based dispatcher must be nested in typed-actor element!") } } diff --git a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Bar.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/Bar.java similarity index 76% rename from akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Bar.java rename to akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/Bar.java index 1b9e67e09c..7a0f5c439d 100644 --- a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Bar.java +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/Bar.java @@ -1,8 +1,9 @@ package se.scalablesolutions.akka.spring.foo; import java.io.IOException; +import se.scalablesolutions.akka.actor.*; -public class Bar implements IBar { +public class Bar extends TypedActor implements IBar { @Override public String getBar() { diff --git a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Foo.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/Foo.java similarity index 50% rename from akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Foo.java rename to akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/Foo.java index 36536cdb5d..00e4b0df2e 100644 --- a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Foo.java +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/Foo.java @@ -1,6 +1,8 @@ package se.scalablesolutions.akka.spring.foo; -public class Foo { +import se.scalablesolutions.akka.actor.*; + +public class Foo extends TypedActor implements IFoo{ public String foo() { return "foo"; diff --git a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/IBar.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/IBar.java similarity index 100% rename from akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/IBar.java rename to akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/IBar.java diff --git a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/IFoo.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/IFoo.java new file mode 100644 index 0000000000..b7e6b622d5 --- /dev/null +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/IFoo.java @@ -0,0 +1,12 @@ +package se.scalablesolutions.akka.spring.foo; + +/** + * Created by IntelliJ IDEA. + * User: michaelkober + * Date: Aug 11, 2010 + * Time: 12:49:58 PM + * To change this template use File | Settings | File Templates. + */ +public interface IFoo { + public String foo(); +} diff --git a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/IMyPojo.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/IMyPojo.java new file mode 100644 index 0000000000..f2c5e24884 --- /dev/null +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/IMyPojo.java @@ -0,0 +1,21 @@ +package se.scalablesolutions.akka.spring.foo; + +/** + * Created by IntelliJ IDEA. + * User: michaelkober + * Date: Aug 11, 2010 + * Time: 12:01:00 PM + * To change this template use File | Settings | File Templates. + */ +public interface IMyPojo { + public String getFoo(); + + public String getBar(); + + public void preRestart(); + + public void postRestart(); + + public String longRunning(); + +} diff --git a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/MyPojo.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/MyPojo.java similarity index 88% rename from akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/MyPojo.java rename to akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/MyPojo.java index 1269f43f62..0ead5901cc 100644 --- a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/MyPojo.java +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/MyPojo.java @@ -1,6 +1,8 @@ package se.scalablesolutions.akka.spring.foo; -public class MyPojo { +import se.scalablesolutions.akka.actor.*; + +public class MyPojo extends TypedActor implements IMyPojo{ private String foo; private String bar; diff --git a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java similarity index 88% rename from akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java rename to akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java index 17332c696a..3b4e05453b 100644 --- a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java @@ -1,18 +1,19 @@ package se.scalablesolutions.akka.spring.foo; -import se.scalablesolutions.akka.actor.annotation.inittransactionalstate; + import se.scalablesolutions.akka.stm.TransactionalMap; import se.scalablesolutions.akka.stm.TransactionalVector; import se.scalablesolutions.akka.stm.Ref; +import se.scalablesolutions.akka.actor.*; -public class StatefulPojo { +public class StatefulPojo extends TypedActor { private TransactionalMap mapState; private TransactionalVector vectorState; private Ref refState; private boolean isInitialized = false; - @inittransactionalstate - public void init() { + @Override + public void initTransactionalState() { if (!isInitialized) { mapState = new TransactionalMap(); vectorState = new TransactionalVector(); @@ -21,7 +22,7 @@ public class StatefulPojo { } } - + /* public String getMapState(String key) { return (String)mapState.get(key).get(); } @@ -49,5 +50,6 @@ public class StatefulPojo { public boolean isInitialized() { return isInitialized; } + */ } diff --git a/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/dispatcher-config.xml b/akka-spring/src/test/resources/dispatcher-config.xml similarity index 60% rename from akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/dispatcher-config.xml rename to akka-spring/src/test/resources/dispatcher-config.xml index 20879832d0..67d48b0183 100644 --- a/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/dispatcher-config.xml +++ b/akka-spring/src/test/resources/dispatcher-config.xml @@ -10,19 +10,23 @@ http://www.akkasource.org/schema/akka http://scalablesolutions.se/akka/akka-0.10.xsd"> - + - + - + - + - + - + - + @@ -62,6 +66,21 @@ http://scalablesolutions.se/akka/akka-0.10.xsd"> + + + + + + + + @@ -70,11 +89,18 @@ http://scalablesolutions.se/akka/akka-0.10.xsd"> - + - - + implementation="se.scalablesolutions.akka.spring.foo.Bar" + lifecycle="permanent" + timeout="1000"/> + diff --git a/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/supervisor-config.xml b/akka-spring/src/test/resources/supervisor-config.xml similarity index 73% rename from akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/supervisor-config.xml rename to akka-spring/src/test/resources/supervisor-config.xml index d96fdb1c93..ba57a68685 100644 --- a/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/supervisor-config.xml +++ b/akka-spring/src/test/resources/supervisor-config.xml @@ -17,15 +17,23 @@ http://scalablesolutions.se/akka/akka-0.10.xsd"> - + - - + implementation="se.scalablesolutions.akka.spring.foo.Bar" + lifecycle="permanent" + timeout="1000"/> + + - + - - + - + \ No newline at end of file diff --git a/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/test-config.xml b/akka-spring/src/test/resources/typed-actor-config.xml similarity index 67% rename from akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/test-config.xml rename to akka-spring/src/test/resources/typed-actor-config.xml index 23d2476995..dfa40f99ad 100644 --- a/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/test-config.xml +++ b/akka-spring/src/test/resources/typed-actor-config.xml @@ -9,42 +9,41 @@ http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.akkasource.org/schema/akka http://scalablesolutions.se/akka/akka-0.10.xsd"> - - - - - - - - + @@ -65,11 +64,18 @@ http://scalablesolutions.se/akka/akka-0.10.xsd"> - + - - + implementation="se.scalablesolutions.akka.spring.foo.Bar" + lifecycle="permanent" + timeout="1000"/> + diff --git a/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala b/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala index 64e9ea2425..44117f34c8 100644 --- a/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala +++ b/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala @@ -20,13 +20,21 @@ class DispatcherBeanDefinitionParserTest extends Spec with ShouldMatchers { val parser = new DispatcherBeanDefinitionParser() it("should be able to parse the dispatcher configuration") { + // executor-based-event-driven val xml = - val props = parser.parseDispatcher(dom(xml).getDocumentElement); + var props = parser.parseDispatcher(dom(xml).getDocumentElement); assert(props != null) - assert(props.dispatcherType == "executor-based-event-driven") - assert(props.name == "myDispatcher") + assert(props.dispatcherType === "executor-based-event-driven") + assert(props.name === "myDispatcher") + + // executor-based-event-driven-work-stealing + val xml2 = + props = parser.parseDispatcher(dom(xml2).getDocumentElement); + assert(props.dispatcherType === "executor-based-event-driven-work-stealing") } it("should be able to parse the thread pool configuration") { diff --git a/akka-spring/src/test/scala/DispatcherSpringFeatureTest.scala b/akka-spring/src/test/scala/DispatcherSpringFeatureTest.scala new file mode 100644 index 0000000000..a7ed32e0c1 --- /dev/null +++ b/akka-spring/src/test/scala/DispatcherSpringFeatureTest.scala @@ -0,0 +1,134 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ +package se.scalablesolutions.akka.spring + + +import foo.{IMyPojo, MyPojo} +import se.scalablesolutions.akka.dispatch._ + +import org.scalatest.FeatureSpec +import org.scalatest.matchers.ShouldMatchers +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith +import org.springframework.beans.factory.support.DefaultListableBeanFactory +import org.springframework.beans.factory.xml.XmlBeanDefinitionReader +import org.springframework.context.ApplicationContext +import org.springframework.context.support.ClassPathXmlApplicationContext +import org.springframework.core.io.{ClassPathResource, Resource} +import java.util.concurrent._ + +/** + * Tests for spring configuration of typed actors. + * @author michaelkober + */ +@RunWith(classOf[JUnitRunner]) +class DispatcherSpringFeatureTest extends FeatureSpec with ShouldMatchers { + val EVENT_DRIVEN_PREFIX = "akka:event-driven:dispatcher:" + + feature("Spring configuration") { + + scenario("get a executor-event-driven-dispatcher with array-blocking-queue from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val dispatcher = context.getBean("executor-event-driven-dispatcher-1").asInstanceOf[ExecutorBasedEventDrivenDispatcher] + assert(dispatcher.name === EVENT_DRIVEN_PREFIX + "dispatcher-1") + val executor = getThreadPoolExecutorAndAssert(dispatcher) + assert(executor.getCorePoolSize() === 1) + assert(executor.getMaximumPoolSize() === 20) + assert(executor.getKeepAliveTime(TimeUnit.MILLISECONDS) === 3000) + assert(executor.getQueue().isInstanceOf[ArrayBlockingQueue[Runnable]]); + assert(executor.getQueue().remainingCapacity() === 100) + } + + scenario("get a dispatcher via ref from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val pojo = context.getBean("typed-actor-with-dispatcher-ref").asInstanceOf[IMyPojo] + assert(pojo != null) + } + + scenario("get a executor-event-driven-dispatcher with bounded-linked-blocking-queue with unbounded capacity from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val dispatcher = context.getBean("executor-event-driven-dispatcher-2").asInstanceOf[ExecutorBasedEventDrivenDispatcher] + val executor = getThreadPoolExecutorAndAssert(dispatcher) + assert(executor.getQueue().isInstanceOf[LinkedBlockingQueue[Runnable]]) + assert(executor.getQueue().remainingCapacity() === Integer.MAX_VALUE) + assert(dispatcher.name === EVENT_DRIVEN_PREFIX + "dispatcher-2") + } + + scenario("get a executor-event-driven-dispatcher with unbounded-linked-blocking-queue with bounded capacity from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val dispatcher = context.getBean("executor-event-driven-dispatcher-4").asInstanceOf[ExecutorBasedEventDrivenDispatcher] + assert(dispatcher.name === EVENT_DRIVEN_PREFIX + "dispatcher-4") + val executor = getThreadPoolExecutorAndAssert(dispatcher) + assert(executor.getQueue().isInstanceOf[LinkedBlockingQueue[Runnable]]) + assert(executor.getQueue().remainingCapacity() === 55) + } + + scenario("get a executor-event-driven-dispatcher with unbounded-linked-blocking-queue with unbounded capacity from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val dispatcher = context.getBean("executor-event-driven-dispatcher-5").asInstanceOf[ExecutorBasedEventDrivenDispatcher] + assert(dispatcher.name === EVENT_DRIVEN_PREFIX + "dispatcher-5") + val executor = getThreadPoolExecutorAndAssert(dispatcher) + assert(executor.getQueue().isInstanceOf[LinkedBlockingQueue[Runnable]]) + assert(executor.getQueue().remainingCapacity() === Integer.MAX_VALUE) + } + + scenario("get a executor-event-driven-dispatcher with synchronous-queue from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val dispatcher = context.getBean("executor-event-driven-dispatcher-6").asInstanceOf[ExecutorBasedEventDrivenDispatcher] + assert(dispatcher.name === EVENT_DRIVEN_PREFIX + "dispatcher-6") + val executor = getThreadPoolExecutorAndAssert(dispatcher) + assert(executor.getQueue().isInstanceOf[SynchronousQueue[Runnable]]) + } + + scenario("get a reactor-based-thread-pool-event-driven-dispatcher with synchronous-queue from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val dispatcher = context.getBean("reactor-based-thread-pool-event-driven-dispatcher").asInstanceOf[ReactorBasedThreadPoolEventDrivenDispatcher] + val executor = getThreadPoolExecutorAndAssert(dispatcher) + assert(executor.getQueue().isInstanceOf[SynchronousQueue[Runnable]]) + } + + scenario("get a reactor-based-single-thread-event-driven-dispatcher with synchronous-queue from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val dispatcher = context.getBean("reactor-based-single-thread-event-driven-dispatcher").asInstanceOf[ReactorBasedSingleThreadEventDrivenDispatcher] + assert(dispatcher != null) + } + + scenario("get a executor-based-event-driven-work-stealing-dispatcher from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val dispatcher = context.getBean("executor-based-event-driven-work-stealing-dispatcher").asInstanceOf[ExecutorBasedEventDrivenWorkStealingDispatcher] + assert(dispatcher != null) + assert(dispatcher.name === "akka:event-driven-work-stealing:dispatcher:workStealingDispatcher") + val executor = getThreadPoolExecutorAndAssert(dispatcher) + assert(executor.getQueue().isInstanceOf[BlockingQueue[Runnable]]) + } + + scenario("get a hawt-dispatcher from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val dispatcher = context.getBean("hawt-dispatcher").asInstanceOf[HawtDispatcher] + assert(dispatcher != null) + assert(dispatcher.toString === "HawtDispatchEventDrivenDispatcher") + } + + scenario("get a thread-based-dispatcher from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val pojo = context.getBean("typed-actor-with-thread-based-dispatcher").asInstanceOf[IMyPojo] + assert(pojo != null) + } + + } + + /** + * get ThreadPoolExecutor via reflection and assert that dispatcher is correct type + */ + private def getThreadPoolExecutorAndAssert(dispatcher: MessageDispatcher): ThreadPoolExecutor = { + assert(dispatcher.isInstanceOf[ThreadPoolBuilder]) + val pool = dispatcher.asInstanceOf[ThreadPoolBuilder] + val field = pool.getClass.getDeclaredField("se$scalablesolutions$akka$dispatch$ThreadPoolBuilder$$threadPoolBuilder") + field.setAccessible(true) + val executor = field.get(pool).asInstanceOf[ThreadPoolExecutor] + assert(executor != null) + executor; + } + +} \ No newline at end of file diff --git a/akka-spring/src/test/scala/SupervisorSpringFeatureTest.scala b/akka-spring/src/test/scala/SupervisorSpringFeatureTest.scala new file mode 100644 index 0000000000..d51e9b1797 --- /dev/null +++ b/akka-spring/src/test/scala/SupervisorSpringFeatureTest.scala @@ -0,0 +1,105 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ +package se.scalablesolutions.akka.spring + + +import se.scalablesolutions.akka.spring.foo.{IMyPojo, MyPojo, IFoo, IBar} +import se.scalablesolutions.akka.dispatch._ +import se.scalablesolutions.akka.config.TypedActorConfigurator + +import org.scalatest.FeatureSpec +import org.scalatest.matchers.ShouldMatchers +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith +import org.springframework.beans.factory.support.DefaultListableBeanFactory +import org.springframework.beans.factory.xml.XmlBeanDefinitionReader +import org.springframework.context.ApplicationContext +import org.springframework.context.support.ClassPathXmlApplicationContext +import org.springframework.core.io.{ClassPathResource, Resource} +import java.util.concurrent._ + +/** + * Tests for spring configuration of supervisor hierarchies. + * @author michaelkober + */ +@RunWith(classOf[JUnitRunner]) +class SupervisorSpringFeatureTest extends FeatureSpec with ShouldMatchers { + + feature("Spring configuration") { + + scenario("get a supervisor from context") { + val context = new ClassPathXmlApplicationContext("/supervisor-config.xml") + val myConfigurator = context.getBean("supervision1").asInstanceOf[TypedActorConfigurator] + // get TypedActors + val foo = myConfigurator.getInstance(classOf[IFoo]) + assert(foo != null) + val bar = myConfigurator.getInstance(classOf[IBar]) + assert(bar != null) + val pojo = myConfigurator.getInstance(classOf[IMyPojo]) + assert(pojo != null) + } + + scenario("get a supervisor and dispatcher from context") { + val context = new ClassPathXmlApplicationContext("/supervisor-config.xml") + val myConfigurator = context.getBean("supervision-with-dispatcher").asInstanceOf[TypedActorConfigurator] + val foo = myConfigurator.getInstance(classOf[IFoo]) + assert(foo != null) + } + } + + /* +@Test + public void testTransactionalState() { + TypedActorConfigurator conf = (TypedActorConfigurator) context + .getBean("supervision2"); + StatefulPojo stateful = conf.getInstance(StatefulPojo.class); + stateful.setMapState("testTransactionalState", "some map state"); + stateful.setVectorState("some vector state"); + stateful.setRefState("some ref state"); + assertEquals("some map state", stateful + .getMapState("testTransactionalState")); + assertEquals("some vector state", stateful.getVectorState()); + assertEquals("some ref state", stateful.getRefState()); + } + + @Test + public void testInitTransactionalState() { + StatefulPojo stateful = TypedActor.newInstance(StatefulPojo.class, + 1000, true); + assertTrue("should be inititalized", stateful.isInitialized()); + } + + + @Test + public void testSupervisedRemoteTypedActor() { + new Thread(new Runnable() { + public void run() { + RemoteNode.start(); + } + }).start(); + try { + Thread.currentThread().sleep(1000); + } catch (Exception e) { + } + + TypedActorConfigurator conf = new TypedActorConfigurator(); + conf.configure( + new RestartStrategy(new AllForOne(), 3, 10000, new Class[] { Exception.class }), + new Component[] { + new Component( + Foo.class, + new LifeCycle(new Permanent()), + 10000, + new RemoteAddress("localhost", 9999)) + }).supervise(); + + Foo instance = conf.getInstance(Foo.class); + assertEquals("foo", instance.foo()); + } + + + */ + + +} \ No newline at end of file diff --git a/akka-spring/src/test/scala/TypedActorSpringFeatureTest.scala b/akka-spring/src/test/scala/TypedActorSpringFeatureTest.scala new file mode 100644 index 0000000000..8767b2e75a --- /dev/null +++ b/akka-spring/src/test/scala/TypedActorSpringFeatureTest.scala @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ +package se.scalablesolutions.akka.spring + + +import foo.{IMyPojo, MyPojo} +import se.scalablesolutions.akka.dispatch.FutureTimeoutException +import se.scalablesolutions.akka.remote.RemoteNode +import org.scalatest.FeatureSpec +import org.scalatest.matchers.ShouldMatchers +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith +import org.springframework.beans.factory.support.DefaultListableBeanFactory +import org.springframework.beans.factory.xml.XmlBeanDefinitionReader +import org.springframework.context.ApplicationContext +import org.springframework.context.support.ClassPathXmlApplicationContext +import org.springframework.core.io.{ClassPathResource, Resource} + +/** + * Tests for spring configuration of typed actors. + * @author michaelkober + */ +@RunWith(classOf[JUnitRunner]) +class TypedActorSpringFeatureTest extends FeatureSpec with ShouldMatchers { + feature("parse Spring application context") { + + scenario("akka:typed-actor and akka:supervision and akka:dispatcher can be used as top level elements") { + val context = new ClassPathResource("/typed-actor-config.xml") + val beanFactory = new DefaultListableBeanFactory() + val reader = new XmlBeanDefinitionReader(beanFactory) + reader.loadBeanDefinitions(context) + assert(beanFactory.containsBeanDefinition("simple-typed-actor")) + assert(beanFactory.containsBeanDefinition("remote-typed-actor")) + assert(beanFactory.containsBeanDefinition("supervision1")) + assert(beanFactory.containsBeanDefinition("dispatcher1")) + } + + scenario("get a typed actor") { + val context = new ClassPathXmlApplicationContext("/typed-actor-config.xml") + val myPojo = context.getBean("simple-typed-actor").asInstanceOf[IMyPojo] + var msg = myPojo.getFoo() + msg += myPojo.getBar() + assert(msg === "foobar") + } + + scenario("FutureTimeoutException when timed out") { + val context = new ClassPathXmlApplicationContext("/typed-actor-config.xml") + val myPojo = context.getBean("simple-typed-actor").asInstanceOf[IMyPojo] + evaluating {myPojo.longRunning()} should produce[FutureTimeoutException] + + } + + scenario("typed-actor with timeout") { + val context = new ClassPathXmlApplicationContext("/typed-actor-config.xml") + val myPojo = context.getBean("simple-typed-actor-long-timeout").asInstanceOf[IMyPojo] + assert(myPojo.longRunning() === "this took long"); + } + + scenario("transactional typed-actor") { + val context = new ClassPathXmlApplicationContext("/typed-actor-config.xml") + val myPojo = context.getBean("transactional-typed-actor").asInstanceOf[IMyPojo] + var msg = myPojo.getFoo() + msg += myPojo.getBar() + assert(msg === "foobar") + } + + scenario("get a remote typed-actor") { + RemoteNode.start + Thread.sleep(1000) + val context = new ClassPathXmlApplicationContext("/typed-actor-config.xml") + val myPojo = context.getBean("remote-typed-actor").asInstanceOf[IMyPojo] + assert(myPojo.getFoo === "foo") + } + } + +} + From ef79befe9a1da98373c08b36d3790e69bd545fe6 Mon Sep 17 00:00:00 2001 From: Michael Kober Date: Thu, 12 Aug 2010 09:02:49 +0200 Subject: [PATCH 14/24] added config for WorkStealingDispatcher and HawtDispatcher; Tickets 200 and 377 --- .../se/scalablesolutions/akka/spring/akka-0.10.xsd | 1 + .../src/main/scala/AkkaSpringConfigurationTags.scala | 1 + .../src/main/scala/DispatcherFactoryBean.scala | 2 +- akka-spring/src/main/scala/DispatcherParser.scala | 12 +++++++++++- .../src/main/scala/DispatcherProperties.scala | 1 + akka-spring/src/test/resources/dispatcher-config.xml | 4 ++-- .../scala/DispatcherBeanDefinitionParserTest.scala | 11 +++++++++++ .../src/test/scala/DispatcherSpringFeatureTest.scala | 1 + 8 files changed, 29 insertions(+), 4 deletions(-) diff --git a/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd index 76cd477a10..fc83bea3e3 100644 --- a/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd +++ b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd @@ -76,6 +76,7 @@ + diff --git a/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala b/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala index 6b598a4a31..e432edaf23 100644 --- a/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala +++ b/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala @@ -55,6 +55,7 @@ object AkkaSpringConfigurationTags { val NAME = "name" val REF = "ref" val TYPE = "type" + val AGGREGATE = "aggregate" // HawtDispatcher // thread pool attributes val QUEUE = "queue" diff --git a/akka-spring/src/main/scala/DispatcherFactoryBean.scala b/akka-spring/src/main/scala/DispatcherFactoryBean.scala index 5eafdda4f3..273d8d38b8 100644 --- a/akka-spring/src/main/scala/DispatcherFactoryBean.scala +++ b/akka-spring/src/main/scala/DispatcherFactoryBean.scala @@ -22,7 +22,7 @@ object DispatcherFactoryBean { case REACTOR_BASED_THREAD_POOL_EVENT_DRIVEN => Dispatchers.newReactorBasedThreadPoolEventDrivenDispatcher(properties.name) case REACTOR_BASED_SINGLE_THREAD_EVENT_DRIVEN => Dispatchers.newReactorBasedSingleThreadEventDrivenDispatcher(properties.name) case THREAD_BASED => throw new IllegalArgumentException("not implemented yet") //FIXME - case HAWT => throw new IllegalArgumentException("not implemented yet") //FIXME + case HAWT => Dispatchers.newHawtDispatcher(properties.aggregate) case _ => throw new IllegalArgumentException("unknown dispatcher type") } if ((properties.threadPool != null) && (properties.threadPool.queue != null)) { diff --git a/akka-spring/src/main/scala/DispatcherParser.scala b/akka-spring/src/main/scala/DispatcherParser.scala index c3f802018e..a8ffdd4886 100644 --- a/akka-spring/src/main/scala/DispatcherParser.scala +++ b/akka-spring/src/main/scala/DispatcherParser.scala @@ -28,7 +28,7 @@ trait DispatcherParser extends BeanParser { throw new IllegalArgumentException("Referenced dispatcher not found: '" + ref + "'") } } - properties.name = mandatory(dispatcherElement, NAME) + properties.dispatcherType = mandatory(dispatcherElement, TYPE) if (properties.dispatcherType == THREAD_BASED) { if ((dispatcherElement.getParentNode.getNodeName != "akka:typed-actor") && @@ -36,6 +36,16 @@ trait DispatcherParser extends BeanParser { throw new IllegalArgumentException("Thread based dispatcher must be nested in typed-actor element!") } } + + if (properties.dispatcherType == HAWT) { // no name for HawtDispatcher + properties.name = dispatcherElement.getAttribute(NAME) + if (dispatcherElement.hasAttribute(AGGREGATE)) { + properties.aggregate = dispatcherElement.getAttribute(AGGREGATE).toBoolean + } + } else { + properties.name = mandatory(dispatcherElement, NAME) + } + val threadPoolElement = DomUtils.getChildElementByTagName(dispatcherElement, THREAD_POOL_TAG); if (threadPoolElement != null) { if (properties.dispatcherType == REACTOR_BASED_SINGLE_THREAD_EVENT_DRIVEN || diff --git a/akka-spring/src/main/scala/DispatcherProperties.scala b/akka-spring/src/main/scala/DispatcherProperties.scala index e35bb62d27..183b3825bb 100644 --- a/akka-spring/src/main/scala/DispatcherProperties.scala +++ b/akka-spring/src/main/scala/DispatcherProperties.scala @@ -14,6 +14,7 @@ class DispatcherProperties { var dispatcherType: String = "" var name: String = "" var threadPool: ThreadPoolProperties = _ + var aggregate = true /** * Sets the properties to the given builder. diff --git a/akka-spring/src/test/resources/dispatcher-config.xml b/akka-spring/src/test/resources/dispatcher-config.xml index 67d48b0183..2d295f7614 100644 --- a/akka-spring/src/test/resources/dispatcher-config.xml +++ b/akka-spring/src/test/resources/dispatcher-config.xml @@ -69,8 +69,8 @@ http://scalablesolutions.se/akka/akka-0.10.xsd"> - + + + + + + + + + + + + + + Name of the implementation class. + + + + + + + The default timeout for '!!' invocations. + + + + + + + Set this to true if messages should have REQUIRES_NEW semantics. + + + + + + + Defines the lifecycle, can be either 'permanent' or 'temporary'. + + + + + + + Supported scopes are 'singleton' and 'prototype'. + + + + + @@ -175,6 +221,13 @@ + + + + + + + @@ -208,6 +261,7 @@ + @@ -227,6 +281,9 @@ + + + diff --git a/akka-spring/src/main/scala/TypedActorFactoryBean.scala b/akka-spring/src/main/scala/ActorFactoryBean.scala similarity index 77% rename from akka-spring/src/main/scala/TypedActorFactoryBean.scala rename to akka-spring/src/main/scala/ActorFactoryBean.scala index 0cb70e5ae3..f2abe62879 100644 --- a/akka-spring/src/main/scala/TypedActorFactoryBean.scala +++ b/akka-spring/src/main/scala/ActorFactoryBean.scala @@ -20,7 +20,7 @@ import org.springframework.context.{ApplicationContext,ApplicationContextAware} import org.springframework.util.ReflectionUtils import org.springframework.util.StringUtils -import se.scalablesolutions.akka.actor.{AspectInitRegistry, TypedActorConfiguration, TypedActor} +import se.scalablesolutions.akka.actor.{AspectInitRegistry, TypedActorConfiguration, TypedActor, UntypedActor, UntypedActorRef} import se.scalablesolutions.akka.dispatch.MessageDispatcher import se.scalablesolutions.akka.util.{Logging, Duration} @@ -34,17 +34,18 @@ class AkkaBeansException(message: String, cause:Throwable) extends BeansExceptio } /** - * Factory bean for typed actors. + * Factory bean for typed and untyped actors. * * @author michaelkober * @author Johan Rask * @author Martin Krasser * @author Jonas Bonér */ -class TypedActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging with ApplicationContextAware { +class ActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging with ApplicationContextAware { import StringReflect._ import AkkaSpringConfigurationTags._ + @BeanProperty var typed: String = "" @BeanProperty var interface: String = "" @BeanProperty var implementation: String = "" @BeanProperty var timeout: Long = _ @@ -82,15 +83,57 @@ class TypedActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging wit if (isRemote) argumentList += "r" if (hasInterface) argumentList += "i" if (hasDispatcher) argumentList += "d" - val ref = create(argumentList) - setProperties(AspectInitRegistry.initFor(ref).targetInstance) + val ref = typed match { + case TYPED_ACTOR_TAG => val typedActor = createTypedInstance(argumentList) + setProperties(AspectInitRegistry.initFor(typedActor).targetInstance) + typedActor + case UNTYPED_ACTOR_TAG => createUntypedInstance(argumentList) + } ref } + private[akka] def createTypedInstance(argList: String) : AnyRef = { + if (interface == null || interface == "") throw new AkkaBeansException( + "The 'interface' part of the 'akka:actor' element in the Spring config file can't be null or empty string") + if (implementation == null || implementation == "") throw new AkkaBeansException( + "The 'implementation' part of the 'akka:typed-actor' element in the Spring config file can't be null or empty string") + argList match { + case "ri" => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig.makeRemote(host, port)) + case "i" => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig) + case "id" => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig.dispatcher(dispatcherInstance)) + case "rid" => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig.makeRemote(host, port).dispatcher(dispatcherInstance)) + case _ => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig) + } + } + + private[akka] def createUntypedInstance(args: String) : UntypedActorRef = { + if (implementation == null || implementation == "") throw new AkkaBeansException( + "The 'implementation' part of the 'akka:untyped-actor' element in the Spring config file can't be null or empty string") + val actorRef = UntypedActor.actorOf(implementation.toClass) + if (timeout > 0) { + actorRef.setTimeout(timeout) + } + if (transactional) { + actorRef.makeTransactionRequired + } + if (isRemote) { + actorRef.makeRemote(host, port) + } + if (hasDispatcher) { + actorRef.setDispatcher(dispatcherInstance) + } + actorRef + } + /** * Stop the typed actor if it is a singleton. */ - override def destroyInstance(instance: AnyRef) = TypedActor.stop(instance) + override def destroyInstance(instance: AnyRef) { + typed match { + case TYPED_ACTOR_TAG => TypedActor.stop(instance) + case UNTYPED_ACTOR_TAG => instance.asInstanceOf[UntypedActorRef].stop + } + } private def setProperties(ref: AnyRef): AnyRef = { if (hasSetDependecies) return ref @@ -114,22 +157,6 @@ class TypedActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging wit ref } - private[akka] def create(argList: String): AnyRef = { - if (interface == null || interface == "") throw new AkkaBeansException( - "The 'interface' part of the 'akka:actor' element in the Spring config file can't be null or empty string") - if (implementation == null || implementation == "") throw new AkkaBeansException( - "The 'implementation' part of the 'akka:typed-actor' element in the Spring config file can't be null or empty string") - argList match { - case "ri" => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig.makeRemote(host, port)) - case "i" => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig) - case "id" => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig.dispatcher(dispatcherInstance)) - case "rid" => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig.makeRemote(host, port).dispatcher(dispatcherInstance)) - case _ => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig) - // case "rd" => TypedActor.newInstance(implementation.toClass, createConfig.makeRemote(host, port).dispatcher(dispatcherInstance)) - // case "r" => TypedActor.newInstance(implementation.toClass, createConfig.makeRemote(host, port)) - // case "d" => TypedActor.newInstance(implementation.toClass, createConfig.dispatcher(dispatcherInstance)) - } - } private[akka] def createConfig: TypedActorConfiguration = { val config = new TypedActorConfiguration().timeout(Duration(timeout, "millis")) @@ -148,6 +175,12 @@ class TypedActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging wit private[akka] def dispatcherInstance: MessageDispatcher = { import DispatcherFactoryBean._ - createNewInstance(dispatcher) + if (dispatcher.dispatcherType != THREAD_BASED) { + createNewInstance(dispatcher) + } else { + println("### create thread based dispatcher") + createNewInstance(dispatcher) + } + } } diff --git a/akka-spring/src/main/scala/TypedActorParser.scala b/akka-spring/src/main/scala/ActorParser.scala similarity index 92% rename from akka-spring/src/main/scala/TypedActorParser.scala rename to akka-spring/src/main/scala/ActorParser.scala index 5f4d68f297..9858c1fad4 100644 --- a/akka-spring/src/main/scala/TypedActorParser.scala +++ b/akka-spring/src/main/scala/ActorParser.scala @@ -15,7 +15,7 @@ import se.scalablesolutions.akka.actor.IllegalActorStateException * @author Johan Rask * @author Martin Krasser */ -trait TypedActorParser extends BeanParser with DispatcherParser { +trait ActorParser extends BeanParser with DispatcherParser { import AkkaSpringConfigurationTags._ /** @@ -23,8 +23,8 @@ trait TypedActorParser extends BeanParser with DispatcherParser { * @param element dom element to parse * @return configuration for the typed actor */ - def parseTypedActor(element: Element): TypedActorProperties = { - val objectProperties = new TypedActorProperties() + def parseActor(element: Element): ActorProperties = { + val objectProperties = new ActorProperties() val remoteElement = DomUtils.getChildElementByTagName(element, REMOTE_TAG); val dispatcherElement = DomUtils.getChildElementByTagName(element, DISPATCHER_TAG) val propertyEntries = DomUtils.getChildElementsByTagName(element,PROPERTYENTRY_TAG) diff --git a/akka-spring/src/main/scala/TypedActorProperties.scala b/akka-spring/src/main/scala/ActorProperties.scala similarity index 93% rename from akka-spring/src/main/scala/TypedActorProperties.scala rename to akka-spring/src/main/scala/ActorProperties.scala index 46c9cd35aa..15c7e61fe0 100644 --- a/akka-spring/src/main/scala/TypedActorProperties.scala +++ b/akka-spring/src/main/scala/ActorProperties.scala @@ -12,7 +12,8 @@ import AkkaSpringConfigurationTags._ * @author michaelkober * @author Martin Krasser */ -class TypedActorProperties { +class ActorProperties { + var typed: String = "" var target: String = "" var timeout: Long = _ var interface: String = "" @@ -30,6 +31,7 @@ class TypedActorProperties { * @param builder bean definition builder */ def setAsProperties(builder: BeanDefinitionBuilder) { + builder.addPropertyValue("typed", typed) builder.addPropertyValue(HOST, host) builder.addPropertyValue(PORT, port) builder.addPropertyValue(TIMEOUT, timeout) diff --git a/akka-spring/src/main/scala/AkkaNamespaceHandler.scala b/akka-spring/src/main/scala/AkkaNamespaceHandler.scala index 694daa90d4..a478b7b262 100644 --- a/akka-spring/src/main/scala/AkkaNamespaceHandler.scala +++ b/akka-spring/src/main/scala/AkkaNamespaceHandler.scala @@ -13,6 +13,7 @@ import AkkaSpringConfigurationTags._ class AkkaNamespaceHandler extends NamespaceHandlerSupport { def init = { registerBeanDefinitionParser(TYPED_ACTOR_TAG, new TypedActorBeanDefinitionParser()); + registerBeanDefinitionParser(UNTYPED_ACTOR_TAG, new UntypedActorBeanDefinitionParser()); registerBeanDefinitionParser(SUPERVISION_TAG, new SupervisionBeanDefinitionParser()); registerBeanDefinitionParser(DISPATCHER_TAG, new DispatcherBeanDefinitionParser()); registerBeanDefinitionParser(CAMEL_SERVICE_TAG, new CamelServiceBeanDefinitionParser); diff --git a/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala b/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala index e432edaf23..518727bd4c 100644 --- a/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala +++ b/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala @@ -14,16 +14,18 @@ object AkkaSpringConfigurationTags { // // top level tags val TYPED_ACTOR_TAG = "typed-actor" + val UNTYPED_ACTOR_TAG = "untyped-actor" val SUPERVISION_TAG = "supervision" val DISPATCHER_TAG = "dispatcher" val PROPERTYENTRY_TAG = "property" val CAMEL_SERVICE_TAG = "camel-service" - // typed-actor sub tags + // actor sub tags val REMOTE_TAG = "remote" // superivision sub tags val TYPED_ACTORS_TAG = "typed-actors" + val UNTYPED_ACTORS_TAG = "untyped-actors" val STRATEGY_TAG = "restart-strategy" val TRAP_EXISTS_TAG = "trap-exits" val TRAP_EXIT_TAG = "trap-exit" @@ -36,7 +38,7 @@ object AkkaSpringConfigurationTags { // --- ATTRIBUTES // - // typed actor attributes + // actor attributes val TIMEOUT = "timeout" val IMPLEMENTATION = "implementation" val INTERFACE = "interface" diff --git a/akka-spring/src/main/scala/DispatcherBeanDefinitionParser.scala b/akka-spring/src/main/scala/DispatcherBeanDefinitionParser.scala index 9d4a16ff9b..90c56b0b5b 100644 --- a/akka-spring/src/main/scala/DispatcherBeanDefinitionParser.scala +++ b/akka-spring/src/main/scala/DispatcherBeanDefinitionParser.scala @@ -12,7 +12,7 @@ import org.springframework.beans.factory.xml.{ParserContext, AbstractSingleBeanD * Parser for custom namespace configuration. * @author michaelkober */ -class DispatcherBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with TypedActorParser with DispatcherParser { +class DispatcherBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with ActorParser with DispatcherParser { /* * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder) */ diff --git a/akka-spring/src/main/scala/PropertyEntries.scala b/akka-spring/src/main/scala/PropertyEntries.scala index aa2843064c..bf1898a805 100644 --- a/akka-spring/src/main/scala/PropertyEntries.scala +++ b/akka-spring/src/main/scala/PropertyEntries.scala @@ -1,3 +1,6 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ package se.scalablesolutions.akka.spring import org.springframework.beans.factory.support.BeanDefinitionBuilder @@ -5,14 +8,13 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder import scala.collection.mutable._ /** -* Simple container for Properties -* @author Johan Rask -*/ + * Simple container for Properties + * @author Johan Rask + */ class PropertyEntries { + var entryList: ListBuffer[PropertyEntry] = ListBuffer[PropertyEntry]() - var entryList:ListBuffer[PropertyEntry] = ListBuffer[PropertyEntry]() - - def add(entry:PropertyEntry) = { - entryList.append(entry) + def add(entry: PropertyEntry) = { + entryList.append(entry) } } diff --git a/akka-spring/src/main/scala/PropertyEntry.scala b/akka-spring/src/main/scala/PropertyEntry.scala index 4d1aaa1a44..9fe6357fc0 100644 --- a/akka-spring/src/main/scala/PropertyEntry.scala +++ b/akka-spring/src/main/scala/PropertyEntry.scala @@ -1,17 +1,19 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ package se.scalablesolutions.akka.spring /** -* Represents a property element -* @author Johan Rask -*/ + * Represents a property element + * @author Johan Rask + */ class PropertyEntry { - - var name:String = _ - var value:String = null - var ref:String = null + var name: String = _ + var value: String = null + var ref: String = null - override def toString(): String = { - format("name = %s,value = %s, ref = %s", name,value,ref) - } + override def toString(): String = { + format("name = %s,value = %s, ref = %s", name, value, ref) + } } diff --git a/akka-spring/src/main/scala/SupervisionBeanDefinitionParser.scala b/akka-spring/src/main/scala/SupervisionBeanDefinitionParser.scala index 5d430c9450..cc88e39f91 100644 --- a/akka-spring/src/main/scala/SupervisionBeanDefinitionParser.scala +++ b/akka-spring/src/main/scala/SupervisionBeanDefinitionParser.scala @@ -18,7 +18,7 @@ import org.springframework.util.xml.DomUtils * Parser for custom namespace for Akka declarative supervisor configuration. * @author michaelkober */ -class SupervisionBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with TypedActorParser { +class SupervisionBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with ActorParser { /* (non-Javadoc) * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder) */ @@ -30,10 +30,20 @@ class SupervisionBeanDefinitionParser extends AbstractSingleBeanDefinitionParser * made accessible for testing */ private[akka] def parseSupervisor(element: Element, builder: BeanDefinitionBuilder) { - val strategyElement = mandatoryElement(element, STRATEGY_TAG); - val typedActorsElement = mandatoryElement(element, TYPED_ACTORS_TAG); + val strategyElement = mandatoryElement(element, STRATEGY_TAG) + val typedActorsElement = DomUtils.getChildElementByTagName(element, TYPED_ACTORS_TAG) + val untypedActorsElement = DomUtils.getChildElementByTagName(element, UNTYPED_ACTORS_TAG) + if ((typedActorsElement == null) && (untypedActorsElement == null)) { + throw new IllegalArgumentException("One of 'akka:typed-actors' or 'akka:untyped-actors' needed.") + } parseRestartStrategy(strategyElement, builder) - parseTypedActorList(typedActorsElement, builder) + if (typedActorsElement != null) { + builder.addPropertyValue("typed", AkkaSpringConfigurationTags.TYPED_ACTOR_TAG) + parseTypedActorList(typedActorsElement, builder) + } else { + builder.addPropertyValue("typed", AkkaSpringConfigurationTags.UNTYPED_ACTOR_TAG) + parseUntypedActorList(untypedActorsElement, builder) + } } private[akka] def parseRestartStrategy(element: Element, builder: BeanDefinitionBuilder) { @@ -48,8 +58,14 @@ class SupervisionBeanDefinitionParser extends AbstractSingleBeanDefinitionParser private[akka] def parseTypedActorList(element: Element, builder: BeanDefinitionBuilder) { val typedActors = DomUtils.getChildElementsByTagName(element, TYPED_ACTOR_TAG).toArray.toList.asInstanceOf[List[Element]] - val typedActorProperties = typedActors.map(parseTypedActor(_)) - builder.addPropertyValue("supervised", typedActorProperties) + val actorProperties = typedActors.map(parseActor(_)) + builder.addPropertyValue("supervised", actorProperties) + } + + private[akka] def parseUntypedActorList(element: Element, builder: BeanDefinitionBuilder) { + val untypedActors = DomUtils.getChildElementsByTagName(element, UNTYPED_ACTOR_TAG).toArray.toList.asInstanceOf[List[Element]] + val actorProperties = untypedActors.map(parseActor(_)) + builder.addPropertyValue("supervised", actorProperties) } private def parseTrapExits(element: Element): Array[Class[_ <: Throwable]] = { diff --git a/akka-spring/src/main/scala/SupervisionFactoryBean.scala b/akka-spring/src/main/scala/SupervisionFactoryBean.scala index 80a1f8a5fa..8ff62ba4af 100644 --- a/akka-spring/src/main/scala/SupervisionFactoryBean.scala +++ b/akka-spring/src/main/scala/SupervisionFactoryBean.scala @@ -6,6 +6,8 @@ package se.scalablesolutions.akka.spring import org.springframework.beans.factory.config.AbstractFactoryBean import se.scalablesolutions.akka.config.TypedActorConfigurator import se.scalablesolutions.akka.config.JavaConfig._ +import se.scalablesolutions.akka.config.ScalaConfig.{Supervise, Server, SupervisorConfig, RemoteAddress => SRemoteAddress} +import se.scalablesolutions.akka.actor.{Supervisor, SupervisorFactory, UntypedActor} import AkkaSpringConfigurationTags._ import reflect.BeanProperty @@ -14,31 +16,45 @@ import reflect.BeanProperty * Factory bean for supervisor configuration. * @author michaelkober */ -class SupervisionFactoryBean extends AbstractFactoryBean[TypedActorConfigurator] { +class SupervisionFactoryBean extends AbstractFactoryBean[AnyRef] { @BeanProperty var restartStrategy: RestartStrategy = _ - @BeanProperty var supervised: List[TypedActorProperties] = _ + @BeanProperty var supervised: List[ActorProperties] = _ + @BeanProperty var typed: String = "" /* * @see org.springframework.beans.factory.FactoryBean#getObjectType() */ - def getObjectType: Class[TypedActorConfigurator] = classOf[TypedActorConfigurator] + def getObjectType: Class[AnyRef] = classOf[AnyRef] /* * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance() */ - def createInstance: TypedActorConfigurator = { - val configurator = new TypedActorConfigurator() + def createInstance: AnyRef = typed match { + case AkkaSpringConfigurationTags.TYPED_ACTOR_TAG => createInstanceForTypedActors + case AkkaSpringConfigurationTags.UNTYPED_ACTOR_TAG => createInstanceForUntypedActors + } + private def createInstanceForTypedActors() : TypedActorConfigurator = { + val configurator = new TypedActorConfigurator() configurator.configure( restartStrategy, supervised.map(createComponent(_)).toArray ).supervise + + } + + private def createInstanceForUntypedActors() : Supervisor = { + val factory = new SupervisorFactory( + new SupervisorConfig( + restartStrategy.transform, + supervised.map(createSupervise(_)))) + factory.newInstance } /** * Create configuration for TypedActor */ - private[akka] def createComponent(props: TypedActorProperties): Component = { + private[akka] def createComponent(props: ActorProperties): Component = { import StringReflect._ val lifeCycle = if (!props.lifecycle.isEmpty && props.lifecycle.equalsIgnoreCase(VAL_LIFECYCYLE_TEMPORARY)) new LifeCycle(new Temporary()) else new LifeCycle(new Permanent()) val isRemote = (props.host != null) && (!props.host.isEmpty) @@ -58,4 +74,28 @@ class SupervisionFactoryBean extends AbstractFactoryBean[TypedActorConfigurator] } } } + + /** + * Create configuration for UntypedActor + */ + private[akka] def createSupervise(props: ActorProperties): Server = { + import StringReflect._ + val lifeCycle = if (!props.lifecycle.isEmpty && props.lifecycle.equalsIgnoreCase(VAL_LIFECYCYLE_TEMPORARY)) new LifeCycle(new Temporary()) else new LifeCycle(new Permanent()) + val isRemote = (props.host != null) && (!props.host.isEmpty) + val untypedActorRef = UntypedActor.actorOf(props.target.toClass) + if (props.timeout > 0) { + untypedActorRef.setTimeout(props.timeout) + } + if (props.transactional) { + untypedActorRef.makeTransactionRequired + } + + val supervise = if (isRemote) { + val remote = new SRemoteAddress(props.host, props.port) + Supervise(untypedActorRef.actorRef, lifeCycle.transform, remote) + } else { + Supervise(untypedActorRef.actorRef, lifeCycle.transform) + } + supervise + } } diff --git a/akka-spring/src/main/scala/TypedActorBeanDefinitionParser.scala b/akka-spring/src/main/scala/TypedActorBeanDefinitionParser.scala index ec987aacc0..e8e0cef7d4 100644 --- a/akka-spring/src/main/scala/TypedActorBeanDefinitionParser.scala +++ b/akka-spring/src/main/scala/TypedActorBeanDefinitionParser.scala @@ -6,6 +6,7 @@ package se.scalablesolutions.akka.spring import org.springframework.beans.factory.support.BeanDefinitionBuilder import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser import org.springframework.beans.factory.xml.ParserContext +import AkkaSpringConfigurationTags._ import org.w3c.dom.Element @@ -13,17 +14,18 @@ import org.w3c.dom.Element * Parser for custom namespace configuration. * @author michaelkober */ -class TypedActorBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with TypedActorParser { +class TypedActorBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with ActorParser { /* * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder) */ override def doParse(element: Element, parserContext: ParserContext, builder: BeanDefinitionBuilder) { - val typedActorConf = parseTypedActor(element) + val typedActorConf = parseActor(element) + typedActorConf.typed = TYPED_ACTOR_TAG typedActorConf.setAsProperties(builder) } /* * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element) */ - override def getBeanClass(element: Element): Class[_] = classOf[TypedActorFactoryBean] + override def getBeanClass(element: Element): Class[_] = classOf[ActorFactoryBean] } diff --git a/akka-spring/src/main/scala/UntypedActorBeanDefinitionParser.scala b/akka-spring/src/main/scala/UntypedActorBeanDefinitionParser.scala new file mode 100644 index 0000000000..752e18559f --- /dev/null +++ b/akka-spring/src/main/scala/UntypedActorBeanDefinitionParser.scala @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ +package se.scalablesolutions.akka.spring + +import org.springframework.beans.factory.support.BeanDefinitionBuilder +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser +import org.springframework.beans.factory.xml.ParserContext +import AkkaSpringConfigurationTags._ +import org.w3c.dom.Element + + +/** + * Parser for custom namespace configuration. + * @author michaelkober + */ +class UntypedActorBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with ActorParser { + /* + * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder) + */ + override def doParse(element: Element, parserContext: ParserContext, builder: BeanDefinitionBuilder) { + val untypedActorConf = parseActor(element) + untypedActorConf.typed = UNTYPED_ACTOR_TAG + untypedActorConf.setAsProperties(builder) + } + + /* + * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element) + */ + override def getBeanClass(element: Element): Class[_] = classOf[ActorFactoryBean] +} diff --git a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/PingActor.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/PingActor.java new file mode 100644 index 0000000000..c624d63ecd --- /dev/null +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/PingActor.java @@ -0,0 +1,33 @@ +package se.scalablesolutions.akka.spring.foo; + +import se.scalablesolutions.akka.actor.UntypedActor; +import se.scalablesolutions.akka.actor.UntypedActorRef; + +/** + * test class + */ +public class PingActor extends UntypedActor { + + private String longRunning() { + try { + Thread.sleep(6000); + } catch (InterruptedException e) { + } + return "this took long"; + } + + public void onReceive(Object message) throws Exception { + if (message instanceof String) { + System.out.println("Ping received String message: " + message); + if (message.equals("longRunning")) { + System.out.println("### starting pong"); + UntypedActorRef pongActor = UntypedActor.actorOf(PongActor.class).start(); + pongActor.sendRequestReply("longRunning", getContext()); + } + } else { + throw new IllegalArgumentException("Unknown message: " + message); + } + } + + +} diff --git a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/PongActor.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/PongActor.java new file mode 100644 index 0000000000..b67c0809fb --- /dev/null +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/PongActor.java @@ -0,0 +1,18 @@ +package se.scalablesolutions.akka.spring.foo; + +import se.scalablesolutions.akka.actor.UntypedActor; + +/** + * test class + */ +public class PongActor extends UntypedActor { + + public void onReceive(Object message) throws Exception { + if (message instanceof String) { + System.out.println("Pongeceived String message: " + message); + getContext().replyUnsafe(message + " from " + getContext().getUuid()); + } else { + throw new IllegalArgumentException("Unknown message: " + message); + } + } +} diff --git a/akka-spring/src/test/resources/dispatcher-config.xml b/akka-spring/src/test/resources/dispatcher-config.xml index 2d295f7614..18c27c778b 100644 --- a/akka-spring/src/test/resources/dispatcher-config.xml +++ b/akka-spring/src/test/resources/dispatcher-config.xml @@ -78,7 +78,7 @@ http://scalablesolutions.se/akka/akka-0.10.xsd"> implementation="se.scalablesolutions.akka.spring.foo.MyPojo" timeout="1000"> - --> + --> diff --git a/akka-spring/src/test/resources/supervisor-config.xml b/akka-spring/src/test/resources/supervisor-config.xml index ba57a68685..698581d903 100644 --- a/akka-spring/src/test/resources/supervisor-config.xml +++ b/akka-spring/src/test/resources/supervisor-config.xml @@ -33,6 +33,23 @@ http://scalablesolutions.se/akka/akka-0.10.xsd"> + + + + java.io.IOException + java.lang.NullPointerException + + + + + + + + + --> + + + + + diff --git a/akka-spring/src/test/scala/ActorFactoryBeanTest.scala b/akka-spring/src/test/scala/ActorFactoryBeanTest.scala index 1b6c7d2e7c..112f34f0c7 100644 --- a/akka-spring/src/test/scala/ActorFactoryBeanTest.scala +++ b/akka-spring/src/test/scala/ActorFactoryBeanTest.scala @@ -17,7 +17,6 @@ import org.scalatest.matchers.ShouldMatchers */ @RunWith(classOf[JUnitRunner]) class ActorFactoryBeanTest extends Spec with ShouldMatchers with BeforeAndAfterAll { - override protected def afterAll = ActorRegistry.shutdownAll describe("A ActorFactoryBean") { @@ -34,11 +33,6 @@ class ActorFactoryBeanTest extends Spec with ShouldMatchers with BeforeAndAfterA assert(bean.isRemote) } - it("should create object that implements the given interface") { - bean.setInterface("com.biz.IPojo"); - assert(bean.hasInterface) - } - it("should create an typed actor with dispatcher if dispatcher is set") { val props = new DispatcherProperties() props.dispatcherType = "executor-based-event-driven" diff --git a/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala b/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala index b4ace4c290..83c179e29a 100644 --- a/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala +++ b/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala @@ -15,15 +15,14 @@ import ScalaDom._ */ @RunWith(classOf[JUnitRunner]) class DispatcherBeanDefinitionParserTest extends Spec with ShouldMatchers { - describe("A DispatcherBeanDefinitionParser") { val parser = new DispatcherBeanDefinitionParser() it("should be able to parse the dispatcher configuration") { // executor-based-event-driven val xml = + type="executor-based-event-driven" + name="myDispatcher"/> var props = parser.parseDispatcher(dom(xml).getDocumentElement); assert(props != null) assert(props.dispatcherType === "executor-based-event-driven") @@ -31,20 +30,20 @@ class DispatcherBeanDefinitionParserTest extends Spec with ShouldMatchers { // executor-based-event-driven-work-stealing val xml2 = + type="executor-based-event-driven-work-stealing" + name="myDispatcher"/> props = parser.parseDispatcher(dom(xml2).getDocumentElement); assert(props.dispatcherType === "executor-based-event-driven-work-stealing") } it("should be able to parse the thread pool configuration") { val xml = + capacity="100" + fairness="true" + max-pool-size="40" + core-pool-size="6" + keep-alive="2000" + rejection-policy="caller-runs-policy"/> val props = parser.parseThreadPool(dom(xml).getDocumentElement); assert(props != null) assert(props.queue == "bounded-array-blocking-queue") @@ -58,14 +57,14 @@ class DispatcherBeanDefinitionParserTest extends Spec with ShouldMatchers { it("should be able to parse the dispatcher with a thread pool configuration") { val xml = - - + type="reactor-based-thread-pool-event-driven" + name="myDispatcher"> + + val props = parser.parseDispatcher(dom(xml).getDocumentElement); assert(props != null) assert(props.dispatcherType == "reactor-based-thread-pool-event-driven") @@ -77,36 +76,36 @@ class DispatcherBeanDefinitionParserTest extends Spec with ShouldMatchers { } it("should throw IllegalArgumentException on not existing reference") { - val xml = - evaluating { parser.parseDispatcher(dom(xml).getDocumentElement) } should produce [IllegalArgumentException] + val xml = + evaluating {parser.parseDispatcher(dom(xml).getDocumentElement)} should produce[IllegalArgumentException] } it("should throw IllegalArgumentException on missing mandatory attributes") { val xml = - evaluating { parser.parseDispatcher(dom(xml).getDocumentElement) } should produce [IllegalArgumentException] + name="myDispatcher"/> + evaluating {parser.parseDispatcher(dom(xml).getDocumentElement)} should produce[IllegalArgumentException] } it("should throw IllegalArgumentException when configuring a single thread dispatcher with a thread pool") { val xml = - - - evaluating { parser.parseDispatcher(dom(xml).getDocumentElement) } should produce [IllegalArgumentException] + type="reactor-based-single-thread-event-driven" + name="myDispatcher"> + + + evaluating {parser.parseDispatcher(dom(xml).getDocumentElement)} should produce[IllegalArgumentException] } - it("should throw IllegalArgumentException when configuring a thread based dispatcher without TypedActor") { - val xml = - evaluating { parser.parseDispatcher(dom(xml).getDocumentElement) } should produce [IllegalArgumentException] + it("should throw IllegalArgumentException when configuring a thread based dispatcher without TypedActor or UntypedActor") { + val xml = + evaluating {parser.parseDispatcher(dom(xml).getDocumentElement)} should produce[IllegalArgumentException] } it("should be able to parse the hawt dispatcher configuration") { // hawt val xml = + type="hawt" + aggregate="false"/> var props = parser.parseDispatcher(dom(xml).getDocumentElement); assert(props != null) assert(props.dispatcherType === "hawt") diff --git a/akka-spring/src/test/scala/DispatcherSpringFeatureTest.scala b/akka-spring/src/test/scala/DispatcherSpringFeatureTest.scala index 1f8489bdf0..6b6005de8d 100644 --- a/akka-spring/src/test/scala/DispatcherSpringFeatureTest.scala +++ b/akka-spring/src/test/scala/DispatcherSpringFeatureTest.scala @@ -4,8 +4,9 @@ package se.scalablesolutions.akka.spring -import foo.{IMyPojo, MyPojo} +import foo.{IMyPojo, MyPojo, PingActor} import se.scalablesolutions.akka.dispatch._ +import se.scalablesolutions.akka.actor.UntypedActorRef import org.scalatest.FeatureSpec import org.scalatest.matchers.ShouldMatchers @@ -111,14 +112,21 @@ class DispatcherSpringFeatureTest extends FeatureSpec with ShouldMatchers { assert(dispatcher.aggregate === false) } - /* - scenario("get a thread-based-dispatcher from context") { + /* FIXME + scenario("get a thread-based-dispatcher for typed actor from context") { val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") val pojo = context.getBean("typed-actor-with-thread-based-dispatcher").asInstanceOf[IMyPojo] assert(pojo != null) - } - */ + } */ + scenario("get a thread-based-dispatcher for untyped from context") { + val context = new ClassPathXmlApplicationContext("/dispatcher-config.xml") + val actorRef = context.getBean("untyped-actor-with-thread-based-dispatcher").asInstanceOf[UntypedActorRef] + assert(actorRef.getActorClassName() === "se.scalablesolutions.akka.spring.foo.PingActor") + actorRef.start() + actorRef.sendOneWay("Hello") + assert(actorRef.getDispatcher.isInstanceOf[ThreadBasedDispatcher]) + } } /** From 46467835022775aee11a8e3d7b55d17bcaffb5d8 Mon Sep 17 00:00:00 2001 From: Michael Kober Date: Fri, 13 Aug 2010 11:37:01 +0200 Subject: [PATCH 23/24] closing ticket198: support for thread based dispatcher in spring config --- .../src/main/scala/actor/TypedActor.scala | 11 ++++++++- .../src/main/scala/ActorFactoryBean.scala | 23 ++++++++++--------- .../src/test/resources/dispatcher-config.xml | 4 ++-- .../scala/DispatcherSpringFeatureTest.scala | 5 ++-- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/akka-core/src/main/scala/actor/TypedActor.scala b/akka-core/src/main/scala/actor/TypedActor.scala index c171a75211..78b5740344 100644 --- a/akka-core/src/main/scala/actor/TypedActor.scala +++ b/akka-core/src/main/scala/actor/TypedActor.scala @@ -8,7 +8,7 @@ import Actor._ import se.scalablesolutions.akka.config.FaultHandlingStrategy import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._ import se.scalablesolutions.akka.remote.{MessageSerializer, RemoteClient, RemoteRequestProtocolIdFactory} -import se.scalablesolutions.akka.dispatch.{MessageDispatcher, Future, CompletableFuture} +import se.scalablesolutions.akka.dispatch.{MessageDispatcher, Future, CompletableFuture, Dispatchers} import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.serialization.Serializer import se.scalablesolutions.akka.util._ @@ -202,6 +202,7 @@ final class TypedActorConfiguration { private[akka] var _transactionRequired = false private[akka] var _host: Option[InetSocketAddress] = None private[akka] var _messageDispatcher: Option[MessageDispatcher] = None + private[akka] var _threadBasedDispatcher: Option[Boolean] = None def timeout = _timeout def timeout(timeout: Duration) : TypedActorConfiguration = { @@ -220,9 +221,16 @@ final class TypedActorConfiguration { } def dispatcher(messageDispatcher: MessageDispatcher) : TypedActorConfiguration = { + if(_threadBasedDispatcher.isDefined) throw new IllegalArgumentException("Cannot specify both 'threadBasedDispatcher()' and 'dispatcher()'") _messageDispatcher = Some(messageDispatcher) this } + + def threadBasedDispatcher() : TypedActorConfiguration = { + if(_messageDispatcher.isDefined) throw new IllegalArgumentException("Cannot specify both 'threadBasedDispatcher()' and 'dispatcher()'") + _threadBasedDispatcher = Some(true) + this + } } /** @@ -324,6 +332,7 @@ object TypedActor extends Logging { def newInstance[T](intfClass: Class[T], targetClass: Class[_], config: TypedActorConfiguration): T = { val actor = actorOf(new Dispatcher(config._transactionRequired)) if (config._messageDispatcher.isDefined) actor.dispatcher = config._messageDispatcher.get + if (config._threadBasedDispatcher.isDefined) actor.dispatcher = Dispatchers.newThreadBasedDispatcher(actor) newInstance(intfClass, newTypedActor(targetClass), actor, config._host, config.timeout) } diff --git a/akka-spring/src/main/scala/ActorFactoryBean.scala b/akka-spring/src/main/scala/ActorFactoryBean.scala index 864287c96d..4bb3e88241 100644 --- a/akka-spring/src/main/scala/ActorFactoryBean.scala +++ b/akka-spring/src/main/scala/ActorFactoryBean.scala @@ -79,11 +79,8 @@ class ActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging with App * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance() */ def createInstance: AnyRef = { - var argumentList = "" - if (isRemote) argumentList += "r" - if (hasDispatcher) argumentList += "d" val ref = typed match { - case TYPED_ACTOR_TAG => val typedActor = createTypedInstance(argumentList) + case TYPED_ACTOR_TAG => val typedActor = createTypedInstance() setProperties(AspectInitRegistry.initFor(typedActor).targetInstance) typedActor case UNTYPED_ACTOR_TAG => createUntypedInstance() @@ -92,17 +89,13 @@ class ActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging with App ref } - private[akka] def createTypedInstance(argList: String) : AnyRef = { + private[akka] def createTypedInstance() : AnyRef = { if (interface == null || interface == "") throw new AkkaBeansException( "The 'interface' part of the 'akka:actor' element in the Spring config file can't be null or empty string") if (implementation == null || implementation == "") throw new AkkaBeansException( "The 'implementation' part of the 'akka:typed-actor' element in the Spring config file can't be null or empty string") - argList match { - case "r" => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig.makeRemote(host, port)) - case "d" => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig.dispatcher(dispatcherInstance())) - case "rd" => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig.makeRemote(host, port).dispatcher(dispatcherInstance())) - case _ => TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig) - } + + TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig) } /** @@ -168,6 +161,14 @@ class ActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging with App private[akka] def createConfig: TypedActorConfiguration = { val config = new TypedActorConfiguration().timeout(Duration(timeout, "millis")) if (transactional) config.makeTransactionRequired + if (isRemote) config.makeRemote(host, port) + if (hasDispatcher) { + if (dispatcher.dispatcherType != THREAD_BASED) { + config.dispatcher(dispatcherInstance()) + } else { + config.threadBasedDispatcher() + } + } config } diff --git a/akka-spring/src/test/resources/dispatcher-config.xml b/akka-spring/src/test/resources/dispatcher-config.xml index c1b746c8e7..c02c5b4d14 100644 --- a/akka-spring/src/test/resources/dispatcher-config.xml +++ b/akka-spring/src/test/resources/dispatcher-config.xml @@ -72,13 +72,13 @@ http://scalablesolutions.se/akka/akka-0.10.xsd"> - - --> + Date: Fri, 13 Aug 2010 12:27:46 +0200 Subject: [PATCH 24/24] fixed untyped actor parsing --- .../resources/se/scalablesolutions/akka/spring/akka-0.10.xsd | 2 +- akka-spring/src/main/scala/ActorParser.scala | 3 ++- akka-spring/src/test/resources/untyped-actor-config.xml | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd index 5bd18e3c44..2a42ec0900 100644 --- a/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd +++ b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd @@ -177,7 +177,7 @@ - + The default timeout for '!!' invocations. diff --git a/akka-spring/src/main/scala/ActorParser.scala b/akka-spring/src/main/scala/ActorParser.scala index 9858c1fad4..69073bd52f 100644 --- a/akka-spring/src/main/scala/ActorParser.scala +++ b/akka-spring/src/main/scala/ActorParser.scala @@ -48,7 +48,8 @@ trait ActorParser extends BeanParser with DispatcherParser { } try { - objectProperties.timeout = mandatory(element, TIMEOUT).toLong + val timeout = element.getAttribute(TIMEOUT) + objectProperties.timeout = if ((timeout != null) && (!timeout.isEmpty)) timeout.toLong else -1L } catch { case nfe: NumberFormatException => log.error(nfe, "could not parse timeout %s", element.getAttribute(TIMEOUT)) diff --git a/akka-spring/src/test/resources/untyped-actor-config.xml b/akka-spring/src/test/resources/untyped-actor-config.xml index 55e49e4707..aea5e86d44 100644 --- a/akka-spring/src/test/resources/untyped-actor-config.xml +++ b/akka-spring/src/test/resources/untyped-actor-config.xml @@ -10,8 +10,7 @@ http://www.akkasource.org/schema/akka http://scalablesolutions.se/akka/akka-0.10.xsd"> + implementation="se.scalablesolutions.akka.spring.foo.PingActor"/>