diff --git a/.gitignore b/.gitignore index 51b42bf7be..9f15d8fc57 100755 --- a/.gitignore +++ b/.gitignore @@ -18,9 +18,11 @@ deploy/*.jar data out logs +.#* .codefellow storage .codefellow +.ensime _dump .manager manifest.mf @@ -39,4 +41,5 @@ run-codefellow .classpath .idea .scala_dependencies -multiverse.log \ No newline at end of file +multiverse.log +.eprj \ No newline at end of file diff --git a/akka-active-object-test/pom.xml b/akka-active-object-test/pom.xml index dffacd8db0..e498c34d32 100644 --- a/akka-active-object-test/pom.xml +++ b/akka-active-object-test/pom.xml @@ -3,7 +3,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 - Akka Active Object Tests in Java + Akka TypedActor Tests in Java akka-active-object-test se.scalablesolutions.akka 0.9 diff --git a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/AllTest.java b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/AllTest.java index 465c9da182..77739f6ff1 100644 --- a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/AllTest.java +++ b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/AllTest.java @@ -10,7 +10,7 @@ public class AllTest extends TestCase { suite.addTestSuite(InMemoryStateTest.class); suite.addTestSuite(InMemNestedStateTest.class); suite.addTestSuite(RemoteInMemoryStateTest.class); - suite.addTestSuite(ActiveObjectGuiceConfiguratorTest.class); + suite.addTestSuite(TypedActorGuiceConfiguratorTest.class); return suite; } diff --git a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemNestedStateTest.java b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemNestedStateTest.java index 746df950bf..db9d4d4146 100644 --- a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemNestedStateTest.java +++ b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemNestedStateTest.java @@ -6,7 +6,7 @@ package se.scalablesolutions.akka.api; import se.scalablesolutions.akka.config.*; import se.scalablesolutions.akka.config.Config; -import se.scalablesolutions.akka.config.ActiveObjectConfigurator; +import se.scalablesolutions.akka.config.TypedActorConfigurator; import static se.scalablesolutions.akka.config.JavaConfig.*; import se.scalablesolutions.akka.actor.*; import junit.framework.TestCase; @@ -14,7 +14,7 @@ import junit.framework.TestCase; public class InMemNestedStateTest extends TestCase { static String messageLog = ""; - final private ActiveObjectConfigurator conf = new ActiveObjectConfigurator(); + final private TypedActorConfigurator conf = new TypedActorConfigurator(); public InMemNestedStateTest() { conf.configure( diff --git a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemoryStateTest.java b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemoryStateTest.java index 3708d58acc..6562d0d611 100644 --- a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemoryStateTest.java +++ b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemoryStateTest.java @@ -8,7 +8,7 @@ import junit.framework.TestCase; import se.scalablesolutions.akka.config.Config; import se.scalablesolutions.akka.config.*; -import se.scalablesolutions.akka.config.ActiveObjectConfigurator; +import se.scalablesolutions.akka.config.TypedActorConfigurator; import static se.scalablesolutions.akka.config.JavaConfig.*; @@ -17,7 +17,7 @@ import se.scalablesolutions.akka.actor.*; public class InMemoryStateTest extends TestCase { static String messageLog = ""; - final private ActiveObjectConfigurator conf = new ActiveObjectConfigurator(); + final private TypedActorConfigurator conf = new TypedActorConfigurator(); public InMemoryStateTest() { Config.config(); diff --git a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/MiscActiveObjectTest.java b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/MiscActiveObjectTest.java index e61b8ac07d..aaa97d3587 100644 --- a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/MiscActiveObjectTest.java +++ b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/MiscActiveObjectTest.java @@ -1,7 +1,7 @@ package se.scalablesolutions.akka.api; -import static se.scalablesolutions.akka.actor.ActiveObject.link; -import static se.scalablesolutions.akka.actor.ActiveObject.newInstance; +import static se.scalablesolutions.akka.actor.TypedActor.link; +import static se.scalablesolutions.akka.actor.TypedActor.newInstance; import org.junit.Assert; import org.junit.Test; @@ -15,7 +15,7 @@ import junit.framework.TestCase; * @author johanrask * */ -public class MiscActiveObjectTest extends TestCase { +public class MiscTypedActorTest extends TestCase { /** diff --git a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/RemoteInMemoryStateTest.java b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/RemoteInMemoryStateTest.java index d4b4fd7687..3ae8b647ab 100644 --- a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/RemoteInMemoryStateTest.java +++ b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/RemoteInMemoryStateTest.java @@ -5,8 +5,8 @@ package se.scalablesolutions.akka.api; import se.scalablesolutions.akka.config.Config; -import se.scalablesolutions.akka.actor.ActiveObject; -import se.scalablesolutions.akka.config.ActiveObjectConfigurator; +import se.scalablesolutions.akka.actor.TypedActor; +import se.scalablesolutions.akka.config.TypedActorConfigurator; import se.scalablesolutions.akka.remote.RemoteNode; import junit.framework.TestCase; @@ -23,14 +23,14 @@ public class RemoteInMemoryStateTest extends TestCase { try { Thread.currentThread().sleep(1000); } catch (Exception e) {} Config.config(); } - final ActiveObjectConfigurator conf = new ActiveObjectConfigurator(); + final TypedActorConfigurator conf = new TypedActorConfigurator(); protected void tearDown() { conf.stop(); } public void testMapShouldNotRollbackStateForStatefulServerInCaseOfSuccess() { - InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 1000, "localhost", 9999); + InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 1000, "localhost", 9999); stateful.init(); stateful.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init"); // set init state stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired @@ -38,10 +38,10 @@ public class RemoteInMemoryStateTest extends TestCase { } public void testMapShouldRollbackStateForStatefulServerInCaseOfFailure() { - InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999); + InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999); stateful.init(); stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init"); // set init state - InMemFailer failer = ActiveObject.newRemoteInstance(InMemFailer.class, 1000, "localhost", 9999); //conf.getInstance(InMemFailer.class); + InMemFailer failer = TypedActor.newRemoteInstance(InMemFailer.class, 1000, "localhost", 9999); //conf.getInstance(InMemFailer.class); try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method fail("should have thrown an exception"); @@ -51,7 +51,7 @@ public class RemoteInMemoryStateTest extends TestCase { } public void testVectorShouldNotRollbackStateForStatefulServerInCaseOfSuccess() { - InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999); + InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999); stateful.init(); stateful.setVectorState("init"); // set init state stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired @@ -59,10 +59,10 @@ public class RemoteInMemoryStateTest extends TestCase { } public void testVectorShouldRollbackStateForStatefulServerInCaseOfFailure() { - InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999); + InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999); stateful.init(); stateful.setVectorState("init"); // set init state - InMemFailer failer = ActiveObject.newRemoteInstance(InMemFailer.class, 10000, "localhost", 9999); //conf.getInstance(InMemFailer.class); + InMemFailer failer = TypedActor.newRemoteInstance(InMemFailer.class, 10000, "localhost", 9999); //conf.getInstance(InMemFailer.class); try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method fail("should have thrown an exception"); @@ -72,7 +72,7 @@ public class RemoteInMemoryStateTest extends TestCase { } public void testRefShouldNotRollbackStateForStatefulServerInCaseOfSuccess() { - InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999); + InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999); stateful.init(); stateful.setRefState("init"); // set init state stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired @@ -80,10 +80,10 @@ public class RemoteInMemoryStateTest extends TestCase { } public void testRefShouldRollbackStateForStatefulServerInCaseOfFailure() { - InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999); + InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999); stateful.init(); stateful.setRefState("init"); // set init state - InMemFailer failer = ActiveObject.newRemoteInstance(InMemFailer.class, 10000, "localhost", 9999); //conf.getInstance(InMemFailer.class); + InMemFailer failer = TypedActor.newRemoteInstance(InMemFailer.class, 10000, "localhost", 9999); //conf.getInstance(InMemFailer.class); try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method fail("should have thrown an exception"); diff --git a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/ActiveObjectGuiceConfiguratorTest.java b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/TypedActorGuiceConfiguratorTest.java similarity index 87% rename from akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/ActiveObjectGuiceConfiguratorTest.java rename to akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/TypedActorGuiceConfiguratorTest.java index 69f74ec537..e604b4da69 100644 --- a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/ActiveObjectGuiceConfiguratorTest.java +++ b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/TypedActorGuiceConfiguratorTest.java @@ -10,14 +10,14 @@ import com.google.inject.Scopes; import junit.framework.TestCase; import se.scalablesolutions.akka.config.Config; -import se.scalablesolutions.akka.config.ActiveObjectConfigurator; +import se.scalablesolutions.akka.config.TypedActorConfigurator; import static se.scalablesolutions.akka.config.JavaConfig.*; import se.scalablesolutions.akka.dispatch.*; -public class ActiveObjectGuiceConfiguratorTest extends TestCase { +public class TypedActorGuiceConfiguratorTest extends TestCase { static String messageLog = ""; - final private ActiveObjectConfigurator conf = new ActiveObjectConfigurator(); + final private TypedActorConfigurator conf = new TypedActorConfigurator(); protected void setUp() { Config.config(); @@ -46,7 +46,7 @@ public class ActiveObjectGuiceConfiguratorTest extends TestCase { } - public void testGuiceActiveObjectInjection() { + public void testGuiceTypedActorInjection() { messageLog = ""; Foo foo = conf.getInstance(Foo.class); Bar bar = conf.getInstance(Bar.class); @@ -69,7 +69,7 @@ public class ActiveObjectGuiceConfiguratorTest extends TestCase { } } - public void testActiveObjectInvocation() throws InterruptedException { + public void testTypedActorInvocation() throws InterruptedException { messageLog = ""; Foo foo = conf.getInstance(Foo.class); messageLog += foo.foo("foo "); @@ -79,7 +79,7 @@ public class ActiveObjectGuiceConfiguratorTest extends TestCase { assertEquals("foo return_foo before_bar ", messageLog); } - public void testActiveObjectInvocationsInvocation() throws InterruptedException { + public void testTypedActorInvocationsInvocation() throws InterruptedException { messageLog = ""; Foo foo = conf.getInstance(Foo.class); Bar bar = conf.getInstance(Bar.class); diff --git a/akka-amqp/src/main/scala/AMQP.scala b/akka-amqp/src/main/scala/AMQP.scala deleted file mode 100644 index 8605401dbd..0000000000 --- a/akka-amqp/src/main/scala/AMQP.scala +++ /dev/null @@ -1,143 +0,0 @@ -/** - * 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._ -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. - * - * @see se.scalablesolutions.akka.amqp.ExampleSession - * - * @author Irmo Manie - */ -object AMQP { - case class ConnectionParameters( - host: String = ConnectionFactory.DEFAULT_HOST, - port: Int = ConnectionFactory.DEFAULT_AMQP_PORT, - username: String = ConnectionFactory.DEFAULT_USER, - password: String = ConnectionFactory.DEFAULT_PASS, - virtualHost: String = ConnectionFactory.DEFAULT_VHOST, - initReconnectDelay: Long = 5000, - connectionCallback: Option[ActorRef] = None) - - case class ChannelParameters( - shutdownListener: Option[ShutdownListener] = None, - channelCallback: Option[ActorRef] = None) - - case class ExchangeParameters( - exchangeName: String, - exchangeType: ExchangeType, - exchangeDurable: Boolean = false, - exchangeAutoDelete: Boolean = true, - exchangePassive: Boolean = false, - configurationArguments: Map[String, AnyRef] = Map()) - - case class ProducerParameters(exchangeParameters: ExchangeParameters, - producerId: Option[String] = None, - returnListener: Option[ReturnListener] = None, - channelParameters: Option[ChannelParameters] = None) - - case class ConsumerParameters(exchangeParameters: ExchangeParameters, - routingKey: String, - deliveryHandler: ActorRef, - queueName: Option[String] = None, - queueDurable: Boolean = false, - queueAutoDelete: Boolean = true, - queuePassive: Boolean = false, - queueExclusive: Boolean = false, - selfAcknowledging: Boolean = true, - channelParameters: Option[ChannelParameters] = None) { - if (queueDurable && queueName.isEmpty) { - throw new IllegalArgumentException("A queue name is required when requesting a durable queue.") - } - } - - def newConnection(connectionParameters: ConnectionParameters = new ConnectionParameters): ActorRef = { - val connection: ActorRef = supervisor.newConnection(connectionParameters) - connection ! Connect - connection - } - - def newProducer(connection: ActorRef, producerParameters: ProducerParameters): ActorRef = { - val producer: ActorRef = Actor.actorOf(new ProducerActor(producerParameters)) - connection.startLink(producer) - producer ! Start - producer - } - - def newConsumer(connection: ActorRef, consumerParameters: ConsumerParameters): ActorRef = { - val consumer: ActorRef = actorOf(new ConsumerActor(consumerParameters)) - consumer.startLink(consumerParameters.deliveryHandler) - connection.startLink(consumer) - consumer ! Start - 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: PartialFunction[I, O], - 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)) - - } - - private val supervisor = new AMQPSupervisor - - class AMQPSupervisor extends Logging { - class AMQPSupervisorActor extends Actor { - import self._ - - 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 - } - } - - 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/AMQP.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala new file mode 100644 index 0000000000..cd73d27e03 --- /dev/null +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala @@ -0,0 +1,218 @@ +/** + * 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._ +import se.scalablesolutions.akka.config.OneForOneStrategy +import com.rabbitmq.client.{ReturnListener, ShutdownListener, ConnectionFactory} +import com.rabbitmq.client.AMQP.BasicProperties +import java.lang.{String, IllegalArgumentException} + +/** + * AMQP Actor API. Implements Connection, Producer and Consumer materialized as Actors. + * + * @see se.scalablesolutions.akka.amqp.ExampleSession + * + * @author Irmo Manie + */ +object AMQP { + case class ConnectionParameters( + host: String = ConnectionFactory.DEFAULT_HOST, + port: Int = ConnectionFactory.DEFAULT_AMQP_PORT, + username: String = ConnectionFactory.DEFAULT_USER, + password: String = ConnectionFactory.DEFAULT_PASS, + virtualHost: String = ConnectionFactory.DEFAULT_VHOST, + initReconnectDelay: Long = 5000, + connectionCallback: Option[ActorRef] = None) + + case class ChannelParameters( + shutdownListener: Option[ShutdownListener] = None, + channelCallback: Option[ActorRef] = None) + + case class ExchangeParameters( + exchangeName: String, + exchangeType: ExchangeType, + exchangeDurable: Boolean = false, + exchangeAutoDelete: Boolean = true, + exchangePassive: Boolean = false, + configurationArguments: Map[String, AnyRef] = Map()) + + case class ProducerParameters( + exchangeParameters: ExchangeParameters, + producerId: Option[String] = None, + returnListener: Option[ReturnListener] = None, + channelParameters: Option[ChannelParameters] = None) + + case class ConsumerParameters( + exchangeParameters: ExchangeParameters, + routingKey: String, + deliveryHandler: ActorRef, + queueName: Option[String] = None, + queueDurable: Boolean = false, + queueAutoDelete: Boolean = true, + queuePassive: Boolean = false, + queueExclusive: Boolean = false, + selfAcknowledging: Boolean = true, + channelParameters: Option[ChannelParameters] = None) { + if (queueDurable && queueName.isEmpty) { + throw new IllegalArgumentException("A queue name is required when requesting a durable queue.") + } + } + + def newConnection(connectionParameters: ConnectionParameters = new ConnectionParameters): ActorRef = { + val connection = actorOf(new FaultTolerantConnectionActor(connectionParameters)) + supervisor.startLink(connection) + connection ! Connect + connection + } + + def newProducer(connection: ActorRef, producerParameters: ProducerParameters): ActorRef = { + val producer: ActorRef = Actor.actorOf(new ProducerActor(producerParameters)) + connection.startLink(producer) + producer ! Start + producer + } + + def newConsumer(connection: ActorRef, consumerParameters: ConsumerParameters): ActorRef = { + val consumer: ActorRef = actorOf(new ConsumerActor(consumerParameters)) + val handler = consumerParameters.deliveryHandler + if (handler.supervisor.isEmpty) consumer.startLink(handler) + connection.startLink(consumer) + consumer ! Start + consumer + } + + /** + * Convenience + */ + class ProducerClient[O](client: ActorRef, routingKey: String, toBinary: ToBinary[O]) { + def send(request: O, replyTo: Option[String] = None) = { + val basicProperties = new BasicProperties + basicProperties.setReplyTo(replyTo.getOrElse(null)) + client ! Message(toBinary.toBinary(request), routingKey, false, false, Some(basicProperties)) + } + + def stop = client.stop + } + + def newStringProducer(connection: ActorRef, + exchange: String, + routingKey: Option[String] = None, + producerId: Option[String] = None, + durable: Boolean = false, + autoDelete: Boolean = true, + passive: Boolean = true): ProducerClient[String] = { + + val exchangeParameters = ExchangeParameters(exchange, ExchangeType.Topic, + exchangeDurable = durable, exchangeAutoDelete = autoDelete) + val rKey = routingKey.getOrElse("%s.request".format(exchange)) + + val producerRef = newProducer(connection, ProducerParameters(exchangeParameters, producerId)) + val toBinary = new ToBinary[String] { + def toBinary(t: String) = t.getBytes + } + new ProducerClient(producerRef, rKey, toBinary) + } + + def newStringConsumer(connection: ActorRef, + exchange: String, + handler: String => Unit, + routingKey: Option[String] = None, + queueName: Option[String] = None, + durable: Boolean = false, + autoDelete: Boolean = true): ActorRef = { + + val deliveryHandler = actor { + case Delivery(payload, _, _, _, _) => handler.apply(new String(payload)) + } + + 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)) + + newConsumer(connection, ConsumerParameters(exchangeParameters, rKey, deliveryHandler, Some(qName), durable, autoDelete)) + } + + def newProtobufProducer[O <: com.google.protobuf.Message](connection: ActorRef, + exchange: String, + routingKey: Option[String] = None, + producerId: Option[String] = None, + durable: Boolean = false, + autoDelete: Boolean = true, + passive: Boolean = true): ProducerClient[O] = { + + val exchangeParameters = ExchangeParameters(exchange, ExchangeType.Topic, + exchangeDurable = durable, exchangeAutoDelete = autoDelete) + val rKey = routingKey.getOrElse("%s.request".format(exchange)) + + val producerRef = newProducer(connection, ProducerParameters(exchangeParameters, producerId)) + new ProducerClient(producerRef, rKey, new ToBinary[O] { + def toBinary(t: O) = t.toByteArray + }) + } + + def newProtobufConsumer[I <: com.google.protobuf.Message](connection: ActorRef, + exchange: String, + handler: I => Unit, + routingKey: Option[String] = None, + queueName: Option[String] = None, + durable: Boolean = false, + autoDelete: Boolean = true)(implicit manifest: Manifest[I]): ActorRef = { + + val deliveryHandler = actor { + case Delivery(payload, _, _, _, _) => { + handler.apply(createProtobufFromBytes[I](payload)) + } + } + + 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)) + + newConsumer(connection, ConsumerParameters(exchangeParameters, rKey, deliveryHandler, Some(qName), durable, autoDelete)) + } + + /** + * Main supervisor + */ + + class AMQPSupervisorActor extends Actor { + import self._ + + faultHandler = Some(OneForOneStrategy(5, 5000)) + trapExit = List(classOf[Throwable]) + + def receive = { + case _ => {} // ignore all messages + } + } + + private val supervisor = actorOf(new AMQPSupervisorActor).start + + def shutdownAll = { + supervisor.shutdownLinkedActors + } + + /** + * Serialization stuff + */ + + trait FromBinary[T] { + def fromBinary(bytes: Array[Byte]): T + } + + trait ToBinary[T] { + def toBinary(t: T): Array[Byte] + } + + private val ARRAY_OF_BYTE_ARRAY = Array[Class[_]](classOf[Array[Byte]]) + + private[amqp] def createProtobufFromBytes[I <: com.google.protobuf.Message](bytes: Array[Byte])(implicit manifest: Manifest[I]): I = { + manifest.erasure.getDeclaredMethod("parseFrom", ARRAY_OF_BYTE_ARRAY: _*).invoke(null, bytes).asInstanceOf[I] + } +} diff --git a/akka-amqp/src/main/scala/AMQPMessage.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala similarity index 60% rename from akka-amqp/src/main/scala/AMQPMessage.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala index bf2461723f..2b53a73d08 100644 --- a/akka-amqp/src/main/scala/AMQPMessage.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala @@ -5,25 +5,27 @@ package se.scalablesolutions.akka.amqp import se.scalablesolutions.akka.actor.ActorRef +import se.scalablesolutions.akka.AkkaException + import com.rabbitmq.client.AMQP.BasicProperties import com.rabbitmq.client.ShutdownSignalException sealed trait AMQPMessage sealed trait InternalAMQPMessage extends AMQPMessage -case class Message(payload: Array[Byte], - routingKey: String, - mandatory: Boolean = false, - immediate: Boolean = false, - properties: Option[BasicProperties] = None) extends AMQPMessage - -case class Delivery(payload: Array[Byte], - routingKey: String, - deliveryTag: Long, - properties: BasicProperties, - sender: Option[ActorRef]) extends AMQPMessage - +case class Message( + payload: Array[Byte], + routingKey: String, + mandatory: Boolean = false, + immediate: Boolean = false, + properties: Option[BasicProperties] = None) extends AMQPMessage +case class Delivery( + payload: Array[Byte], + routingKey: String, + deliveryTag: Long, + properties: BasicProperties, + sender: Option[ActorRef]) extends AMQPMessage // connection messages case object Connect extends AMQPMessage @@ -44,6 +46,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 @@ -51,10 +56,10 @@ private[akka] case class ConnectionShutdown(cause: ShutdownSignalException) exte private[akka] case class ChannelShutdown(cause: ShutdownSignalException) extends InternalAMQPMessage private[akka] class MessageNotDeliveredException( - val message: String, - val replyCode: Int, - val replyText: String, - val exchange: String, - val routingKey: String, - val properties: BasicProperties, - val body: Array[Byte]) extends RuntimeException(message) + val message: String, + val replyCode: Int, + val replyText: String, + val exchange: String, + val routingKey: String, + val properties: BasicProperties, + val body: Array[Byte]) extends RuntimeException(message) diff --git a/akka-amqp/src/main/scala/ConsumerActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala similarity index 70% rename from akka-amqp/src/main/scala/ConsumerActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala index 26d1ac00db..d3f0acd1cf 100644 --- a/akka-amqp/src/main/scala/ConsumerActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala @@ -4,17 +4,20 @@ package se.scalablesolutions.akka.amqp -import com.rabbitmq.client.AMQP.Queue.DeclareOk import collection.JavaConversions + import se.scalablesolutions.akka.amqp.AMQP.ConsumerParameters import se.scalablesolutions.akka.util.Logging -import com.rabbitmq.client.{Channel, Envelope, DefaultConsumer} +import se.scalablesolutions.akka.AkkaException + +import com.rabbitmq.client.AMQP.Queue.DeclareOk import com.rabbitmq.client.AMQP.BasicProperties -import java.lang.Throwable +import com.rabbitmq.client.{Channel, Envelope, DefaultConsumer} private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) - extends FaultTolerantChannelActor(consumerParameters.exchangeParameters, consumerParameters.channelParameters) { - + extends FaultTolerantChannelActor( + consumerParameters.exchangeParameters, consumerParameters.channelParameters) { + import consumerParameters._ import exchangeParameters._ @@ -22,6 +25,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 => @@ -34,10 +38,11 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) queueName match { case Some(name) => log.debug("Declaring new queue [%s] for %s", name, toString) - if (queuePassive) { - ch.queueDeclarePassive(name) - } else { - ch.queueDeclare(name, queueDurable, queueExclusive, queueAutoDelete, JavaConversions.asMap(configurationArguments)) + if (queuePassive) ch.queueDeclarePassive(name) + else { + ch.queueDeclare( + name, queueDurable, queueExclusive, queueAutoDelete, + JavaConversions.asMap(configurationArguments)) } case None => log.debug("Declaring new generated queue for %s", toString) @@ -80,12 +85,24 @@ 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) } - override def preRestart(reason: Throwable) = { listenerTag = None super.preRestart(reason) @@ -93,15 +110,15 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) override def shutdown = { listenerTag.foreach(tag => channel.foreach(_.basicCancel(tag))) - self.linkedActorsAsList.foreach(_.stop) + self.shutdownLinkedActors super.shutdown } - override def toString(): String = + override def toString = "AMQP.Consumer[id= "+ self.id + - ", exchange=" + exchangeName + - ", exchangeType=" + exchangeType + - ", durable=" + exchangeDurable + - ", autoDelete=" + exchangeAutoDelete + "]" + ", exchange=" + exchangeName + + ", exchangeType=" + exchangeType + + ", durable=" + exchangeDurable + + ", autoDelete=" + exchangeAutoDelete + "]" } diff --git a/akka-amqp/src/main/scala/ExampleSession.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala similarity index 57% rename from akka-amqp/src/main/scala/ExampleSession.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala index 97571e2783..f3a8197abf 100644 --- a/akka-amqp/src/main/scala/ExampleSession.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala @@ -4,44 +4,64 @@ package se.scalablesolutions.akka.amqp +import rpc.RPC +import rpc.RPC.{RpcClientSerializer, RpcServerSerializer} import se.scalablesolutions.akka.actor.{Actor, ActorRegistry} import Actor._ import java.util.concurrent.{CountDownLatch, TimeUnit} -import se.scalablesolutions.akka.amqp.AMQP._ import java.lang.String +import se.scalablesolutions.akka.amqp.AMQP._ +import se.scalablesolutions.akka.remote.protocol.RemoteProtocol.AddressProtocol object ExampleSession { def main(args: Array[String]) = { - println("==== DIRECT ===") + + printTopic("DIRECT") direct - TimeUnit.SECONDS.sleep(2) - - println("==== FANOUT ===") + printTopic("FANOUT") fanout - TimeUnit.SECONDS.sleep(2) - - println("==== TOPIC ===") + printTopic("TOPIC") topic - TimeUnit.SECONDS.sleep(2) - - println("==== CALLBACK ===") + printTopic("CALLBACK") callback - TimeUnit.SECONDS.sleep(2) + printTopic("EASY STRING PRODUCER AND CONSUMER") + easyStringProducerConsumer - println("==== RPC ===") + printTopic("EASY PROTOBUF PRODUCER AND CONSUMER") + easyProtobufProducerConsumer + + printTopic("RPC") rpc - TimeUnit.SECONDS.sleep(2) + printTopic("EASY STRING RPC") + easyStringRpc + + printTopic("EASY PROTOBUF RPC") + easyProtobufRpc + + printTopic("Happy hAkking :-)") + + // shutdown everything the amqp tree except the main AMQP supervisor + // all connections/consumers/producers will be stopped + AMQP.shutdownAll ActorRegistry.shutdownAll System.exit(0) } + def printTopic(topic: String) { + + println("") + println("==== " + topic + " ===") + println("") + TimeUnit.SECONDS.sleep(2) + } + def direct = { // defaults to amqp://guest:guest@localhost:5672/ @@ -115,7 +135,7 @@ object ExampleSession { case Restarting => // not used, sent when channel or connection fails and initiates a restart case Stopped => log.info("Channel callback: Stopped") } - val exchangeParameters = ExchangeParameters("my_direct_exchange", ExchangeType.Direct) + val exchangeParameters = ExchangeParameters("my_callback_exchange", ExchangeType.Direct) val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) val consumer = AMQP.newConsumer(connection, ConsumerParameters(exchangeParameters, "callback.routing", actor { @@ -129,6 +149,40 @@ object ExampleSession { connection.stop } + def easyStringProducerConsumer = { + val connection = AMQP.newConnection() + + val exchangeName = "easy.string" + + // listen by default to: + // exchange = exchangeName + // routingKey = .request + // queueName = .in + AMQP.newStringConsumer(connection, exchangeName, message => println("Received message: "+message)) + + // send by default to: + // exchange = exchangeName + // routingKey = .request + val producer = AMQP.newStringProducer(connection, exchangeName) + + producer.send("This shit is easy!") + } + + def easyProtobufProducerConsumer = { + val connection = AMQP.newConnection() + + val exchangeName = "easy.protobuf" + + def protobufMessageHandler(message: AddressProtocol) = { + log.info("Received "+message) + } + + AMQP.newProtobufConsumer(connection, exchangeName, protobufMessageHandler) + + val producerClient = AMQP.newProtobufProducer[AddressProtocol](connection, exchangeName) + producerClient.send(AddressProtocol.newBuilder.setHostname("akkarocks.com").setPort(1234).build) + } + def rpc = { val connection = AMQP.newConnection() @@ -144,10 +198,10 @@ object ExampleSession { } val rpcServerSerializer = new RpcServerSerializer[String, Int](serverFromBinary, serverToBinary) - val rpcServer = AMQP.newRpcServer[String,Int](connection, exchangeParameters, "rpc.in.key", rpcServerSerializer, { - case "rpc_request" => 3 - case _ => error("unknown request") - }) + def requestHandler(request: String) = 3 + + val rpcServer = RPC.newRpcServer[String,Int](connection, exchangeParameters, "rpc.in.key", rpcServerSerializer, + requestHandler, queueName = Some("rpc.in.key.queue")) /** Client */ @@ -159,9 +213,56 @@ 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) } + + def easyStringRpc = { + + val connection = AMQP.newConnection() + + val exchangeName = "easy.stringrpc" + + // listen by default to: + // exchange = exchangeName + // routingKey = .request + // queueName = .in + RPC.newStringRpcServer(connection, exchangeName, request => { + log.info("Got request: "+request) + "Response to: '"+request+"'" + }) + + // send by default to: + // exchange = exchangeName + // routingKey = .request + val stringRpcClient = RPC.newStringRpcClient(connection, exchangeName) + + val response = stringRpcClient.call("AMQP Rocks!") + log.info("Got response: "+response) + + stringRpcClient.callAsync("AMQP is dead easy") { + case response => log.info("This is handled async: "+response) + } + } + + def easyProtobufRpc = { + + val connection = AMQP.newConnection() + + val exchangeName = "easy.protobuf.rpc" + + def protobufRequestHandler(request: AddressProtocol): AddressProtocol = { + AddressProtocol.newBuilder.setHostname(request.getHostname.reverse).setPort(request.getPort).build + } + + RPC.newProtobufRpcServer(connection, exchangeName, protobufRequestHandler) + + val protobufRpcClient = RPC.newProtobufRpcClient[AddressProtocol, AddressProtocol](connection, exchangeName) + + val response = protobufRpcClient.call(AddressProtocol.newBuilder.setHostname("localhost").setPort(4321).build) + + log.info("Got 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 99% rename from akka-amqp/src/main/scala/FaultTolerantChannelActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala index 40bcd5de57..5ecae4c6d3 100644 --- a/akka-amqp/src/main/scala/FaultTolerantChannelActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala @@ -14,7 +14,7 @@ import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ChannelParameter abstract private[amqp] class FaultTolerantChannelActor( exchangeParameters: ExchangeParameters, channelParameters: Option[ChannelParameters]) extends Actor { - + import exchangeParameters._ protected[amqp] var channel: Option[Channel] = None @@ -104,4 +104,4 @@ abstract private[amqp] class FaultTolerantChannelActor( } override def shutdown = closeChannel -} \ No newline at end of file +} diff --git a/akka-amqp/src/main/scala/FaultTolerantConnectionActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala similarity index 98% rename from akka-amqp/src/main/scala/FaultTolerantConnectionActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala index 5f0a49910e..1e50a985be 100644 --- a/akka-amqp/src/main/scala/FaultTolerantConnectionActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala @@ -60,7 +60,6 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio } private def connect = if (connection.isEmpty || !connection.get.isOpen) { - try { connection = Some(connectionFactory.newConnection) connection.foreach { @@ -108,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 } @@ -118,5 +117,4 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio notifyCallback(Reconnecting) connect } - } diff --git a/akka-amqp/src/main/scala/ProducerActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala similarity index 64% rename from akka-amqp/src/main/scala/ProducerActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala index db290a5ac1..3551ffa276 100644 --- a/akka-amqp/src/main/scala/ProducerActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala @@ -5,11 +5,14 @@ package se.scalablesolutions.akka.amqp import com.rabbitmq.client._ + import se.scalablesolutions.akka.amqp.AMQP.ProducerParameters +import se.scalablesolutions.akka.AkkaException private[amqp] class ProducerActor(producerParameters: ProducerParameters) - extends FaultTolerantChannelActor(producerParameters.exchangeParameters, producerParameters.channelParameters) { - + extends FaultTolerantChannelActor( + producerParameters.exchangeParameters, producerParameters.channelParameters) { + import producerParameters._ import exchangeParameters._ @@ -32,29 +35,29 @@ private[amqp] class ProducerActor(producerParameters: ProducerParameters) case Some(listener) => ch.setReturnListener(listener) case None => ch.setReturnListener(new ReturnListener() { def handleBasicReturn( - replyCode: Int, - replyText: String, - exchange: String, - routingKey: String, - properties: com.rabbitmq.client.AMQP.BasicProperties, - body: Array[Byte]) = { + replyCode: Int, + replyText: String, + exchange: String, + routingKey: String, + properties: com.rabbitmq.client.AMQP.BasicProperties, + body: Array[Byte]) = { throw new MessageNotDeliveredException( "Could not deliver message [" + body + - "] with reply code [" + replyCode + - "] with reply text [" + replyText + - "] and routing key [" + routingKey + - "] to exchange [" + exchange + "]", + "] with reply code [" + replyCode + + "] with reply text [" + replyText + + "] and routing key [" + routingKey + + "] to exchange [" + exchange + "]", replyCode, replyText, exchange, routingKey, properties, body) } }) } } - override def toString(): String = + override def toString = "AMQP.Poducer[id= "+ self.id + - ", exchange=" + exchangeName + - ", exchangeType=" + exchangeType + - ", durable=" + exchangeDurable + - ", autoDelete=" + exchangeAutoDelete + "]" + ", exchange=" + exchangeName + + ", exchangeType=" + exchangeType + + ", durable=" + exchangeDurable + + ", autoDelete=" + exchangeAutoDelete + "]" } 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..b51cbe407f --- /dev/null +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala @@ -0,0 +1,182 @@ +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): RpcServerHandle = { + val producer = newProducer(connection, ProducerParameters( + ExchangeParameters("", ExchangeType.Direct), channelParameters = channelParameters)) + val rpcServer = actorOf(new RpcServerActor[I, O](producer, serializer, requestHandler)) + val consumer = newConsumer(connection, ConsumerParameters(exchangeParameters, routingKey, rpcServer, + channelParameters = channelParameters, selfAcknowledging = false, queueName = queueName)) + RpcServerHandle(producer, consumer) + } + + case class RpcServerHandle(producer: ActorRef, consumer: ActorRef) { + def stop = { + consumer.stop + producer.stop + } + } + + case class RpcClientSerializer[O, I](toBinary: ToBinary[O], fromBinary: FromBinary[I]) + + case class RpcServerSerializer[I, O](fromBinary: FromBinary[I], toBinary: ToBinary[O]) + + + /** + * RPC convenience + */ + class RpcClient[O, I](client: ActorRef){ + 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) + } + } + def stop = client.stop + } + + def newProtobufRpcServer[I <: Message, O <: Message]( + connection: ActorRef, + exchange: String, + requestHandler: I => O, + routingKey: Option[String] = None, + queueName: Option[String] = None, + durable: Boolean = false, + autoDelete: Boolean = true)(implicit manifest: Manifest[I]): RpcServerHandle = { + + 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 + }) + + startServer(connection, exchange, requestHandler, routingKey, queueName, durable, autoDelete, serializer) + } + + def newProtobufRpcClient[O <: Message, I <: Message]( + connection: ActorRef, + exchange: String, + routingKey: Option[String] = None, + durable: Boolean = false, + autoDelete: Boolean = true, + passive: Boolean = true)(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) + } + }) + + startClient(connection, exchange, routingKey, durable, autoDelete, passive, serializer) + } + + def newStringRpcServer(connection: ActorRef, + exchange: String, + requestHandler: String => String, + routingKey: Option[String] = None, + queueName: Option[String] = None, + durable: Boolean = false, + autoDelete: Boolean = true): RpcServerHandle = { + + val serializer = new RpcServerSerializer[String, String]( + new FromBinary[String] { + def fromBinary(bytes: Array[Byte]): String = { + new String(bytes) + } + }, new ToBinary[String] { + def toBinary(t: String) = t.getBytes + }) + + startServer(connection, exchange, requestHandler, routingKey, queueName, durable, autoDelete, serializer) + } + + def newStringRpcClient(connection: ActorRef, + exchange: String, + routingKey: Option[String] = None, + durable: Boolean = false, + autoDelete: Boolean = true, + passive: Boolean = true): RpcClient[String, String] = { + + + val serializer = new RpcClientSerializer[String, String]( + new ToBinary[String] { + def toBinary(t: String) = t.getBytes + }, new FromBinary[String] { + def fromBinary(bytes: Array[Byte]): String = { + new String(bytes) + } + }) + + startClient(connection, exchange, routingKey, durable, autoDelete, passive, serializer) + } + + private def startClient[O, I](connection: ActorRef, + exchange: String, + routingKey: Option[String] = None, + durable: Boolean = false, + autoDelete: Boolean = true, + passive: Boolean = true, + serializer: RpcClientSerializer[O, I]): RpcClient[O, I] = { + + val exchangeParameters = ExchangeParameters(exchange, ExchangeType.Topic, + exchangeDurable = durable, exchangeAutoDelete = autoDelete, exchangePassive = passive) + val rKey = routingKey.getOrElse("%s.request".format(exchange)) + + val client = newRpcClient(connection, exchangeParameters, rKey, serializer) + new RpcClient(client) + } + + private def startServer[I, O](connection: ActorRef, + exchange: String, + requestHandler: I => O, + routingKey: Option[String] = None, + queueName: Option[String] = None, + durable: Boolean = false, + autoDelete: Boolean = true, + serializer: RpcServerSerializer[I, O]): RpcServerHandle = { + + 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)) + + newRpcServer(connection, exchangeParameters, rKey, serializer, requestHandler, queueName = Some(qName)) + } +} + 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 68% rename from akka-amqp/src/main/scala/RpcClientActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcClientActor.scala index ba85005777..5c717cb8bb 100644 --- a/akka-amqp/src/main/scala/RpcClientActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcClientActor.scala @@ -4,16 +4,16 @@ package se.scalablesolutions.akka.amqp -import se.scalablesolutions.akka.serialization.Serializer +import com.rabbitmq.client.{Channel, RpcClient} +import rpc.RPC.RpcClientSerializer import se.scalablesolutions.akka.amqp.AMQP.{ChannelParameters, ExchangeParameters} -import com.rabbitmq.client.{Channel, RpcClient} -import se.scalablesolutions.akka.amqp.AMQP.{RpcClientSerializer, ChannelParameters, ExchangeParameters} - -class RpcClientActor[I,O](exchangeParameters: ExchangeParameters, - routingKey: String, - serializer: RpcClientSerializer[I,O], - channelParameters: Option[ChannelParameters] = None) extends FaultTolerantChannelActor(exchangeParameters, channelParameters) { +class RpcClientActor[I,O]( + exchangeParameters: ExchangeParameters, + routingKey: String, + serializer: RpcClientSerializer[I,O], + channelParameters: Option[ChannelParameters] = None) + extends FaultTolerantChannelActor(exchangeParameters, channelParameters) { import exchangeParameters._ @@ -39,5 +39,11 @@ class RpcClientActor[I,O](exchangeParameters: ExchangeParameters, super.preRestart(reason) } + + override def shutdown = { + rpcClient.foreach(rpc => rpc.close) + super.shutdown + } + override def toString = "AMQP.RpcClient[exchange=" +exchangeName + ", routingKey=" + routingKey+ "]" -} \ No newline at end of file +} 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 78% rename from akka-amqp/src/main/scala/RpcServerActor.scala rename to akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcServerActor.scala index 897c041c69..5f6b4b713c 100644 --- a/akka-amqp/src/main/scala/RpcServerActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcServerActor.scala @@ -4,11 +4,14 @@ 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: PartialFunction[I, O]) extends Actor { +class RpcServerActor[I,O]( + producer: ActorRef, + serializer: RpcServerSerializer[I,O], + requestHandler: I => O) extends Actor { log.info("%s started", this) @@ -29,6 +32,5 @@ class RpcServerActor[I,O](producer: ActorRef, serializer: RpcServerSerializer[I, case Acknowledged(tag) => log.debug("%s acknowledged delivery with tag %d", this, tag) } - override def toString(): String = - "AMQP.RpcServer[]" -} \ No newline at end of file + override def toString = "AMQP.RpcServer[]" +} diff --git a/akka-amqp/src/test/scala/AMQPRpcClientServerTest.scala b/akka-amqp/src/test/scala/AMQPRpcClientServerTest.scala deleted file mode 100644 index 7dbfb4becd..0000000000 --- a/akka-amqp/src/test/scala/AMQPRpcClientServerTest.scala +++ /dev/null @@ -1,71 +0,0 @@ -/** - * 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 se.scalablesolutions.akka.actor.Actor._ -import org.scalatest.matchers.MustMatchers -import java.util.concurrent.{CountDownLatch, TimeUnit} -import se.scalablesolutions.akka.serialization.Serializer -import se.scalablesolutions.akka.amqp.AMQP._ - -class AMQPRpcClientServerTest extends JUnitSuite with MustMatchers with Logging { - - @Test - def consumerMessage = if (AMQPTest.enabled) { - val connection = AMQP.newConnection() - try { - - val countDown = new CountDownLatch(3) - val channelCallback = actor { - case Started => countDown.countDown - case Restarting => () - case Stopped => () - } - - val exchangeParameters = ExchangeParameters("text_topic_exchange", ExchangeType.Topic) - val channelParameters = ChannelParameters(channelCallback - = Some(channelCallback)) - - val serverFromBinary = new FromBinary[String] { - def fromBinary(bytes: Array[Byte]) = new String(bytes) - } - val serverToBinary = new ToBinary[Int] { - def toBinary(t: Int) = Array(t.toByte) - } - val rpcServerSerializer = new RpcServerSerializer[String, Int](serverFromBinary, serverToBinary) - val rpcServer = AMQP.newRpcServer[String,Int](connection, exchangeParameters, "rpc.routing", rpcServerSerializer, { - case "some_payload" => 3 - case _ => error("unknown request") - }, channelParameters = Some(channelParameters)) - - val clientToBinary = new ToBinary[String] { - def toBinary(t: String) = t.getBytes - } - val clientFromBinary = new FromBinary[Int] { - def fromBinary(bytes: Array[Byte]) = bytes.head.toInt - } - val rpcClientSerializer = new RpcClientSerializer[String, Int](clientToBinary, clientFromBinary) - val rpcClient = AMQP.newRpcClient[String,Int](connection, exchangeParameters, "rpc.routing", rpcClientSerializer, - channelParameters = Some(channelParameters)) - - countDown.await(2, TimeUnit.SECONDS) must be (true) - val response = rpcClient !! "some_payload" - response must be (Some(3)) - } finally { - 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) - } -} \ No newline at end of file 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 81% rename from akka-amqp/src/test/scala/AMQPConnectionRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTest.scala index 3bc2cb20dd..f9d30227f0 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,11 +11,13 @@ 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) { + def connectionAndRecovery = if (AMQPTest.enabled) AMQPTest.withCleanEndState { val connectedLatch = new StandardLatch val reconnectingLatch = new StandardLatch @@ -45,15 +44,9 @@ class AMQPConnectionRecoveryTest extends JUnitSuite with MustMatchers with Loggi reconnectedLatch.tryAwait(2, TimeUnit.SECONDS) must be(true) } finally { - connection.stop + AMQP.shutdownAll disconnectedLatch.tryAwait(2, TimeUnit.SECONDS) must be(true) } } - @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) - } -} \ No newline at end of file +} 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 86% rename from akka-amqp/src/test/scala/AMQPConsumerChannelRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala index 0f6fadfcc4..31a90c8200 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,11 +12,13 @@ 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) { + def consumerChannelRecovery = if (AMQPTest.enabled) AMQPTest.withCleanEndState { val connection = AMQP.newConnection(ConnectionParameters(initReconnectDelay = 50)) try { @@ -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) - } -} \ No newline at end of file +} 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 86% rename from akka-amqp/src/test/scala/AMQPConsumerConnectionRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala index 9dccd43be8..50c078a13a 100644 --- a/akka-amqp/src/test/scala/AMQPConsumerConnectionRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala @@ -1,25 +1,24 @@ +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) { + def consumerConnectionRecovery = if (AMQPTest.enabled) AMQPTest.withCleanEndState { val connection = AMQP.newConnection(ConnectionParameters(initReconnectDelay = 50)) try { @@ -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) - } -} \ No newline at end of file +} 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 86% rename from akka-amqp/src/test/scala/AMQPConsumerManualAcknowledgeTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala index d48f38afc5..011f287636 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,11 +11,13 @@ 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) { + def consumerMessageManualAcknowledge = if (AMQPTest.enabled) AMQPTest.withCleanEndState { val connection = AMQP.newConnection() try { val countDown = new CountDownLatch(2) @@ -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) - } -} \ No newline at end of file +} 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..d00d09b480 --- /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) AMQPTest.withCleanEndState { + 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 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 83% rename from akka-amqp/src/test/scala/AMQPConsumerMessageTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala index af94b0a515..88661de58d 100644 --- a/akka-amqp/src/test/scala/AMQPConsumerMessageTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala @@ -1,23 +1,22 @@ +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) { + def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { val connection = AMQP.newConnection() try { @@ -38,7 +37,7 @@ class AMQPConsumerMessageTest extends JUnitSuite with MustMatchers with Logging val producer = AMQP.newProducer(connection, ProducerParameters(exchangeParameters, channelParameters = Some(channelParameters))) - + countDown.await(2, TimeUnit.SECONDS) must be (true) producer ! Message("some_payload".getBytes, "non.interesting.routing.key") payloadLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) @@ -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) - } -} \ No newline at end of file +} 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 84% rename from akka-amqp/src/test/scala/AMQPProducerChannelRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTest.scala index 095a21fc86..e0ede02de3 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,11 +11,13 @@ 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) { + def producerChannelRecovery = if (AMQPTest.enabled) AMQPTest.withCleanEndState { val connection = AMQP.newConnection(ConnectionParameters(initReconnectDelay = 50)) @@ -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) - } -} \ No newline at end of file +} 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 84% rename from akka-amqp/src/test/scala/AMQPProducerConnectionRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTest.scala index 71bc08bdaa..ad756ff5f0 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,11 +11,13 @@ 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) { + def producerConnectionRecovery = if (AMQPTest.enabled) AMQPTest.withCleanEndState { val connection = AMQP.newConnection(ConnectionParameters(initReconnectDelay = 50)) try { @@ -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) - } -} \ No newline at end of file +} 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 81% rename from akka-amqp/src/test/scala/AMQPProducerMessageTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTest.scala index ab9bb00e7c..7d485b1b8f 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,12 +13,14 @@ 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) { - + def producerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + val connection: ActorRef = AMQP.newConnection() try { val returnLatch = new StandardLatch @@ -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) - } -} \ No newline at end of file +} diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProtobufProducerConsumerTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProtobufProducerConsumerTest.scala new file mode 100644 index 0000000000..5d03dae5c2 --- /dev/null +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProtobufProducerConsumerTest.scala @@ -0,0 +1,43 @@ +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 org.junit.Test +import org.multiverse.api.latches.StandardLatch +import java.util.concurrent.TimeUnit +import se.scalablesolutions.akka.amqp.rpc.RPC +import se.scalablesolutions.akka.remote.protocol.RemoteProtocol.AddressProtocol + +class AMQPProtobufProducerConsumerTest extends JUnitSuite with MustMatchers { + + @Test + def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + + val connection = AMQP.newConnection() + + val responseLatch = new StandardLatch + + RPC.newProtobufRpcServer(connection, "protoexchange", requestHandler) + + val request = AddressProtocol.newBuilder.setHostname("testhost").setPort(4321).build + + def responseHandler(response: AddressProtocol) = { + assert(response.getHostname == request.getHostname.reverse) + responseLatch.open + } + AMQP.newProtobufConsumer(connection, "", responseHandler, Some("proto.reply.key")) + + val producer = AMQP.newProtobufProducer[AddressProtocol](connection, "protoexchange") + producer.send(request, Some("proto.reply.key")) + + responseLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) + } + + 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/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTest.scala new file mode 100644 index 0000000000..7de8044314 --- /dev/null +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTest.scala @@ -0,0 +1,61 @@ +package se.scalablesolutions.akka.amqp.test + +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +import se.scalablesolutions.akka.amqp._ +import rpc.RPC +import rpc.RPC.{RpcClientSerializer, RpcServerSerializer} +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 { + + @Test + def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + + val connection = AMQP.newConnection() + + val countDown = new CountDownLatch(3) + val channelCallback = actor { + case Started => countDown.countDown + case Restarting => () + case Stopped => () + } + + val exchangeParameters = ExchangeParameters("text_topic_exchange", ExchangeType.Topic) + val channelParameters = ChannelParameters(channelCallback + = Some(channelCallback)) + + val rpcServerSerializer = new RpcServerSerializer[String, Int]( + new FromBinary[String] { + def fromBinary(bytes: Array[Byte]) = new String(bytes) + }, new ToBinary[Int] { + def toBinary(t: Int) = Array(t.toByte) + }) + + def requestHandler(request: String) = 3 + + val rpcServer = RPC.newRpcServer[String, Int](connection, exchangeParameters, "rpc.routing", rpcServerSerializer, + requestHandler, channelParameters = Some(channelParameters)) + + val rpcClientSerializer = new RpcClientSerializer[String, Int]( + new ToBinary[String] { + def toBinary(t: String) = t.getBytes + }, new FromBinary[Int] { + def fromBinary(bytes: Array[Byte]) = bytes.head.toInt + }) + + val rpcClient = RPC.newRpcClient[String, Int](connection, exchangeParameters, "rpc.routing", rpcClientSerializer, + channelParameters = Some(channelParameters)) + + countDown.await(2, TimeUnit.SECONDS) must be(true) + val response = rpcClient !! "some_payload" + response must be(Some(3)) + } +} 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..6b796374a6 --- /dev/null +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTest.scala @@ -0,0 +1,49 @@ +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 +import org.multiverse.api.latches.StandardLatch +import java.util.concurrent.TimeUnit + +class AMQPRpcProtobufTest extends JUnitSuite with MustMatchers { + + @Test + def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + + val connection = AMQP.newConnection() + + RPC.newProtobufRpcServer(connection, "protoservice", requestHandler) + + val protobufClient = RPC.newProtobufRpcClient[AddressProtocol, AddressProtocol](connection, "protoservice") + + val request = AddressProtocol.newBuilder.setHostname("testhost").setPort(4321).build + + 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 = { + AddressProtocol.newBuilder.setHostname(request.getHostname.reverse).setPort(request.getPort).build + } +} \ No newline at end of file diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcStringTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcStringTest.scala new file mode 100644 index 0000000000..0a55fda954 --- /dev/null +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcStringTest.scala @@ -0,0 +1,47 @@ +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 org.junit.Test +import se.scalablesolutions.akka.amqp.rpc.RPC +import org.multiverse.api.latches.StandardLatch +import java.util.concurrent.TimeUnit + +class AMQPRpcStringTest extends JUnitSuite with MustMatchers { + + @Test + def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + + val connection = AMQP.newConnection() + + RPC.newStringRpcServer(connection, "stringservice", requestHandler) + + val protobufClient = RPC.newStringRpcClient(connection, "stringservice") + + val request = "teststring" + + protobufClient.call(request) match { + case Some(response) => assert(response == request.reverse) + case None => fail("no response") + } + + val aSyncLatch = new StandardLatch + protobufClient.callAsync(request) { + case Some(response) => { + assert(response == request.reverse) + aSyncLatch.open + } + case None => fail("no response") + } + + aSyncLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) + } + + def requestHandler(request: String): String= { + request.reverse + } +} \ No newline at end of file diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTest.scala new file mode 100644 index 0000000000..bbb77c51a7 --- /dev/null +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTest.scala @@ -0,0 +1,44 @@ +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 org.junit.Test +import org.multiverse.api.latches.StandardLatch +import java.util.concurrent.TimeUnit +import se.scalablesolutions.akka.amqp.rpc.RPC + +class AMQPStringProducerConsumerTest extends JUnitSuite with MustMatchers { + + @Test + def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + + val connection = AMQP.newConnection() + + val responseLatch = new StandardLatch + + RPC.newStringRpcServer(connection, "stringexchange", requestHandler) + + val request = "somemessage" + + def responseHandler(response: String) = { + + assert(response == request.reverse) + responseLatch.open + } + AMQP.newStringConsumer(connection, "", responseHandler, Some("string.reply.key")) + + val producer = AMQP.newStringProducer(connection, "stringexchange") + producer.send(request, Some("string.reply.key")) + + responseLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) + } + + def requestHandler(request: String): String= { + println("###### Reverse") + request.reverse + } +} \ 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 51% rename from akka-amqp/src/test/scala/AMQPTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala index e50ab673f6..2930ce4e68 100644 --- a/akka-amqp/src/test/scala/AMQPTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala @@ -4,6 +4,16 @@ package se.scalablesolutions.akka.amqp.test +import se.scalablesolutions.akka.amqp.AMQP object AMQPTest { + def enabled = false -} \ No newline at end of file + + def withCleanEndState(action: => Unit) { + try { + action + } finally { + AMQP.shutdownAll + } + } +} diff --git a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/consume.java b/akka-camel/src/main/java/se/scalablesolutions/akka/camel/consume.java similarity index 59% rename from akka-core/src/main/java/se/scalablesolutions/akka/annotation/consume.java rename to akka-camel/src/main/java/se/scalablesolutions/akka/camel/consume.java index 34d42debab..e2ea003894 100644 --- a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/consume.java +++ b/akka-camel/src/main/java/se/scalablesolutions/akka/camel/consume.java @@ -2,17 +2,26 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ - package se.scalablesolutions.akka.actor.annotation; +package se.scalablesolutions.akka.camel; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Annotation used by implementations of {@link se.scalablesolutions.akka.actor.TypedActor} + * (on method-level) to define consumer endpoints. + * + * @author Martin Krasser + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface consume { + /** + * Consumer endpoint URI + */ public abstract String value(); } diff --git a/akka-camel/src/main/resources/META-INF/services/org/apache/camel/component/active-object b/akka-camel/src/main/resources/META-INF/services/org/apache/camel/component/active-object deleted file mode 100644 index 5dd88a0671..0000000000 --- a/akka-camel/src/main/resources/META-INF/services/org/apache/camel/component/active-object +++ /dev/null @@ -1 +0,0 @@ -class=se.scalablesolutions.akka.camel.component.ActiveObjectComponent \ No newline at end of file diff --git a/akka-camel/src/main/resources/META-INF/services/org/apache/camel/component/typed-actor b/akka-camel/src/main/resources/META-INF/services/org/apache/camel/component/typed-actor new file mode 100644 index 0000000000..e004d887b3 --- /dev/null +++ b/akka-camel/src/main/resources/META-INF/services/org/apache/camel/component/typed-actor @@ -0,0 +1 @@ +class=se.scalablesolutions.akka.camel.component.TypedActorComponent \ No newline at end of file diff --git a/akka-camel/src/main/scala/CamelContextLifecycle.scala b/akka-camel/src/main/scala/CamelContextLifecycle.scala index 640ba14e36..05c18396b8 100644 --- a/akka-camel/src/main/scala/CamelContextLifecycle.scala +++ b/akka-camel/src/main/scala/CamelContextLifecycle.scala @@ -9,7 +9,7 @@ import java.util.Map import org.apache.camel.{ProducerTemplate, CamelContext} import org.apache.camel.impl.DefaultCamelContext -import se.scalablesolutions.akka.camel.component.ActiveObjectComponent +import se.scalablesolutions.akka.camel.component.TypedActorComponent import se.scalablesolutions.akka.util.Logging /** @@ -29,15 +29,15 @@ trait CamelContextLifecycle extends Logging { private var _started = false /** - * Camel component for accessing active objects. + * Camel component for accessing typed actors. */ - private[camel] var activeObjectComponent: ActiveObjectComponent = _ + private[camel] var typedActorComponent: TypedActorComponent = _ /** - * Registry in which active objects are TEMPORARILY registered during - * creation of Camel routes to active objects. + * Registry in which typed actors are TEMPORARILY registered during + * creation of Camel routes to typed actors. */ - private[camel] var activeObjectRegistry: Map[String, AnyRef] = _ + private[camel] var typedActorRegistry: Map[String, AnyRef] = _ /** * Returns the managed CamelContext. @@ -93,15 +93,15 @@ trait CamelContextLifecycle extends Logging { * CamelContext stream-caching is enabled. If applications want to disable stream- * caching they can do so after this method returned and prior to calling start. * This method also registers a new - * {@link se.scalablesolutions.akka.camel.component.ActiveObjectComponent} at - * context under a name defined by ActiveObjectComponent.InternalSchema. + * {@link se.scalablesolutions.akka.camel.component.TypedActorComponent} at + * context under a name defined by TypedActorComponent.InternalSchema. */ def init(context: CamelContext) { - this.activeObjectComponent = new ActiveObjectComponent - this.activeObjectRegistry = activeObjectComponent.activeObjectRegistry + this.typedActorComponent = new TypedActorComponent + this.typedActorRegistry = typedActorComponent.typedActorRegistry this.context = context this.context.setStreamCaching(true) - this.context.addComponent(ActiveObjectComponent.InternalSchema, activeObjectComponent) + this.context.addComponent(TypedActorComponent.InternalSchema, typedActorComponent) this.template = context.createProducerTemplate _initialized = true log.info("Camel context initialized") diff --git a/akka-camel/src/main/scala/CamelService.scala b/akka-camel/src/main/scala/CamelService.scala index 65a6a44fe5..d47daf8d9a 100644 --- a/akka-camel/src/main/scala/CamelService.scala +++ b/akka-camel/src/main/scala/CamelService.scala @@ -1,24 +1,24 @@ /** * Copyright (C) 2009-2010 Scalable Solutions AB */ - package se.scalablesolutions.akka.camel +import java.util.concurrent.CountDownLatch + +import org.apache.camel.CamelContext + import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.actor.{AspectInitRegistry, ActorRegistry} import se.scalablesolutions.akka.util.{Bootable, Logging} /** - * Used by applications (and the Kernel) to publish consumer actors and active objects via + * Used by applications (and the Kernel) to publish consumer actors and typed actors via * Camel endpoints and to manage the life cycle of a a global CamelContext which can be * accessed via se.scalablesolutions.akka.camel.CamelContextManager.context. * * @author Martin Krasser */ trait CamelService extends Bootable with Logging { - - import CamelContextManager._ - private[camel] val consumerPublisher = actorOf[ConsumerPublisher] private[camel] val publishRequestor = actorOf[PublishRequestor] @@ -32,72 +32,142 @@ trait CamelService extends Bootable with Logging { * Starts the CamelService. Any started actor that is a consumer actor will be (asynchronously) * published as Camel endpoint. Consumer actors that are started after this method returned will * be published as well. Actor publishing is done asynchronously. A started (loaded) CamelService - * also publishes @consume annotated methods of active objects that have been created - * with ActiveObject.newInstance(..) (and ActiveObject.newInstance(..) + * also publishes @consume annotated methods of typed actors that have been created + * with TypedActor.newInstance(..) (and TypedActor.newInstance(..) * on a remote node). */ abstract override def onLoad = { super.onLoad // Only init and start if not already done by application - if (!initialized) init - if (!started) start + if (!CamelContextManager.initialized) CamelContextManager.init + if (!CamelContextManager.started) CamelContextManager.start - // start actor that exposes consumer actors and active objects via Camel endpoints + // start actor that exposes consumer actors and typed actors via Camel endpoints consumerPublisher.start // init publishRequestor so that buffered and future events are delivered to consumerPublisher publishRequestor ! PublishRequestorInit(consumerPublisher) + + // Register this instance as current CamelService + CamelServiceManager.register(this) } /** * Stops the CamelService. */ abstract override def onUnload = { + // Unregister this instance as current CamelService + CamelServiceManager.unregister(this) + + // Remove related listeners from registry ActorRegistry.removeListener(publishRequestor) AspectInitRegistry.removeListener(publishRequestor) + + // Stop related services consumerPublisher.stop - stop + CamelContextManager.stop + super.onUnload } + @deprecated("use start() instead") + def load: CamelService = { + onLoad + this + } + + @deprecated("use stop() instead") + def unload = onUnload + /** * Starts the CamelService. * * @see onLoad */ - def load: CamelService = { + def start: CamelService = { onLoad this } - + /** * Stops the CamelService. * * @see onUnload */ - def unload = onUnload + def stop = onUnload + + /** + * Sets an expectation of the number of upcoming endpoint activations and returns + * a {@link CountDownLatch} that can be used to wait for the activations to occur. + * Endpoint activations that occurred in the past are not considered. + */ + def expectEndpointActivationCount(count: Int): CountDownLatch = + (consumerPublisher !! SetExpectedRegistrationCount(count)).as[CountDownLatch].get + + /** + * Sets an expectation of the number of upcoming endpoint de-activations and returns + * a {@link CountDownLatch} that can be used to wait for the de-activations to occur. + * Endpoint de-activations that occurred in the past are not considered. + */ + def expectEndpointDeactivationCount(count: Int): CountDownLatch = + (consumerPublisher !! SetExpectedUnregistrationCount(count)).as[CountDownLatch].get } /** - * CamelService companion object used by standalone applications to create their own - * CamelService instance. + * ... * * @author Martin Krasser */ -object CamelService { +object CamelServiceManager { /** - * Creates a new CamelService instance. + * The current (optional) CamelService. Is defined when a CamelService has been started. */ - def newInstance: CamelService = new DefaultCamelService + private var _current: Option[CamelService] = None + + /** + * Starts a new CamelService and makes it the current CamelService. + */ + def startCamelService = CamelServiceFactory.createCamelService.start + + /** + * Stops the current CamelService. + */ + def stopCamelService = service.stop + + /** + * Returns the current CamelService. + * + * @throws IllegalStateException if there's no current CamelService. + */ + def service = + if (_current.isDefined) _current.get + else throw new IllegalStateException("no current CamelService") + + private[camel] def register(service: CamelService) = + if (_current.isDefined) throw new IllegalStateException("current CamelService already registered") + else _current = Some(service) + + private[camel] def unregister(service: CamelService) = + if (_current == Some(service)) _current = None + else throw new IllegalStateException("only current CamelService can be unregistered") } /** - * Default CamelService implementation to be created in Java applications with - *
- * CamelService service = new DefaultCamelService()
- * 
+ * @author Martin Krasser */ -class DefaultCamelService extends CamelService { +object CamelServiceFactory { + /** + * Creates a new CamelService instance + */ + def createCamelService: CamelService = new CamelService { } + + /** + * Creates a new CamelService instance + */ + def createCamelService(camelContext: CamelContext): CamelService = { + CamelContextManager.init(camelContext) + createCamelService + } } diff --git a/akka-camel/src/main/scala/Consumer.scala b/akka-camel/src/main/scala/Consumer.scala index e5218a21f2..3b54c5df77 100644 --- a/akka-camel/src/main/scala/Consumer.scala +++ b/akka-camel/src/main/scala/Consumer.scala @@ -4,7 +4,9 @@ package se.scalablesolutions.akka.camel -import se.scalablesolutions.akka.actor.{ActorRef, Actor} +import se.scalablesolutions.akka.actor._ + +import java.net.InetSocketAddress /** * Mixed in by Actor implementations that consume message from Camel endpoints. @@ -25,6 +27,58 @@ trait Consumer { self: Actor => def blocking = false } +/** + * Java-friendly {@link Consumer} inherited by + * + *
    + *
  • {@link UntypedConsumerActor}
  • + *
  • {@link RemoteUntypedConsumerActor}
  • + *
  • {@link UntypedConsumerTransactor}
  • + *
+ * + * implementations. + * + * @author Martin Krasser + */ +trait UntypedConsumer extends Consumer { self: UntypedActor => + + final override def endpointUri = getEndpointUri + + final override def blocking = isBlocking + + /** + * Returns the Camel endpoint URI to consume messages from. + */ + def getEndpointUri(): String + + /** + * Determines whether two-way communications with this consumer actor should + * be done in blocking or non-blocking mode (default is non-blocking). One-way + * communications never block. + */ + def isBlocking() = super.blocking +} + +/** + * Subclass this abstract class to create an MDB-style untyped consumer actor. This + * class is meant to be used from Java. + */ +abstract class UntypedConsumerActor extends UntypedActor with UntypedConsumer + +/** + * Subclass this abstract class to create an MDB-style transacted untyped consumer + * actor. This class is meant to be used from Java. + */ +abstract class UntypedConsumerTransactor extends UntypedTransactor with UntypedConsumer + +/** + * Subclass this abstract class to create an MDB-style remote untyped consumer + * actor. This class is meant to be used from Java. + */ +abstract class RemoteUntypedConsumerActor(address: InetSocketAddress) extends RemoteUntypedActor(address) with UntypedConsumer { + def this(host: String, port: Int) = this(new InetSocketAddress(host, port)) +} + /** * @author Martin Krasser */ @@ -42,4 +96,4 @@ private[camel] object Consumer { else if (actorRef.remoteAddress.isDefined) None else Some(f(actorRef.actor.asInstanceOf[Consumer])) } -} \ No newline at end of file +} diff --git a/akka-camel/src/main/scala/ConsumerPublisher.scala b/akka-camel/src/main/scala/ConsumerPublisher.scala index 298c70c2b7..c0b64021af 100644 --- a/akka-camel/src/main/scala/ConsumerPublisher.scala +++ b/akka-camel/src/main/scala/ConsumerPublisher.scala @@ -12,8 +12,7 @@ import java.util.concurrent.CountDownLatch import org.apache.camel.builder.RouteBuilder import se.scalablesolutions.akka.actor._ -import se.scalablesolutions.akka.actor.annotation.consume -import se.scalablesolutions.akka.camel.component.ActiveObjectComponent +import se.scalablesolutions.akka.camel.component.TypedActorComponent import se.scalablesolutions.akka.util.Logging /** @@ -37,15 +36,15 @@ private[camel] object ConsumerPublisher extends Logging { } /** - * Creates a route to an active object method. + * Creates a route to an typed actor method. */ def handleConsumerMethodRegistered(event: ConsumerMethodRegistered) { val targetMethod = event.method.getName val objectId = "%s_%s" format (event.init.actorRef.uuid, targetMethod) - CamelContextManager.activeObjectRegistry.put(objectId, event.activeObject) + CamelContextManager.typedActorRegistry.put(objectId, event.typedActor) CamelContextManager.context.addRoutes(new ConsumerMethodRoute(event.uri, objectId, targetMethod)) - log.info("published method %s of %s at endpoint %s" format (targetMethod, event.activeObject, event.uri)) + log.info("published method %s of %s at endpoint %s" format (targetMethod, event.typedActor, event.uri)) } /** @@ -55,66 +54,66 @@ private[camel] object ConsumerPublisher extends Logging { val targetMethod = event.method.getName val objectId = "%s_%s" format (event.init.actorRef.uuid, targetMethod) - CamelContextManager.activeObjectRegistry.remove(objectId) + CamelContextManager.typedActorRegistry.remove(objectId) CamelContextManager.context.stopRoute(objectId) - log.info("unpublished method %s of %s from endpoint %s" format (targetMethod, event.activeObject, event.uri)) + log.info("unpublished method %s of %s from endpoint %s" format (targetMethod, event.typedActor, event.uri)) } } /** - * Actor that publishes consumer actors and active object methods at Camel endpoints. + * Actor that publishes consumer actors and typed actor methods at Camel endpoints. * The Camel context used for publishing is CamelContextManager.context. This actor * accepts messages of type - * se.scalablesolutions.akka.camel.service.ConsumerRegistered, - * se.scalablesolutions.akka.camel.service.ConsumerMethodRegistered and - * se.scalablesolutions.akka.camel.service.ConsumerUnregistered. + * se.scalablesolutions.akka.camel.ConsumerRegistered, + * se.scalablesolutions.akka.camel.ConsumerUnregistered. + * se.scalablesolutions.akka.camel.ConsumerMethodRegistered and + * se.scalablesolutions.akka.camel.ConsumerMethodUnregistered. * * @author Martin Krasser */ private[camel] class ConsumerPublisher extends Actor { import ConsumerPublisher._ - @volatile private var latch = new CountDownLatch(0) + @volatile private var registrationLatch = new CountDownLatch(0) + @volatile private var unregistrationLatch = new CountDownLatch(0) - /** - * Adds a route to the actor identified by a Publish message to the global CamelContext. - */ protected def receive = { case r: ConsumerRegistered => { handleConsumerRegistered(r) - latch.countDown // needed for testing only. + registrationLatch.countDown } case u: ConsumerUnregistered => { handleConsumerUnregistered(u) - latch.countDown // needed for testing only. + unregistrationLatch.countDown } case mr: ConsumerMethodRegistered => { handleConsumerMethodRegistered(mr) - latch.countDown // needed for testing only. + registrationLatch.countDown } case mu: ConsumerMethodUnregistered => { handleConsumerMethodUnregistered(mu) - latch.countDown // needed for testing only. + unregistrationLatch.countDown } - case SetExpectedMessageCount(num) => { - // needed for testing only. - latch = new CountDownLatch(num) - self.reply(latch) + case SetExpectedRegistrationCount(num) => { + registrationLatch = new CountDownLatch(num) + self.reply(registrationLatch) + } + case SetExpectedUnregistrationCount(num) => { + unregistrationLatch = new CountDownLatch(num) + self.reply(unregistrationLatch) } case _ => { /* ignore */} } } -/** - * Command message used For testing-purposes only. - */ -private[camel] case class SetExpectedMessageCount(num: Int) +private[camel] case class SetExpectedRegistrationCount(num: Int) +private[camel] case class SetExpectedUnregistrationCount(num: Int) /** - * Defines an abstract route to a target which is either an actor or an active object method.. + * Defines an abstract route to a target which is either an actor or an typed actor method.. * - * @param endpointUri endpoint URI of the consumer actor or active object method. - * @param id actor identifier or active object identifier (registry key). + * @param endpointUri endpoint URI of the consumer actor or typed actor method. + * @param id actor identifier or typed actor identifier (registry key). * * @author Martin Krasser */ @@ -149,20 +148,20 @@ private[camel] class ConsumerActorRoute(endpointUri: String, uuid: String, block } /** - * Defines the route to an active object method.. + * Defines the route to an typed actor method.. * * @param endpointUri endpoint URI of the consumer actor method - * @param id active object identifier + * @param id typed actor identifier * @param method name of the method to invoke. * * @author Martin Krasser */ private[camel] class ConsumerMethodRoute(val endpointUri: String, id: String, method: String) extends ConsumerRoute(endpointUri, id) { - protected override def targetUri = "%s:%s?method=%s" format (ActiveObjectComponent.InternalSchema, id, method) + protected override def targetUri = "%s:%s?method=%s" format (TypedActorComponent.InternalSchema, id, method) } /** - * A registration listener that triggers publication of consumer actors and active object + * A registration listener that triggers publication of consumer actors and typed actor * methods as well as un-publication of consumer actors. This actor needs to be initialized * with a PublishRequestorInit command message for obtaining a reference to * a publisher actor. Before initialization it buffers all outbound messages @@ -209,7 +208,7 @@ private[camel] class PublishRequestor extends Actor { /** * Command message to initialize a PublishRequestor to use consumerPublisher - * for publishing actors or active object methods. + * for publishing actors or typed actor methods. */ private[camel] case class PublishRequestorInit(consumerPublisher: ActorRef) @@ -244,32 +243,30 @@ private[camel] case class ConsumerRegistered(actorRef: ActorRef, uri: String, uu private[camel] case class ConsumerUnregistered(actorRef: ActorRef, uri: String, uuid: String) extends ConsumerEvent /** - * Event indicating that an active object proxy has been created for a POJO. For each - * @consume annotated POJO method a separate instance of this class is - * created. + * Event indicating that an typed actor proxy has been created for a typed actor. For each @consume + * annotated typed actor method a separate instance of this class is created. * - * @param activeObject active object (proxy). + * @param typedActor typed actor (proxy). * @param init - * @param uri endpoint URI of the active object method + * @param uri endpoint URI of the typed actor method * @param method method to be published. * * @author Martin Krasser */ -private[camel] case class ConsumerMethodRegistered(activeObject: AnyRef, init: AspectInit, uri: String, method: Method) extends ConsumerEvent +private[camel] case class ConsumerMethodRegistered(typedActor: AnyRef, init: AspectInit, uri: String, method: Method) extends ConsumerEvent /** - * Event indicating that an active object has been stopped. For each - * @consume annotated POJO method a separate instance of this class is - * created. + * Event indicating that an typed actor has been stopped. For each @consume + * annotated typed object method a separate instance of this class is created. * - * @param activeObject active object (proxy). + * @param typedActor typed actor (proxy). * @param init - * @param uri endpoint URI of the active object method + * @param uri endpoint URI of the typed actor method * @param method method to be un-published. * * @author Martin Krasser */ -private[camel] case class ConsumerMethodUnregistered(activeObject: AnyRef, init: AspectInit, uri: String, method: Method) extends ConsumerEvent +private[camel] case class ConsumerMethodUnregistered(typedActor: AnyRef, init: AspectInit, uri: String, method: Method) extends ConsumerEvent /** * @author Martin Krasser @@ -306,18 +303,22 @@ private[camel] object ConsumerUnregistered { */ private[camel] object ConsumerMethod { /** - * Applies a function f to each consumer method of activeObject and + * Applies a function f to each consumer method of TypedActor and * returns the function results as a list. A consumer method is one that is annotated with - * @consume. If activeObject is a proxy for a remote active object + * @consume. If typedActor is a proxy for a remote typed actor * f is never called and Nil is returned. */ - def forConsumer[T](activeObject: AnyRef, init: AspectInit)(f: Method => T): List[T] = { - // TODO: support consumer annotation inheritance - // - visit overridden methods in superclasses - // - visit implemented method declarations in interfaces - if (init.remoteAddress.isDefined) Nil // let remote node publish active object methods on endpoints - else for (m <- activeObject.getClass.getMethods.toList; if (m.isAnnotationPresent(classOf[consume]))) - yield f(m) + def forConsumer[T](typedActor: AnyRef, init: AspectInit)(f: Method => T): List[T] = { + if (init.remoteAddress.isDefined) Nil // let remote node publish typed actor methods on endpoints + else { + // TODO: support consumer annotation inheritance + // - visit overridden methods in superclasses + // - visit implemented method declarations in interfaces + val intfClass = typedActor.getClass + val implClass = init.targetInstance.getClass + (for (m <- intfClass.getMethods.toList; if (m.isAnnotationPresent(classOf[consume]))) yield f(m)) ++ + (for (m <- implClass.getMethods.toList; if (m.isAnnotationPresent(classOf[consume]))) yield f(m)) + } } } @@ -326,13 +327,13 @@ private[camel] object ConsumerMethod { */ private[camel] object ConsumerMethodRegistered { /** - * Creates a list of ConsumerMethodRegistered event messages for an active object or an empty - * list if the active object is a proxy for an remote active object or the active object doesn't + * Creates a list of ConsumerMethodRegistered event messages for an typed actor or an empty + * list if the typed actor is a proxy for an remote typed actor or the typed actor doesn't * have any @consume annotated methods. */ - def forConsumer(activeObject: AnyRef, init: AspectInit): List[ConsumerMethodRegistered] = { - ConsumerMethod.forConsumer(activeObject, init) { - m => ConsumerMethodRegistered(activeObject, init, m.getAnnotation(classOf[consume]).value, m) + def forConsumer(typedActor: AnyRef, init: AspectInit): List[ConsumerMethodRegistered] = { + ConsumerMethod.forConsumer(typedActor, init) { + m => ConsumerMethodRegistered(typedActor, init, m.getAnnotation(classOf[consume]).value, m) } } } @@ -342,13 +343,13 @@ private[camel] object ConsumerMethodRegistered { */ private[camel] object ConsumerMethodUnregistered { /** - * Creates a list of ConsumerMethodUnregistered event messages for an active object or an empty - * list if the active object is a proxy for an remote active object or the active object doesn't + * Creates a list of ConsumerMethodUnregistered event messages for an typed actor or an empty + * list if the typed actor is a proxy for an remote typed actor or the typed actor doesn't * have any @consume annotated methods. */ - def forConsumer(activeObject: AnyRef, init: AspectInit): List[ConsumerMethodUnregistered] = { - ConsumerMethod.forConsumer(activeObject, init) { - m => ConsumerMethodUnregistered(activeObject, init, m.getAnnotation(classOf[consume]).value, m) + def forConsumer(typedActor: AnyRef, init: AspectInit): List[ConsumerMethodUnregistered] = { + ConsumerMethod.forConsumer(typedActor, init) { + m => ConsumerMethodUnregistered(typedActor, init, m.getAnnotation(classOf[consume]).value, m) } } } diff --git a/akka-camel/src/main/scala/Message.scala b/akka-camel/src/main/scala/Message.scala index ec382df0c9..a834568a22 100644 --- a/akka-camel/src/main/scala/Message.scala +++ b/akka-camel/src/main/scala/Message.scala @@ -23,7 +23,6 @@ case class Message(val body: Any, val headers: Map[String, Any] = Map.empty) { * * @see CamelContextManager. */ - @deprecated("use bodyAs[T](implicit m: Manifest[T]): T instead") def bodyAs[T](clazz: Class[T]): T = CamelContextManager.context.getTypeConverter.mandatoryConvertTo[T](clazz, body) @@ -43,6 +42,26 @@ case class Message(val body: Any, val headers: Map[String, Any] = Map.empty) { */ def headers(names: Set[String]): Map[String, Any] = headers.filter(names contains _._1) + /** + * Returns the header with given name. Throws NoSuchElementException + * if the header doesn't exist. + */ + def header(name: String): Any = headers(name) + + /** + * Returns the header with given name converted to type T. Throws + * NoSuchElementException if the header doesn't exist. + */ + def headerAs[T](name: String)(implicit m: Manifest[T]): T = + CamelContextManager.context.getTypeConverter.mandatoryConvertTo[T](m.erasure.asInstanceOf[Class[T]], header(name)) + + /** + * Returns the header with given name converted to type given by the clazz + * argument. Throws NoSuchElementException if the header doesn't exist. + */ + def headerAs[T](name: String, clazz: Class[T]): T = + CamelContextManager.context.getTypeConverter.mandatoryConvertTo[T](clazz, header(name)) + /** * Creates a Message with a new body using a transformer function. */ diff --git a/akka-camel/src/main/scala/Producer.scala b/akka-camel/src/main/scala/Producer.scala index c49591ec7f..6f5c914a65 100644 --- a/akka-camel/src/main/scala/Producer.scala +++ b/akka-camel/src/main/scala/Producer.scala @@ -9,14 +9,14 @@ import CamelMessageConversion.toExchangeAdapter import org.apache.camel._ import org.apache.camel.processor.SendProcessor -import se.scalablesolutions.akka.actor.{Actor, ActorRef} +import se.scalablesolutions.akka.actor.{Actor, ActorRef, UntypedActor} /** - * Mixed in by Actor implementations that produce messages to Camel endpoints. + * Support trait for producing messages to Camel endpoints. * * @author Martin Krasser */ -trait Producer { this: Actor => +trait ProducerSupport { this: Actor => /** * Message headers to copy by default from request message to response-message. @@ -141,11 +141,6 @@ trait Producer { this: Actor => case msg => if (!oneway) self.reply(msg) } - /** - * Default implementation of Actor.receive - */ - protected def receive = produce - /** * Creates a new Exchange with given pattern from the endpoint specified by * endpointUri. @@ -162,6 +157,78 @@ trait Producer { this: Actor => } } +/** + * Mixed in by Actor implementations that produce messages to Camel endpoints. + */ +trait Producer extends ProducerSupport { this: Actor => + + /** + * Default implementation of Actor.receive + */ + protected def receive = produce +} + +/** + * Java-friendly {@link ProducerSupport} inherited by {@link UntypedProducerActor} implementations. + * + * @author Martin Krasser + */ +trait UntypedProducer extends ProducerSupport { this: UntypedActor => + + final override def endpointUri = getEndpointUri + + final override def oneway = isOneway + + final override def receiveBeforeProduce = { + case msg => onReceiveBeforeProduce(msg) + } + + final override def receiveAfterProduce = { + case msg => onReceiveAfterProduce(msg) + } + + /** + * Default implementation of UntypedActor.onReceive + */ + def onReceive(message: Any) = produce(message) + + /** + * Returns the Camel endpoint URI to produce messages to. + */ + def getEndpointUri(): String + + /** + * If set to false (default), this producer expects a response message from the Camel endpoint. + * If set to true, this producer communicates with the Camel endpoint with an in-only message + * exchange pattern (fire and forget). + */ + def isOneway() = super.oneway + + /** + * Called before the message is sent to the endpoint specified by getEndpointUri. The original + * message is passed as argument. By default, this method simply returns the argument but may be overridden + * by subclasses. + */ + @throws(classOf[Exception]) + def onReceiveBeforeProduce(message: Any): Any = super.receiveBeforeProduce(message) + + /** + * Called after the a result was received from the endpoint specified by getEndpointUri. The + * result is passed as argument. By default, this method replies the result back to the original sender + * if isOneway returns false. If isOneway returns true then nothing is done. This + * method may be overridden by subclasses. + */ + @throws(classOf[Exception]) + def onReceiveAfterProduce(message: Any): Unit = super.receiveAfterProduce(message) +} + +/** + * Subclass this abstract class to create an untyped producer actor. This class is meant to be used from Java. + * + * @author Martin Krasser + */ +abstract class UntypedProducerActor extends UntypedActor with UntypedProducer + /** * @author Martin Krasser */ diff --git a/akka-camel/src/main/scala/component/ActorComponent.scala b/akka-camel/src/main/scala/component/ActorComponent.scala index e267fcd077..a5d56dd9dc 100644 --- a/akka-camel/src/main/scala/component/ActorComponent.scala +++ b/akka-camel/src/main/scala/component/ActorComponent.scala @@ -14,18 +14,17 @@ import jsr166x.Deque import org.apache.camel._ import org.apache.camel.impl.{DefaultProducer, DefaultEndpoint, DefaultComponent} -import se.scalablesolutions.akka.actor.{ActorRegistry, Actor, ActorRef} import se.scalablesolutions.akka.camel.{Failure, CamelMessageConversion, Message} +import CamelMessageConversion.toExchangeAdapter import se.scalablesolutions.akka.dispatch.{CompletableFuture, MessageInvocation, MessageDispatcher} import se.scalablesolutions.akka.stm.TransactionConfig +import se.scalablesolutions.akka.actor.{ScalaActorRef, ActorRegistry, Actor, ActorRef} +import se.scalablesolutions.akka.AkkaException import scala.reflect.BeanProperty -import CamelMessageConversion.toExchangeAdapter -import java.lang.Throwable - /** - * Camel component for sending messages to and receiving replies from actors. + * Camel component for sending messages to and receiving replies from (untyped) actors. * * @see se.scalablesolutions.akka.camel.component.ActorEndpoint * @see se.scalablesolutions.akka.camel.component.ActorProducer @@ -50,7 +49,7 @@ class ActorComponent extends DefaultComponent { } /** - * Camel endpoint for referencing an actor. The actor reference is given by the endpoint URI. + * Camel endpoint for referencing an (untyped) actor. The actor reference is given by the endpoint URI. * An actor can be referenced by its ActorRef.id or its ActorRef.uuid. * Supported endpoint URI formats are * actor:<actorid>, @@ -68,7 +67,7 @@ class ActorEndpoint(uri: String, val uuid: Option[String]) extends DefaultEndpoint(uri, comp) { /** - * Blocking of client thread during two-way message exchanges with consumer actors. This is set + * Blocking of caller thread during two-way message exchanges with consumer actors. This is set * via the blocking=true|false endpoint URI parameter. If omitted blocking is false. */ @BeanProperty var blocking: Boolean = false @@ -91,7 +90,7 @@ class ActorEndpoint(uri: String, } /** - * Sends the in-message of an exchange to an actor. If the exchange pattern is out-capable and + * Sends the in-message of an exchange to an (untyped) actor. If the exchange pattern is out-capable and * blocking is enabled then the producer waits for a reply (using the !! operator), * otherwise the ! operator is used for sending the message. * @@ -132,10 +131,8 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) with Asyn result match { case Some(msg: Failure) => exchange.fromFailureMessage(msg) case Some(msg) => exchange.fromResponseMessage(Message.canonicalize(msg)) - case None => { - throw new TimeoutException("timeout (%d ms) while waiting response from %s" - format (actor.timeout, ep.getEndpointUri)) - } + case None => throw new TimeoutException("timeout (%d ms) while waiting response from %s" + format (actor.timeout, ep.getEndpointUri)) } } @@ -150,9 +147,8 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) with Asyn else targetByUuid(ep.uuid.get) private def targetById(id: String) = ActorRegistry.actorsFor(id) match { - case Nil => None - case actor :: Nil => Some(actor) - case actors => Some(actors.head) + case actors if actors.length == 0 => None + case actors => Some(actors(0)) } private def targetByUuid(uuid: String) = ActorRegistry.actorFor(uuid) @@ -200,7 +196,7 @@ private[akka] object AsyncCallbackAdapter { * * @author Martin Krasser */ -private[akka] class AsyncCallbackAdapter(exchange: Exchange, callback: AsyncCallback) extends ActorRef { +private[akka] class AsyncCallbackAdapter(exchange: Exchange, callback: AsyncCallback) extends ActorRef with ScalaActorRef { def start = { _isRunning = true @@ -242,15 +238,15 @@ private[akka] class AsyncCallbackAdapter(exchange: Exchange, callback: AsyncCall def unlink(actorRef: ActorRef): Unit = unsupported def startLink(actorRef: ActorRef): Unit = unsupported def startLinkRemote(actorRef: ActorRef, hostname: String, port: Int): Unit = unsupported - def spawn[T <: Actor : Manifest]: ActorRef = unsupported - def spawnRemote[T <: Actor: Manifest](hostname: String, port: Int): ActorRef = unsupported - def spawnLink[T <: Actor: Manifest]: ActorRef = unsupported - def spawnLinkRemote[T <: Actor : Manifest](hostname: String, port: Int): ActorRef = unsupported + def spawn(clazz: Class[_ <: Actor]): ActorRef = unsupported + def spawnRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = unsupported + def spawnLink(clazz: Class[_ <: Actor]): ActorRef = unsupported + def spawnLinkRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = unsupported def shutdownLinkedActors: Unit = unsupported - def mailboxSize: Int = unsupported def supervisor: Option[ActorRef] = unsupported protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout[T](message: Any, timeout: Long, senderOption: Option[ActorRef], senderFuture: Option[CompletableFuture[T]]) = unsupported - protected[akka] def mailbox: Deque[MessageInvocation] = unsupported + protected[akka] def mailbox: AnyRef = unsupported + protected[akka] def mailbox_=(msg: AnyRef):AnyRef = unsupported protected[akka] def restart(reason: Throwable, maxNrOfRetries: Int, withinTimeRange: Int): Unit = unsupported protected[akka] def restartLinkedActors(reason: Throwable, maxNrOfRetries: Int, withinTimeRange: Int): Unit = unsupported protected[akka] def handleTrapExit(dead: ActorRef, reason: Throwable): Unit = unsupported @@ -260,7 +256,7 @@ private[akka] class AsyncCallbackAdapter(exchange: Exchange, callback: AsyncCall protected[akka] def remoteAddress_=(addr: Option[InetSocketAddress]): Unit = unsupported protected[akka] def registerSupervisorAsRemoteActor = unsupported protected[akka] def supervisor_=(sup: Option[ActorRef]): Unit = unsupported - protected[this] def actorInstance: AtomicReference[Actor] = unsupported + protected[akka] def actorInstance: AtomicReference[Actor] = unsupported private def unsupported = throw new UnsupportedOperationException("Not supported for %s" format classOf[AsyncCallbackAdapter].getName) -} \ No newline at end of file +} diff --git a/akka-camel/src/main/scala/component/ActiveObjectComponent.scala b/akka-camel/src/main/scala/component/TypedActorComponent.scala similarity index 64% rename from akka-camel/src/main/scala/component/ActiveObjectComponent.scala rename to akka-camel/src/main/scala/component/TypedActorComponent.scala index 05fa026e04..2a48cf9fc4 100644 --- a/akka-camel/src/main/scala/component/ActiveObjectComponent.scala +++ b/akka-camel/src/main/scala/component/TypedActorComponent.scala @@ -12,31 +12,31 @@ import org.apache.camel.component.bean._ /** * @author Martin Krasser */ -object ActiveObjectComponent { +object TypedActorComponent { /** - * Default schema name for active object endpoint URIs. + * Default schema name for typed actor endpoint URIs. */ - val InternalSchema = "active-object-internal" + val InternalSchema = "typed-actor-internal" } /** - * Camel component for exchanging messages with active objects. This component - * tries to obtain the active object from the activeObjectRegistry + * Camel component for exchanging messages with typed actors. This component + * tries to obtain the typed actor from the typedActorRegistry * first. If it's not there it tries to obtain it from the CamelContext's registry. * * @see org.apache.camel.component.bean.BeanComponent * * @author Martin Krasser */ -class ActiveObjectComponent extends BeanComponent { - val activeObjectRegistry = new ConcurrentHashMap[String, AnyRef] +class TypedActorComponent extends BeanComponent { + val typedActorRegistry = new ConcurrentHashMap[String, AnyRef] /** * Creates a {@link org.apache.camel.component.bean.BeanEndpoint} with a custom - * bean holder that uses activeObjectRegistry for getting access to - * active objects (beans). + * bean holder that uses typedActorRegistry for getting access to + * typed actors (beans). * - * @see se.scalablesolutions.akka.camel.component.ActiveObjectHolder + * @see se.scalablesolutions.akka.camel.component.TypedActorHolder */ override def createEndpoint(uri: String, remaining: String, parameters: Map[String, AnyRef]) = { val endpoint = new BeanEndpoint(uri, this) @@ -47,39 +47,39 @@ class ActiveObjectComponent extends BeanComponent { } private def createBeanHolder(beanName: String) = - new ActiveObjectHolder(activeObjectRegistry, getCamelContext, beanName).createCacheHolder + new TypedActorHolder(typedActorRegistry, getCamelContext, beanName).createCacheHolder } /** * {@link org.apache.camel.component.bean.BeanHolder} implementation that uses a custom - * registry for getting access to active objects. + * registry for getting access to typed actors. * * @author Martin Krasser */ -class ActiveObjectHolder(activeObjectRegistry: Map[String, AnyRef], context: CamelContext, name: String) +class TypedActorHolder(typedActorRegistry: Map[String, AnyRef], context: CamelContext, name: String) extends RegistryBean(context, name) { /** - * Returns an {@link se.scalablesolutions.akka.camel.component.ActiveObjectInfo} instance. + * Returns an {@link se.scalablesolutions.akka.camel.component.TypedActorInfo} instance. */ override def getBeanInfo: BeanInfo = - new ActiveObjectInfo(getContext, getBean.getClass, getParameterMappingStrategy) + new TypedActorInfo(getContext, getBean.getClass, getParameterMappingStrategy) /** - * Obtains an active object from activeObjectRegistry. + * Obtains an typed actor from typedActorRegistry. */ override def getBean: AnyRef = { - val bean = activeObjectRegistry.get(getName) + val bean = typedActorRegistry.get(getName) if (bean eq null) super.getBean else bean } } /** - * Provides active object meta information. + * Provides typed actor meta information. * * @author Martin Krasser */ -class ActiveObjectInfo(context: CamelContext, clazz: Class[_], strategy: ParameterMappingStrategy) +class TypedActorInfo(context: CamelContext, clazz: Class[_], strategy: ParameterMappingStrategy) extends BeanInfo(context, clazz, strategy) { /** diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoBase.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoBase.java deleted file mode 100644 index 05bf1625bb..0000000000 --- a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoBase.java +++ /dev/null @@ -1,34 +0,0 @@ -package se.scalablesolutions.akka.camel; - -import org.apache.camel.Body; -import org.apache.camel.Header; - -import se.scalablesolutions.akka.actor.annotation.consume; - -/** - * @author Martin Krasser - */ -public class PojoBase { - - public String m1(String b, String h) { - return "m1base: " + b + " " + h; - } - - @consume("direct:m2base") - public String m2(@Body String b, @Header("test") String h) { - return "m2base: " + b + " " + h; - } - - @consume("direct:m3base") - public String m3(@Body String b, @Header("test") String h) { - return "m3base: " + b + " " + h; - } - - @consume("direct:m4base") - public String m4(@Body String b, @Header("test") String h) { - return "m4base: " + b + " " + h; - } - - public void m5(@Body String b, @Header("test") String h) { - } -} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoImpl.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoImpl.java deleted file mode 100644 index b48202d4dc..0000000000 --- a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoImpl.java +++ /dev/null @@ -1,23 +0,0 @@ -package se.scalablesolutions.akka.camel; - -import org.apache.camel.Body; -import org.apache.camel.Header; - -import se.scalablesolutions.akka.actor.annotation.consume; - -/** - * @author Martin Krasser - */ -public class PojoImpl implements PojoIntf { - - public String m1(String b, String h) { - return "m1impl: " + b + " " + h; - } - - @consume("direct:m2impl") - public String m2(@Body String b, @Header("test") String h) { - return "m2impl: " + b + " " + h; - } - - -} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoIntf.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoIntf.java deleted file mode 100644 index 14f63afd2e..0000000000 --- a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoIntf.java +++ /dev/null @@ -1,18 +0,0 @@ -package se.scalablesolutions.akka.camel; - -import org.apache.camel.Body; -import org.apache.camel.Header; - -import se.scalablesolutions.akka.actor.annotation.consume; - -/** - * @author Martin Krasser - */ -public interface PojoIntf { - - public String m1(String b, String h); - - @consume("direct:m2intf") - public String m2(@Body String b, @Header("test") String h); - -} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoRemote.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoRemote.java deleted file mode 100644 index 57b0999b8f..0000000000 --- a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoRemote.java +++ /dev/null @@ -1,15 +0,0 @@ -package se.scalablesolutions.akka.camel; - -import se.scalablesolutions.akka.actor.annotation.consume; - -/** - * @author Martin Krasser - */ -public class PojoRemote { - - @consume("direct:remote-active-object") - public String foo(String s) { - return String.format("remote active object: %s", s); - } - -} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoSingle.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoSingle.java deleted file mode 100644 index 7d577535b2..0000000000 --- a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoSingle.java +++ /dev/null @@ -1,14 +0,0 @@ -package se.scalablesolutions.akka.camel; - -import se.scalablesolutions.akka.actor.annotation.consume; - -/** - * @author Martin Krasser - */ -public class PojoSingle { - - @consume("direct:foo") - public void foo(String b) { - } - -} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoSub.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoSub.java deleted file mode 100644 index be5b453698..0000000000 --- a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/PojoSub.java +++ /dev/null @@ -1,27 +0,0 @@ -package se.scalablesolutions.akka.camel; - -import org.apache.camel.Body; -import org.apache.camel.Header; - -import se.scalablesolutions.akka.actor.annotation.consume; - -public class PojoSub extends PojoBase { - - @Override - @consume("direct:m1sub") - public String m1(@Body String b, @Header("test") String h) { - return "m1sub: " + b + " " + h; - } - - @Override - public String m2(String b, String h) { - return "m2sub: " + b + " " + h; - } - - @Override - @consume("direct:m3sub") - public String m3(@Body String b, @Header("test") String h) { - return "m3sub: " + b + " " + h; - } - -} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleRemoteTypedConsumer.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleRemoteTypedConsumer.java new file mode 100644 index 0000000000..5fd39f07d9 --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleRemoteTypedConsumer.java @@ -0,0 +1,12 @@ +package se.scalablesolutions.akka.camel; + +import se.scalablesolutions.akka.camel.consume; + +/** + * @author Martin Krasser + */ +public interface SampleRemoteTypedConsumer { + + @consume("direct:remote-typed-consumer") + public String foo(String s); +} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleRemoteTypedConsumerImpl.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleRemoteTypedConsumerImpl.java new file mode 100644 index 0000000000..f6b0076e73 --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleRemoteTypedConsumerImpl.java @@ -0,0 +1,14 @@ +package se.scalablesolutions.akka.camel; + +import se.scalablesolutions.akka.actor.TypedActor; + +/** + * @author Martin Krasser + */ +public class SampleRemoteTypedConsumerImpl extends TypedActor implements SampleRemoteTypedConsumer { + + public String foo(String s) { + return String.format("remote typed actor: %s", s); + } + +} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleRemoteUntypedConsumer.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleRemoteUntypedConsumer.java new file mode 100644 index 0000000000..c35bd92b71 --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleRemoteUntypedConsumer.java @@ -0,0 +1,29 @@ +package se.scalablesolutions.akka.camel; + +import se.scalablesolutions.akka.camel.RemoteUntypedConsumerActor; + +/** + * @author Martin Krasser + */ +public class SampleRemoteUntypedConsumer extends RemoteUntypedConsumerActor { + + public SampleRemoteUntypedConsumer() { + this("localhost", 7774); + } + + public SampleRemoteUntypedConsumer(String host, int port) { + super(host, port); + } + + public String getEndpointUri() { + return "direct:remote-untyped-consumer"; + } + + public void onReceive(Object message) { + Message msg = (Message)message; + String body = msg.bodyAs(String.class); + String header = msg.headerAs("test", String.class); + getContext().replySafe(String.format("%s %s", body, header)); + } + +} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedActor.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedActor.java new file mode 100644 index 0000000000..d4d7f152bf --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedActor.java @@ -0,0 +1,9 @@ +package se.scalablesolutions.akka.camel; + +/** + * @author Martin Krasser + */ +public interface SampleTypedActor { + + public String foo(String s); +} \ No newline at end of file diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/Pojo.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedActorImpl.java similarity index 53% rename from akka-camel/src/test/java/se/scalablesolutions/akka/camel/Pojo.java rename to akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedActorImpl.java index d1848c49ee..dc91fc3f7f 100644 --- a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/Pojo.java +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedActorImpl.java @@ -1,14 +1,14 @@ package se.scalablesolutions.akka.camel; -import se.scalablesolutions.akka.actor.annotation.consume; +import se.scalablesolutions.akka.actor.TypedActor; /** * @author Martin Krasser */ -public class Pojo { +public class SampleTypedActorImpl extends TypedActor implements SampleTypedActor { public String foo(String s) { return String.format("foo: %s", s); } - + } \ No newline at end of file diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedConsumer.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedConsumer.java new file mode 100644 index 0000000000..a6a695f8d0 --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedConsumer.java @@ -0,0 +1,20 @@ +package se.scalablesolutions.akka.camel; + +import org.apache.camel.Body; +import org.apache.camel.Header; + +import se.scalablesolutions.akka.camel.consume; + +/** + * @author Martin Krasser + */ +public interface SampleTypedConsumer { + + public String m1(String b, String h); + public String m2(@Body String b, @Header("test") String h); + public String m3(@Body String b, @Header("test") String h); + + @consume("direct:m4") + public String m4(@Body String b, @Header("test") String h); + public void m5(@Body String b, @Header("test") String h); +} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedConsumerImpl.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedConsumerImpl.java new file mode 100644 index 0000000000..4fa00f2da0 --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedConsumerImpl.java @@ -0,0 +1,30 @@ +package se.scalablesolutions.akka.camel; + +import se.scalablesolutions.akka.actor.TypedActor; + +/** + * @author Martin Krasser + */ +public class SampleTypedConsumerImpl extends TypedActor implements SampleTypedConsumer { + + public String m1(String b, String h) { + return "m1: " + b + " " + h; + } + + @consume("direct:m2") + public String m2(String b, String h) { + return "m2: " + b + " " + h; + } + + @consume("direct:m3") + public String m3(String b, String h) { + return "m3: " + b + " " + h; + } + + public String m4(String b, String h) { + return "m4: " + b + " " + h; + } + + public void m5(String b, String h) { + } +} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedSingleConsumer.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedSingleConsumer.java new file mode 100644 index 0000000000..5d31a35ae2 --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedSingleConsumer.java @@ -0,0 +1,13 @@ +package se.scalablesolutions.akka.camel; + +import se.scalablesolutions.akka.camel.consume; + +/** + * @author Martin Krasser + */ +public interface SampleTypedSingleConsumer { + + @consume("direct:foo") + public void foo(String b); + +} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedSingleConsumerImpl.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedSingleConsumerImpl.java new file mode 100644 index 0000000000..608a74d5e9 --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleTypedSingleConsumerImpl.java @@ -0,0 +1,13 @@ +package se.scalablesolutions.akka.camel; + +import se.scalablesolutions.akka.actor.TypedActor; + +/** + * @author Martin Krasser + */ +public class SampleTypedSingleConsumerImpl extends TypedActor implements SampleTypedSingleConsumer { + + public void foo(String b) { + } + +} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedActor.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedActor.java new file mode 100644 index 0000000000..d8cb1dd1b9 --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedActor.java @@ -0,0 +1,11 @@ +package se.scalablesolutions.akka.camel; + +import se.scalablesolutions.akka.actor.UntypedActor; + +/** + * @author Martin Krasser + */ +public class SampleUntypedActor extends UntypedActor { + public void onReceive(Object message) { + } +} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedConsumer.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedConsumer.java new file mode 100644 index 0000000000..303b4302f3 --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedConsumer.java @@ -0,0 +1,21 @@ +package se.scalablesolutions.akka.camel; + +import se.scalablesolutions.akka.camel.UntypedConsumerActor; + +/** + * @author Martin Krasser + */ +public class SampleUntypedConsumer extends UntypedConsumerActor { + + public String getEndpointUri() { + return "direct:test-untyped-consumer"; + } + + public void onReceive(Object message) { + Message msg = (Message)message; + String body = msg.bodyAs(String.class); + String header = msg.headerAs("test", String.class); + getContext().replySafe(String.format("%s %s", body, header)); + } + +} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedConsumerBlocking.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedConsumerBlocking.java new file mode 100644 index 0000000000..c653d421bc --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedConsumerBlocking.java @@ -0,0 +1,23 @@ +package se.scalablesolutions.akka.camel; + +/** + * @author Martin Krasser + */ +public class SampleUntypedConsumerBlocking extends UntypedConsumerActor { + + public String getEndpointUri() { + return "direct:test-untyped-consumer-blocking"; + } + + public boolean isBlocking() { + return true; + } + + public void onReceive(Object message) { + Message msg = (Message)message; + String body = msg.bodyAs(String.class); + String header = msg.headerAs("test", String.class); + getContext().replySafe(String.format("%s %s", body, header)); + } + +} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedForwardingProducer.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedForwardingProducer.java new file mode 100644 index 0000000000..e909947de8 --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedForwardingProducer.java @@ -0,0 +1,18 @@ +package se.scalablesolutions.akka.camel; + +/** + * @author Martin Krasser + */ +public class SampleUntypedForwardingProducer extends UntypedProducerActor { + + public String getEndpointUri() { + return "direct:producer-test-1"; + } + + @Override + public void onReceiveAfterProduce(Object message) { + Message msg = (Message)message; + String body = msg.bodyAs(String.class); + CamelContextManager.template().sendBody("direct:forward-test-1", body); + } +} diff --git a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedReplyingProducer.java b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedReplyingProducer.java new file mode 100644 index 0000000000..cc3fbf110d --- /dev/null +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedReplyingProducer.java @@ -0,0 +1,12 @@ +package se.scalablesolutions.akka.camel; + +/** + * @author Martin Krasser + */ +public class SampleUntypedReplyingProducer extends UntypedProducerActor { + + public String getEndpointUri() { + return "direct:producer-test-1"; + } + +} diff --git a/akka-camel/src/test/scala/CamelServiceFeatureTest.scala b/akka-camel/src/test/scala/CamelServiceFeatureTest.scala deleted file mode 100644 index df42237a61..0000000000 --- a/akka-camel/src/test/scala/CamelServiceFeatureTest.scala +++ /dev/null @@ -1,203 +0,0 @@ -package se.scalablesolutions.akka.camel - -import java.util.concurrent.{TimeoutException, CountDownLatch, TimeUnit} - -import org.apache.camel.CamelExecutionException -import org.apache.camel.builder.RouteBuilder -import org.scalatest.{GivenWhenThen, BeforeAndAfterAll, FeatureSpec} - -import se.scalablesolutions.akka.actor.Actor._ -import se.scalablesolutions.akka.actor.{ActiveObject, Actor, ActorRegistry} - -class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with GivenWhenThen { - import CamelServiceFeatureTest._ - - var service: CamelService = _ - - override protected def beforeAll = { - ActorRegistry.shutdownAll - // create new CamelService instance - service = CamelService.newInstance - // register test consumer before starting the CamelService - actorOf(new TestConsumer("direct:publish-test-1")).start - // Configure a custom camel route - CamelContextManager.init - CamelContextManager.context.addRoutes(new TestRoute) - // start consumer publisher, otherwise we cannot set message - // count expectations in the next step (needed for testing only). - service.consumerPublisher.start - // set expectations on publish count - val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get - // start the CamelService - service.load - // await publication of first test consumer - assert(latch.await(5000, TimeUnit.MILLISECONDS)) - } - - override protected def afterAll = { - service.unload - ActorRegistry.shutdownAll - } - - feature("Publish registered consumer actors in the global CamelContext") { - - scenario("access non-blocking consumer actors via Camel direct-endpoints") { - - given("two consumer actors registered before and after CamelService startup") - val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get - actorOf(new TestConsumer("direct:publish-test-2")).start - assert(latch.await(5000, TimeUnit.MILLISECONDS)) - - when("requests are sent to these actors") - val response1 = CamelContextManager.template.requestBody("direct:publish-test-1", "msg1") - val response2 = CamelContextManager.template.requestBody("direct:publish-test-2", "msg2") - - then("both actors should have replied with expected responses") - assert(response1 === "received msg1") - assert(response2 === "received msg2") - } - - scenario("access blocking, non-responding consumer actor via a Camel direct-endpoint") { - - given("a consumer actor registered after CamelService startup") - val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get - actorOf(new TestBlocker("direct:publish-test-3")).start - assert(latch.await(5000, TimeUnit.MILLISECONDS)) - - try { - when("a request is sent to this actor") - CamelContextManager.template.requestBody("direct:publish-test-3", "msg3") - fail("expected TimoutException not thrown") - } catch { - case e => { - then("a TimoutException should be thrown") - assert(e.getCause.isInstanceOf[TimeoutException]) - } - } - } - } - - feature("Unpublish registered consumer actor from the global CamelContext") { - - scenario("access to unregistered consumer actor via Camel direct-endpoint fails") { - val endpointUri = "direct:unpublish-test-1" - - given("a consumer actor registered after CamelService startup") - assert(CamelContextManager.context.hasEndpoint(endpointUri) eq null) - var latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get - val consumer = actorOf(new TestConsumer(endpointUri)).start - assert(latch.await(5000, TimeUnit.MILLISECONDS)) - assert(CamelContextManager.context.hasEndpoint(endpointUri) ne null) - - when("the actor is stopped") - latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get - consumer.stop - assert(latch.await(5000, TimeUnit.MILLISECONDS)) - - then("the associated endpoint isn't accessible any more") - intercept[CamelExecutionException] { - CamelContextManager.template.requestBody(endpointUri, "msg1") - } - } - } - - feature("Configure a custom Camel route for the global CamelContext") { - - scenario("access an actor from the custom Camel route") { - - given("a registered actor and a custom route to that actor") - val actor = actorOf[TestActor].start - - when("sending a a message to that route") - val response = CamelContextManager.template.requestBody("direct:custom-route-test-1", "msg3") - - then("an expected response generated by the actor should be returned") - assert(response === "received msg3") - } - } - - feature("Publish active object methods in the global CamelContext") { - - scenario("access active object methods via Camel direct-endpoints") { - - given("an active object registered after CamelService startup") - var latch = (service.consumerPublisher !! SetExpectedMessageCount(3)).as[CountDownLatch].get - val obj = ActiveObject.newInstance(classOf[PojoBase]) - assert(latch.await(5000, TimeUnit.MILLISECONDS)) - - when("requests are sent to published methods") - val response1 = CamelContextManager.template.requestBodyAndHeader("direct:m2base", "x", "test", "y") - val response2 = CamelContextManager.template.requestBodyAndHeader("direct:m3base", "x", "test", "y") - val response3 = CamelContextManager.template.requestBodyAndHeader("direct:m4base", "x", "test", "y") - - then("each should have returned a different response") - assert(response1 === "m2base: x y") - assert(response2 === "m3base: x y") - assert(response3 === "m4base: x y") - - // cleanup to avoid conflicts with next test (i.e. avoid multiple consumers on direct-endpoints) - latch = (service.consumerPublisher !! SetExpectedMessageCount(3)).as[CountDownLatch].get - ActiveObject.stop(obj) - assert(latch.await(5000, TimeUnit.MILLISECONDS)) - } - } - - feature("Unpublish active object method from the global CamelContext") { - - scenario("access to unregistered active object methof via Camel direct-endpoint fails") { - - given("an active object registered after CamelService startup") - var latch = (service.consumerPublisher !! SetExpectedMessageCount(3)).as[CountDownLatch].get - val obj = ActiveObject.newInstance(classOf[PojoBase]) - assert(latch.await(5000, TimeUnit.MILLISECONDS)) - - when("the active object is stopped") - latch = (service.consumerPublisher !! SetExpectedMessageCount(3)).as[CountDownLatch].get - ActiveObject.stop(obj) - assert(latch.await(5000, TimeUnit.MILLISECONDS)) - - then("the associated endpoints aren't accessible any more") - intercept[CamelExecutionException] { - CamelContextManager.template.requestBodyAndHeader("direct:m2base", "x", "test", "y") - } - intercept[CamelExecutionException] { - CamelContextManager.template.requestBodyAndHeader("direct:m3base", "x", "test", "y") - } - intercept[CamelExecutionException] { - CamelContextManager.template.requestBodyAndHeader("direct:m4base", "x", "test", "y") - } - } - } -} - -object CamelServiceFeatureTest { - - class TestConsumer(uri: String) extends Actor with Consumer { - def endpointUri = uri - protected def receive = { - case msg: Message => self.reply("received %s" format msg.body) - } - } - - class TestBlocker(uri: String) extends Actor with Consumer { - self.timeout = 1 - def endpointUri = uri - override def blocking = true - protected def receive = { - case msg: Message => { /* do not reply */ } - } - } - - class TestActor extends Actor { - self.id = "custom-actor-id" - protected def receive = { - case msg: Message => self.reply("received %s" format msg.body) - } - } - - class TestRoute extends RouteBuilder { - def configure { - from("direct:custom-route-test-1").to("actor:custom-actor-id") - } - } -} diff --git a/akka-camel/src/test/scala/CamelServiceManagerSpec.scala b/akka-camel/src/test/scala/CamelServiceManagerSpec.scala new file mode 100644 index 0000000000..9de84f1c27 --- /dev/null +++ b/akka-camel/src/test/scala/CamelServiceManagerSpec.scala @@ -0,0 +1,63 @@ +package se.scalablesolutions.akka.camel + +import org.scalatest.{BeforeAndAfterAll, WordSpec} +import org.scalatest.matchers.MustMatchers + +import se.scalablesolutions.akka.actor.ActorRegistry + +/** + * @author Martin Krasser + */ +class CamelServiceManagerSpec extends WordSpec with BeforeAndAfterAll with MustMatchers { + + override def afterAll = ActorRegistry.shutdownAll + + "A CamelServiceManager" when { + "the startCamelService method been has been called" must { + "have registered the started CamelService instance" in { + val service = CamelServiceManager.startCamelService + CamelServiceManager.service must be theSameInstanceAs (service) + } + } + "the stopCamelService method been has been called" must { + "have unregistered the current CamelService instance" in { + val service = CamelServiceManager.stopCamelService + intercept[IllegalStateException] { CamelServiceManager.service } + } + } + } + + "A CamelServiceManager" when { + val service = CamelServiceFactory.createCamelService + "a CamelService instance has been started externally" must { + "have registered the started CamelService instance" in { + service.start + CamelServiceManager.service must be theSameInstanceAs (service) + } + } + "the current CamelService instance has been stopped externally" must { + "have unregistered the current CamelService instance" in { + service.stop + intercept[IllegalStateException] { CamelServiceManager.service } + } + } + } + + "A CamelServiceManager" when { + "a CamelService has been started" must { + "not allow further CamelService instances to be started" in { + CamelServiceManager.startCamelService + intercept[IllegalStateException] { CamelServiceManager.startCamelService } + } + } + "a CamelService has been stopped" must { + "only allow the current CamelService instance to be stopped" in { + intercept[IllegalStateException] { CamelServiceFactory.createCamelService.stop } + } + "ensure that the current CamelService instance has been actually started" in { + CamelServiceManager.stopCamelService + intercept[IllegalStateException] { CamelServiceManager.stopCamelService } + } + } + } +} \ No newline at end of file diff --git a/akka-camel/src/test/scala/ConsumerMethodRegisteredTest.scala b/akka-camel/src/test/scala/ConsumerMethodRegisteredTest.scala deleted file mode 100644 index 7c28c7d8ee..0000000000 --- a/akka-camel/src/test/scala/ConsumerMethodRegisteredTest.scala +++ /dev/null @@ -1,57 +0,0 @@ -package se.scalablesolutions.akka.camel - -import java.net.InetSocketAddress - -import org.scalatest.junit.JUnitSuite - -import se.scalablesolutions.akka.actor.{AspectInit, ActiveObject} -import se.scalablesolutions.akka.camel.ConsumerMethodRegistered._ -import org.junit.{AfterClass, Test} - -class ConsumerMethodRegisteredTest extends JUnitSuite { - import ConsumerMethodRegisteredTest._ - - val remoteAddress = new InetSocketAddress("localhost", 8888); - val remoteAspectInit = AspectInit(classOf[String], null, Some(remoteAddress), 1000) - val localAspectInit = AspectInit(classOf[String], null, None, 1000) - - val ascendingMethodName = (r1: ConsumerMethodRegistered, r2: ConsumerMethodRegistered) => - r1.method.getName < r2.method.getName - - @Test def shouldSelectPojoBaseMethods234 = { - val registered = forConsumer(activePojoBase, localAspectInit).sortWith(ascendingMethodName) - assert(registered.size === 3) - assert(registered.map(_.method.getName) === List("m2", "m3", "m4")) - } - - @Test def shouldSelectPojoSubMethods134 = { - val registered = forConsumer(activePojoSub, localAspectInit).sortWith(ascendingMethodName) - assert(registered.size === 3) - assert(registered.map(_.method.getName) === List("m1", "m3", "m4")) - } - - @Test def shouldSelectPojoIntfMethod2 = { - val registered = forConsumer(activePojoIntf, localAspectInit) - assert(registered.size === 1) - assert(registered(0).method.getName === "m2") - } - - @Test def shouldIgnoreRemoteProxies = { - val registered = forConsumer(activePojoBase, remoteAspectInit) - assert(registered.size === 0) - } - -} - -object ConsumerMethodRegisteredTest { - val activePojoBase = ActiveObject.newInstance(classOf[PojoBase]) - val activePojoSub = ActiveObject.newInstance(classOf[PojoSub]) - val activePojoIntf = ActiveObject.newInstance(classOf[PojoIntf], new PojoImpl) - - @AfterClass - def afterClass = { - ActiveObject.stop(activePojoBase) - ActiveObject.stop(activePojoSub) - ActiveObject.stop(activePojoIntf) - } -} diff --git a/akka-camel/src/test/scala/ConsumerRegisteredTest.scala b/akka-camel/src/test/scala/ConsumerRegisteredTest.scala index 3339caacf2..787142d50a 100644 --- a/akka-camel/src/test/scala/ConsumerRegisteredTest.scala +++ b/akka-camel/src/test/scala/ConsumerRegisteredTest.scala @@ -3,8 +3,46 @@ package se.scalablesolutions.akka.camel import org.junit.Test import org.scalatest.junit.JUnitSuite -import se.scalablesolutions.akka.actor.Actor -import se.scalablesolutions.akka.actor.Actor._ +import se.scalablesolutions.akka.actor.{Actor, UntypedActor} + +class ConsumerRegisteredTest extends JUnitSuite { + import ConsumerRegisteredTest._ + + @Test def shouldCreateSomeNonBlockingPublishRequestFromConsumer = { + val c = Actor.actorOf[ConsumerActor1] + val event = ConsumerRegistered.forConsumer(c) + assert(event === Some(ConsumerRegistered(c, "mock:test1", c.uuid, false))) + } + + @Test def shouldCreateSomeBlockingPublishRequestFromConsumer = { + val c = Actor.actorOf[ConsumerActor2] + val event = ConsumerRegistered.forConsumer(c) + assert(event === Some(ConsumerRegistered(c, "mock:test2", c.uuid, true))) + } + + @Test def shouldCreateNoneFromConsumer = { + val event = ConsumerRegistered.forConsumer(Actor.actorOf[PlainActor]) + assert(event === None) + } + + @Test def shouldCreateSomeNonBlockingPublishRequestFromUntypedConsumer = { + val uc = UntypedActor.actorOf(classOf[SampleUntypedConsumer]) + val event = ConsumerRegistered.forConsumer(uc) + assert(event === Some(ConsumerRegistered(uc, "direct:test-untyped-consumer", uc.uuid, false))) + } + + @Test def shouldCreateSomeBlockingPublishRequestFromUntypedConsumer = { + val uc = UntypedActor.actorOf(classOf[SampleUntypedConsumerBlocking]) + val event = ConsumerRegistered.forConsumer(uc) + assert(event === Some(ConsumerRegistered(uc, "direct:test-untyped-consumer-blocking", uc.uuid, true))) + } + + @Test def shouldCreateNoneFromUntypedConsumer = { + val a = UntypedActor.actorOf(classOf[SampleUntypedActor]) + val event = ConsumerRegistered.forConsumer(a) + assert(event === None) + } +} object ConsumerRegisteredTest { class ConsumerActor1 extends Actor with Consumer { @@ -22,24 +60,3 @@ object ConsumerRegisteredTest { protected def receive = null } } - -class ConsumerRegisteredTest extends JUnitSuite { - import ConsumerRegisteredTest._ - - @Test def shouldCreateSomeNonBlockingPublishRequest = { - val ca = actorOf[ConsumerActor1] - val event = ConsumerRegistered.forConsumer(ca) - assert(event === Some(ConsumerRegistered(ca, "mock:test1", ca.uuid, false))) - } - - @Test def shouldCreateSomeBlockingPublishRequest = { - val ca = actorOf[ConsumerActor2] - val event = ConsumerRegistered.forConsumer(ca) - assert(event === Some(ConsumerRegistered(ca, "mock:test2", ca.uuid, true))) - } - - @Test def shouldCreateNone = { - val event = ConsumerRegistered.forConsumer(actorOf[PlainActor]) - assert(event === None) - } -} diff --git a/akka-camel/src/test/scala/ConsumerSpec.scala b/akka-camel/src/test/scala/ConsumerSpec.scala new file mode 100644 index 0000000000..14f5e1cf40 --- /dev/null +++ b/akka-camel/src/test/scala/ConsumerSpec.scala @@ -0,0 +1,205 @@ +package se.scalablesolutions.akka.camel + +import java.util.concurrent.{TimeoutException, CountDownLatch, TimeUnit} + +import org.apache.camel.CamelExecutionException +import org.apache.camel.builder.RouteBuilder +import org.scalatest.{BeforeAndAfterAll, WordSpec} +import org.scalatest.matchers.MustMatchers + +import se.scalablesolutions.akka.actor.Actor._ +import se.scalablesolutions.akka.actor._ + +/** + * @author Martin Krasser + */ +class ConsumerSpec extends WordSpec with BeforeAndAfterAll with MustMatchers { + import CamelContextManager.template + import ConsumerSpec._ + + var service: CamelService = _ + + override protected def beforeAll = { + ActorRegistry.shutdownAll + // create new CamelService instance + service = CamelServiceFactory.createCamelService + // register test consumer before starting the CamelService + actorOf(new TestConsumer("direct:publish-test-1")).start + // start consumer publisher, otherwise we cannot set message + // count expectations in the next step (needed for testing only). + service.consumerPublisher.start + // set expectations on publish count + val latch = service.expectEndpointActivationCount(1) + // start the CamelService + service.start + // await publication of first test consumer + latch.await(5000, TimeUnit.MILLISECONDS) must be (true) + } + + override protected def afterAll = { + service.stop + ActorRegistry.shutdownAll + } + + "A responding consumer" when { + val consumer = actorOf(new TestConsumer("direct:publish-test-2")) + "started before starting the CamelService" must { + "support an in-out message exchange via its endpoint" in { + template.requestBody("direct:publish-test-1", "msg1") must equal ("received msg1") + } + } + "not started" must { + "not have an associated endpoint in the CamelContext" in { + CamelContextManager.context.hasEndpoint("direct:publish-test-2") must be (null) + } + } + "started" must { + "support an in-out message exchange via its endpoint" in { + val latch = service.expectEndpointActivationCount(1) + consumer.start + latch.await(5000, TimeUnit.MILLISECONDS) must be (true) + template.requestBody("direct:publish-test-2", "msg2") must equal ("received msg2") + } + "have an associated endpoint in the CamelContext" in { + CamelContextManager.context.hasEndpoint("direct:publish-test-2") must not be (null) + } + } + "stopped" must { + "not support an in-out message exchange via its endpoint" in { + val latch = service.expectEndpointDeactivationCount(1) + consumer.stop + latch.await(5000, TimeUnit.MILLISECONDS) must be (true) + intercept[CamelExecutionException] { + template.requestBody("direct:publish-test-2", "msg2") + } + } + } + } + + "A responding, typed consumer" when { + var actor: SampleTypedConsumer = null + "started" must { + "support in-out message exchanges via its endpoints" in { + val latch = service.expectEndpointActivationCount(3) + actor = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl]) + latch.await(5000, TimeUnit.MILLISECONDS) must be (true) + template.requestBodyAndHeader("direct:m2", "x", "test", "y") must equal ("m2: x y") + template.requestBodyAndHeader("direct:m3", "x", "test", "y") must equal ("m3: x y") + template.requestBodyAndHeader("direct:m4", "x", "test", "y") must equal ("m4: x y") + } + } + "stopped" must { + "not support in-out message exchanges via its endpoints" in { + val latch = service.expectEndpointDeactivationCount(3) + TypedActor.stop(actor) + latch.await(5000, TimeUnit.MILLISECONDS) must be (true) + intercept[CamelExecutionException] { + template.requestBodyAndHeader("direct:m2", "x", "test", "y") + } + intercept[CamelExecutionException] { + template.requestBodyAndHeader("direct:m3", "x", "test", "y") + } + intercept[CamelExecutionException] { + template.requestBodyAndHeader("direct:m4", "x", "test", "y") + } + } + } + } + + "A responding, typed consumer (Scala)" when { + var actor: TestTypedConsumer = null + "started" must { + "support in-out message exchanges via its endpoints" in { + val latch = service.expectEndpointActivationCount(2) + actor = TypedActor.newInstance(classOf[TestTypedConsumer], classOf[TestTypedConsumerImpl]) + latch.await(5000, TimeUnit.MILLISECONDS) must be (true) + template.requestBody("direct:publish-test-3", "x") must equal ("foo: x") + template.requestBody("direct:publish-test-4", "x") must equal ("bar: x") + } + } + "stopped" must { + "not support in-out message exchanges via its endpoints" in { + val latch = service.expectEndpointDeactivationCount(2) + TypedActor.stop(actor) + latch.await(5000, TimeUnit.MILLISECONDS) must be (true) + intercept[CamelExecutionException] { + template.requestBody("direct:publish-test-3", "x") + } + intercept[CamelExecutionException] { + template.requestBody("direct:publish-test-4", "x") + } + } + } + } + + "A responding, untyped consumer" when { + val consumer = UntypedActor.actorOf(classOf[SampleUntypedConsumer]) + "started" must { + "support an in-out message exchange via its endpoint" in { + val latch = service.expectEndpointActivationCount(1) + consumer.start + latch.await(5000, TimeUnit.MILLISECONDS) must be (true) + template.requestBodyAndHeader("direct:test-untyped-consumer", "x", "test", "y") must equal ("x y") + } + } + "stopped" must { + "not support an in-out message exchange via its endpoint" in { + val latch = service.expectEndpointDeactivationCount(1) + consumer.stop + latch.await(5000, TimeUnit.MILLISECONDS) must be (true) + intercept[CamelExecutionException] { + template.sendBodyAndHeader("direct:test-untyped-consumer", "blah", "test", "blub") + } + } + } + } + + "A non-responding, blocking consumer" when { + "receiving an in-out message exchange" must { + "lead to a TimeoutException" in { + val latch = service.expectEndpointActivationCount(1) + actorOf(new TestBlocker("direct:publish-test-5")).start + latch.await(5000, TimeUnit.MILLISECONDS) must be (true) + + try { + template.requestBody("direct:publish-test-5", "msg3") + fail("expected TimoutException not thrown") + } catch { + case e => { + assert(e.getCause.isInstanceOf[TimeoutException]) + } + } + } + } + } +} + +object ConsumerSpec { + class TestConsumer(uri: String) extends Actor with Consumer { + def endpointUri = uri + protected def receive = { + case msg: Message => self.reply("received %s" format msg.body) + } + } + + trait TestTypedConsumer { + @consume("direct:publish-test-3") + def foo(s: String): String + def bar(s: String): String + } + + class TestTypedConsumerImpl extends TypedActor with TestTypedConsumer { + def foo(s: String) = "foo: %s" format s + @consume("direct:publish-test-4") + def bar(s: String) = "bar: %s" format s + } + + class TestBlocker(uri: String) extends Actor with Consumer { + self.timeout = 1000 + def endpointUri = uri + override def blocking = true + protected def receive = { + case msg: Message => { /* do not reply */ } + } + } +} \ No newline at end of file diff --git a/akka-camel/src/test/scala/MessageTest.scala b/akka-camel/src/test/scala/MessageTest.scala index b87a99e18c..1467402b9a 100644 --- a/akka-camel/src/test/scala/MessageTest.scala +++ b/akka-camel/src/test/scala/MessageTest.scala @@ -15,6 +15,7 @@ class MessageTest extends JUnitSuite with BeforeAndAfterAll { @Test def shouldConvertDoubleBodyToString = { assertEquals("1.4", Message(1.4, null).bodyAs[String]) + assertEquals("1.4", Message(1.4, null).bodyAs(classOf[String])) } @Test def shouldThrowExceptionWhenConvertingDoubleBodyToInputStream { @@ -23,6 +24,17 @@ class MessageTest extends JUnitSuite with BeforeAndAfterAll { } } + @Test def shouldReturnDoubleHeader = { + val message = Message("test" , Map("test" -> 1.4)) + assertEquals(1.4, message.header("test")) + } + + @Test def shouldConvertDoubleHeaderToString = { + val message = Message("test" , Map("test" -> 1.4)) + assertEquals("1.4", message.headerAs[String]("test")) + assertEquals("1.4", message.headerAs("test", classOf[String])) + } + @Test def shouldReturnSubsetOfHeaders = { val message = Message("test" , Map("A" -> "1", "B" -> "2")) assertEquals(Map("B" -> "2"), message.headers(Set("B"))) diff --git a/akka-camel/src/test/scala/ProducerFeatureTest.scala b/akka-camel/src/test/scala/ProducerFeatureTest.scala index 5cf24eaaa3..a27e05a54f 100644 --- a/akka-camel/src/test/scala/ProducerFeatureTest.scala +++ b/akka-camel/src/test/scala/ProducerFeatureTest.scala @@ -27,7 +27,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before mockEndpoint.reset } - feature("Produce a message to a Camel endpoint") { + feature("Produce a message to a sync Camel route") { scenario("produce message and receive normal response") { given("a registered two-way producer") @@ -86,9 +86,9 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before } } - feature("Produce a message to an async Camel endpoint") { + feature("Produce a message to an async Camel route") { - scenario("produce message and async receive normal response") { + scenario("produce message and receive normal response") { given("a registered two-way producer") val producer = actorOf(new TestProducer("direct:producer-test-3")) producer.start @@ -102,7 +102,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before assert(result === Some(expected)) } - scenario("produce message and async receive failure response") { + scenario("produce message and receive failure response") { given("a registered two-way producer") val producer = actorOf(new TestProducer("direct:producer-test-3")) producer.start @@ -119,9 +119,9 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before } } - feature("Produce a message to a Camel endpoint and then forward the result") { + feature("Produce a message to a sync Camel route and then forward the response") { - scenario("produce message, forward and receive normal response") { + scenario("produce message, forward normal response to a replying target actor and receive response") { given("a registered two-way producer configured with a forward target") val target = actorOf[ReplyingForwardTarget].start val producer = actorOf(new TestForwarder("direct:producer-test-2", target)).start @@ -135,7 +135,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before assert(result === Some(expected)) } - scenario("produce message, forward and receive failure response") { + scenario("produce message, forward failure response to a replying target actor and receive response") { given("a registered two-way producer configured with a forward target") val target = actorOf[ReplyingForwardTarget].start val producer = actorOf(new TestForwarder("direct:producer-test-2", target)).start @@ -151,7 +151,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before assert(expectedHeaders === Map(Message.MessageExchangeId -> "123", "test" -> "failure")) } - scenario("produce message, forward and produce normal response") { + scenario("produce message, forward normal response to a producing target actor and produce response to direct:forward-test-1") { given("a registered one-way producer configured with a forward target") val target = actorOf[ProducingForwardTarget].start val producer = actorOf(new TestForwarder("direct:producer-test-2", target)).start @@ -164,7 +164,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before mockEndpoint.assertIsSatisfied } - scenario("produce message, forward and produce failure response") { + scenario("produce message, forward failure response to a producing target actor and produce response to direct:forward-test-1") { given("a registered one-way producer configured with a forward target") val target = actorOf[ProducingForwardTarget].start val producer = actorOf(new TestForwarder("direct:producer-test-2", target)).start @@ -179,9 +179,9 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before } } - feature("Produce a message to an async Camel endpoint and then forward the result") { + feature("Produce a message to an async Camel route and then forward the response") { - scenario("produce message, forward and async receive normal response") { + scenario("produce message, forward normal response to a replying target actor and receive response") { given("a registered two-way producer configured with a forward target") val target = actorOf[ReplyingForwardTarget].start val producer = actorOf(new TestForwarder("direct:producer-test-3", target)).start @@ -195,7 +195,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before assert(result === Some(expected)) } - scenario("produce message, forward and async receive failure response") { + scenario("produce message, forward failure response to a replying target actor and receive response") { given("a registered two-way producer configured with a forward target") val target = actorOf[ReplyingForwardTarget].start val producer = actorOf(new TestForwarder("direct:producer-test-3", target)).start @@ -211,7 +211,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before assert(expectedHeaders === Map(Message.MessageExchangeId -> "123", "test" -> "failure")) } - scenario("produce message, forward and async produce normal response") { + scenario("produce message, forward normal response to a producing target actor and produce response to direct:forward-test-1") { given("a registered one-way producer configured with a forward target") val target = actorOf[ProducingForwardTarget].start val producer = actorOf(new TestForwarder("direct:producer-test-3", target)).start @@ -224,7 +224,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before mockEndpoint.assertIsSatisfied } - scenario("produce message, forward and async produce failure response") { + scenario("produce message, forward failure response to a producing target actor and produce response to direct:forward-test-1") { given("a registered one-way producer configured with a forward target") val target = actorOf[ProducingForwardTarget].start val producer = actorOf(new TestForwarder("direct:producer-test-3", target)).start @@ -298,4 +298,4 @@ object ProducerFeatureTest { }) } } -} \ No newline at end of file +} diff --git a/akka-camel/src/test/scala/PublishRequestorTest.scala b/akka-camel/src/test/scala/PublishRequestorTest.scala index 131f4fe2b5..7cfced57e1 100644 --- a/akka-camel/src/test/scala/PublishRequestorTest.scala +++ b/akka-camel/src/test/scala/PublishRequestorTest.scala @@ -16,7 +16,10 @@ class PublishRequestorTest extends JUnitSuite { var requestor: ActorRef = _ var consumer: ActorRef = _ - @Before def setUp = { + val ascendingMethodName = (r1: ConsumerMethodRegistered, r2: ConsumerMethodRegistered) => + r1.method.getName < r2.method.getName + + @Before def setUp: Unit = { publisher = actorOf[PublisherMock].start requestor = actorOf[PublishRequestor].start requestor ! PublishRequestorInit(publisher) @@ -24,40 +27,58 @@ class PublishRequestorTest extends JUnitSuite { def endpointUri = "mock:test" protected def receive = null }).start - } @After def tearDown = { + AspectInitRegistry.removeListener(requestor); ActorRegistry.shutdownAll } - @Test def shouldReceiveConsumerMethodRegisteredEvent = { - val obj = ActiveObject.newInstance(classOf[PojoSingle]) - val init = AspectInit(classOf[PojoSingle], null, None, 1000) + @Test def shouldReceiveOneConsumerMethodRegisteredEvent = { + AspectInitRegistry.addListener(requestor) val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get - requestor ! AspectInitRegistered(obj, init) + val obj = TypedActor.newInstance(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl]) assert(latch.await(5000, TimeUnit.MILLISECONDS)) - val event = (publisher !! GetRetainedMessage).get.asInstanceOf[ConsumerMethodRegistered] - assert(event.init === init) + val event = (publisher !! GetRetainedMessage).as[ConsumerMethodRegistered].get assert(event.uri === "direct:foo") - assert(event.activeObject === obj) + assert(event.typedActor === obj) assert(event.method.getName === "foo") } - @Test def shouldReceiveConsumerMethodUnregisteredEvent = { - val obj = ActiveObject.newInstance(classOf[PojoSingle]) - val init = AspectInit(classOf[PojoSingle], null, None, 1000) + @Test def shouldReceiveOneConsumerMethodUnregisteredEvent = { + val obj = TypedActor.newInstance(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl]) val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get - requestor ! AspectInitUnregistered(obj, init) + AspectInitRegistry.addListener(requestor) + TypedActor.stop(obj) assert(latch.await(5000, TimeUnit.MILLISECONDS)) - val event = (publisher !! GetRetainedMessage).get.asInstanceOf[ConsumerMethodUnregistered] - assert(event.init === init) + val event = (publisher !! GetRetainedMessage).as[ConsumerMethodUnregistered].get assert(event.uri === "direct:foo") - assert(event.activeObject === obj) + assert(event.typedActor === obj) assert(event.method.getName === "foo") } - @Test def shouldReceiveConsumerRegisteredEvent = { + @Test def shouldReceiveThreeConsumerMethodRegisteredEvents = { + AspectInitRegistry.addListener(requestor) + val latch = (publisher !! SetExpectedTestMessageCount(3)).as[CountDownLatch].get + val obj = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl]) + assert(latch.await(5000, TimeUnit.MILLISECONDS)) + val request = GetRetainedMessages(_.isInstanceOf[ConsumerMethodRegistered]) + val events = (publisher !! request).as[List[ConsumerMethodRegistered]].get + assert(events.map(_.method.getName).sortWith(_ < _) === List("m2", "m3", "m4")) + } + + @Test def shouldReceiveThreeConsumerMethodUnregisteredEvents = { + val obj = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl]) + val latch = (publisher !! SetExpectedTestMessageCount(3)).as[CountDownLatch].get + AspectInitRegistry.addListener(requestor) + TypedActor.stop(obj) + assert(latch.await(5000, TimeUnit.MILLISECONDS)) + val request = GetRetainedMessages(_.isInstanceOf[ConsumerMethodUnregistered]) + val events = (publisher !! request).as[List[ConsumerMethodUnregistered]].get + assert(events.map(_.method.getName).sortWith(_ < _) === List("m2", "m3", "m4")) + } + + @Test def shouldReceiveOneConsumerRegisteredEvent = { val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get requestor ! ActorRegistered(consumer) assert(latch.await(5000, TimeUnit.MILLISECONDS)) @@ -65,7 +86,7 @@ class PublishRequestorTest extends JUnitSuite { Some(ConsumerRegistered(consumer, "mock:test", consumer.uuid, false))) } - @Test def shouldReceiveConsumerUnregisteredEvent = { + @Test def shouldReceiveOneConsumerUnregisteredEvent = { val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get requestor ! ActorUnregistered(consumer) assert(latch.await(5000, TimeUnit.MILLISECONDS)) diff --git a/akka-camel/src/test/scala/RemoteConsumerTest.scala b/akka-camel/src/test/scala/RemoteConsumerTest.scala index 7e3b666590..afba2011d5 100644 --- a/akka-camel/src/test/scala/RemoteConsumerTest.scala +++ b/akka-camel/src/test/scala/RemoteConsumerTest.scala @@ -4,24 +4,23 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} import org.scalatest.{GivenWhenThen, BeforeAndAfterAll, FeatureSpec} +import se.scalablesolutions.akka.actor._ import se.scalablesolutions.akka.actor.Actor._ -import se.scalablesolutions.akka.actor.{ActiveObject, ActorRegistry, RemoteActor} import se.scalablesolutions.akka.remote.{RemoteClient, RemoteServer} /** * @author Martin Krasser */ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWhenThen { + import CamelServiceManager._ import RemoteConsumerTest._ - var service: CamelService = _ var server: RemoteServer = _ override protected def beforeAll = { ActorRegistry.shutdownAll - service = CamelService.newInstance - service.load + startCamelService server = new RemoteServer() server.start(host, port) @@ -31,7 +30,8 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh override protected def afterAll = { server.shutdown - service.unload + + stopCamelService RemoteClient.shutdownAll ActorRegistry.shutdownAll @@ -39,35 +39,51 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh Thread.sleep(1000) } - feature("Client-initiated remote consumer actor") { - scenario("access published remote consumer actor") { - given("a client-initiated remote consumer actor") + feature("Publish consumer on remote node") { + scenario("access published remote consumer") { + given("a client-initiated remote consumer") val consumer = actorOf[RemoteConsumer].start when("remote consumer publication is triggered") - val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get + var latch = service.expectEndpointActivationCount(1) consumer !! "init" assert(latch.await(5000, TimeUnit.MILLISECONDS)) - then("the published actor is accessible via its endpoint URI") - val response = CamelContextManager.template.requestBody("direct:remote-actor", "test") + then("the published consumer is accessible via its endpoint URI") + val response = CamelContextManager.template.requestBody("direct:remote-consumer", "test") assert(response === "remote actor: test") } } - feature("Client-initiated remote consumer active object") { + feature("Publish typed consumer on remote node") { scenario("access published remote consumer method") { - given("a client-initiated remote consumer active object") - val consumer = ActiveObject.newRemoteInstance(classOf[PojoRemote], host, port) + given("a client-initiated remote typed consumer") + val consumer = TypedActor.newRemoteInstance(classOf[SampleRemoteTypedConsumer], classOf[SampleRemoteTypedConsumerImpl], host, port) - when("remote consumer publication is triggered") - val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get + when("remote typed consumer publication is triggered") + var latch = service.expectEndpointActivationCount(1) consumer.foo("init") assert(latch.await(5000, TimeUnit.MILLISECONDS)) then("the published method is accessible via its endpoint URI") - val response = CamelContextManager.template.requestBody("direct:remote-active-object", "test") - assert(response === "remote active object: test") + val response = CamelContextManager.template.requestBody("direct:remote-typed-consumer", "test") + assert(response === "remote typed actor: test") + } + } + + feature("Publish untyped consumer on remote node") { + scenario("access published remote untyped consumer") { + given("a client-initiated remote untyped consumer") + val consumer = UntypedActor.actorOf(classOf[SampleRemoteUntypedConsumer]).start + + when("remote untyped consumer publication is triggered") + var latch = service.expectEndpointActivationCount(1) + consumer.sendRequestReply(Message("init", Map("test" -> "init"))) + assert(latch.await(5000, TimeUnit.MILLISECONDS)) + + then("the published untyped consumer is accessible via its endpoint URI") + val response = CamelContextManager.template.requestBodyAndHeader("direct:remote-untyped-consumer", "a", "test", "b") + assert(response === "a b") } } } @@ -77,7 +93,7 @@ object RemoteConsumerTest { val port = 7774 class RemoteConsumer extends RemoteActor(host, port) with Consumer { - def endpointUri = "direct:remote-actor" + def endpointUri = "direct:remote-consumer" protected def receive = { case "init" => self.reply("done") diff --git a/akka-camel/src/test/scala/UntypedProducerFeatureTest.scala b/akka-camel/src/test/scala/UntypedProducerFeatureTest.scala new file mode 100644 index 0000000000..c8a0bd8542 --- /dev/null +++ b/akka-camel/src/test/scala/UntypedProducerFeatureTest.scala @@ -0,0 +1,98 @@ +package se.scalablesolutions.akka.camel + +import org.apache.camel.{Exchange, Processor} +import org.apache.camel.builder.RouteBuilder +import org.apache.camel.component.mock.MockEndpoint +import org.scalatest.{GivenWhenThen, BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec} + +import se.scalablesolutions.akka.actor.UntypedActor._ +import se.scalablesolutions.akka.actor.ActorRegistry + +class UntypedProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach with GivenWhenThen { + import UntypedProducerFeatureTest._ + + override protected def beforeAll = { + ActorRegistry.shutdownAll + CamelContextManager.init + CamelContextManager.context.addRoutes(new TestRoute) + CamelContextManager.start + } + + override protected def afterAll = { + CamelContextManager.stop + ActorRegistry.shutdownAll + } + + override protected def afterEach = { + mockEndpoint.reset + } + + feature("Produce a message to a sync Camel route") { + + scenario("produce message and receive normal response") { + given("a registered two-way producer") + val producer = actorOf(classOf[SampleUntypedReplyingProducer]) + producer.start + + when("a test message is sent to the producer with !!") + val message = Message("test", Map(Message.MessageExchangeId -> "123")) + val result = producer.sendRequestReply(message) + + then("a normal response should have been returned by the producer") + val expected = Message("received test", Map(Message.MessageExchangeId -> "123")) + assert(result === expected) + } + + scenario("produce message and receive failure response") { + given("a registered two-way producer") + val producer = actorOf(classOf[SampleUntypedReplyingProducer]) + producer.start + + when("a test message causing an exception is sent to the producer with !!") + val message = Message("fail", Map(Message.MessageExchangeId -> "123")) + val result = producer.sendRequestReply(message).asInstanceOf[Failure] + + then("a failure response should have been returned by the producer") + val expectedFailureText = result.cause.getMessage + val expectedHeaders = result.headers + assert(expectedFailureText === "failure") + assert(expectedHeaders === Map(Message.MessageExchangeId -> "123")) + } + + } + + feature("Produce a message to a sync Camel route and then forward the response") { + + scenario("produce message and send normal response to direct:forward-test-1") { + given("a registered one-way producer configured with a forward target") + val producer = actorOf(classOf[SampleUntypedForwardingProducer]) + producer.start + + when("a test message is sent to the producer with !") + mockEndpoint.expectedBodiesReceived("received test") + val result = producer.sendOneWay(Message("test"), producer) + + then("a normal response should have been sent") + mockEndpoint.assertIsSatisfied + } + + } + + private def mockEndpoint = CamelContextManager.context.getEndpoint("mock:mock", classOf[MockEndpoint]) +} + +object UntypedProducerFeatureTest { + class TestRoute extends RouteBuilder { + def configure { + from("direct:forward-test-1").to("mock:mock") + from("direct:producer-test-1").process(new Processor() { + def process(exchange: Exchange) = { + exchange.getIn.getBody match { + case "fail" => throw new Exception("failure") + case body => exchange.getOut.setBody("received %s" format body) + } + } + }) + } + } +} diff --git a/akka-camel/src/test/scala/component/ActiveObjectComponentFeatureTest.scala b/akka-camel/src/test/scala/component/ActiveObjectComponentFeatureTest.scala deleted file mode 100644 index d80eedfd7a..0000000000 --- a/akka-camel/src/test/scala/component/ActiveObjectComponentFeatureTest.scala +++ /dev/null @@ -1,105 +0,0 @@ -package se.scalablesolutions.akka.camel.component - -import org.scalatest.{BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec} - -import org.apache.camel.builder.RouteBuilder -import se.scalablesolutions.akka.actor.Actor._ -import se.scalablesolutions.akka.actor.{ActorRegistry, ActiveObject} -import se.scalablesolutions.akka.camel._ -import org.apache.camel.impl.{DefaultCamelContext, SimpleRegistry} -import org.apache.camel.{ResolveEndpointFailedException, ExchangePattern, Exchange, Processor} - -/** - * @author Martin Krasser - */ -class ActiveObjectComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach { - import ActiveObjectComponentFeatureTest._ - import CamelContextManager.template - - override protected def beforeAll = { - val activePojo = ActiveObject.newInstance(classOf[Pojo]) // not a consumer - val activePojoBase = ActiveObject.newInstance(classOf[PojoBase]) - val activePojoIntf = ActiveObject.newInstance(classOf[PojoIntf], new PojoImpl) - - val registry = new SimpleRegistry - registry.put("pojo", activePojo) - - CamelContextManager.init(new DefaultCamelContext(registry)) - CamelContextManager.context.addRoutes(new CustomRouteBuilder) - CamelContextManager.start - - CamelContextManager.activeObjectRegistry.put("base", activePojoBase) - CamelContextManager.activeObjectRegistry.put("intf", activePojoIntf) - } - - override protected def afterAll = { - CamelContextManager.stop - ActorRegistry.shutdownAll - } - - feature("Communicate with an active object from a Camel application using active object endpoint URIs") { - import ActiveObjectComponent.InternalSchema - import ExchangePattern._ - - scenario("in-out exchange with proxy created from interface and method returning String") { - val result = template.requestBodyAndHeader("%s:intf?method=m2" format InternalSchema, "x", "test", "y") - assert(result === "m2impl: x y") - } - - scenario("in-out exchange with proxy created from class and method returning String") { - val result = template.requestBodyAndHeader("%s:base?method=m2" format InternalSchema, "x", "test", "y") - assert(result === "m2base: x y") - } - - scenario("in-out exchange with proxy created from class and method returning void") { - val result = template.requestBodyAndHeader("%s:base?method=m5" format InternalSchema, "x", "test", "y") - assert(result === "x") // returns initial body - } - - scenario("in-only exchange with proxy created from class and method returning String") { - val result = template.send("%s:base?method=m2" format InternalSchema, InOnly, new Processor { - def process(exchange: Exchange) = { - exchange.getIn.setBody("x") - exchange.getIn.setHeader("test", "y") - } - }); - assert(result.getPattern === InOnly) - assert(result.getIn.getBody === "m2base: x y") - assert(result.getOut.getBody === null) - } - - scenario("in-only exchange with proxy created from class and method returning void") { - val result = template.send("%s:base?method=m5" format InternalSchema, InOnly, new Processor { - def process(exchange: Exchange) = { - exchange.getIn.setBody("x") - exchange.getIn.setHeader("test", "y") - } - }); - assert(result.getPattern === InOnly) - assert(result.getIn.getBody === "x") - assert(result.getOut.getBody === null) - } - } - - feature("Communicate with an active object from a Camel application from a custom Camel route") { - - scenario("in-out exchange with externally registered active object") { - val result = template.requestBody("direct:test", "test") - assert(result === "foo: test") - } - - scenario("in-out exchange with internally registered active object not possible") { - intercept[ResolveEndpointFailedException] { - template.requestBodyAndHeader("active-object:intf?method=m2", "x", "test", "y") - } - } - } -} - -object ActiveObjectComponentFeatureTest { - class CustomRouteBuilder extends RouteBuilder { - def configure = { - from("direct:test").to("active-object:pojo?method=foo") - } - } -} diff --git a/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala b/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala index 7d1482c36c..331f2c23b6 100644 --- a/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala +++ b/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala @@ -3,37 +3,36 @@ package se.scalablesolutions.akka.camel.component import java.util.concurrent.{TimeUnit, CountDownLatch} import org.apache.camel.RuntimeCamelException +import org.apache.camel.builder.RouteBuilder +import org.apache.camel.component.mock.MockEndpoint import org.scalatest.{BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec} import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.actor.{ActorRegistry, Actor} -import se.scalablesolutions.akka.camel.{Message, CamelContextManager} +import se.scalablesolutions.akka.camel.{Failure, Message, CamelContextManager} import se.scalablesolutions.akka.camel.support._ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach { + import ActorComponentFeatureTest._ + override protected def beforeAll = { ActorRegistry.shutdownAll CamelContextManager.init + CamelContextManager.context.addRoutes(new TestRoute) CamelContextManager.start } override protected def afterAll = CamelContextManager.stop - override protected def afterEach = ActorRegistry.shutdownAll + override protected def afterEach = { + ActorRegistry.shutdownAll + mockEndpoint.reset + } - feature("Communicate with an actor from a Camel application using actor endpoint URIs") { + feature("Communicate with an actor via an actor:uuid endpoint") { import CamelContextManager.template - scenario("one-way communication using actor id") { - val actor = actorOf[Tester1].start - val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get - template.sendBody("actor:%s" format actor.id, "Martin") - assert(latch.await(5000, TimeUnit.MILLISECONDS)) - val reply = (actor !! GetRetainedMessage).get.asInstanceOf[Message] - assert(reply.body === "Martin") - } - - scenario("one-way communication using actor uuid") { + scenario("one-way communication") { val actor = actorOf[Tester1].start val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get template.sendBody("actor:uuid:%s" format actor.uuid, "Martin") @@ -42,12 +41,7 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with assert(reply.body === "Martin") } - scenario("two-way communication using actor id") { - val actor = actorOf[Tester2].start - assert(template.requestBody("actor:%s" format actor.id, "Martin") === "Hello Martin") - } - - scenario("two-way communication using actor uuid") { + scenario("two-way communication") { val actor = actorOf[Tester2].start assert(template.requestBody("actor:uuid:%s" format actor.uuid, "Martin") === "Hello Martin") } @@ -58,5 +52,79 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with template.requestBody("actor:uuid:%s?blocking=true" format actor.uuid, "Martin") } } + + scenario("two-way communication via a custom route with failure response") { + mockEndpoint.expectedBodiesReceived("whatever") + template.requestBody("direct:failure-test-1", "whatever") + mockEndpoint.assertIsSatisfied + } + + scenario("two-way communication via a custom route with exception") { + mockEndpoint.expectedBodiesReceived("whatever") + template.requestBody("direct:failure-test-2", "whatever") + mockEndpoint.assertIsSatisfied + } + } + + feature("Communicate with an actor via an actor:id endpoint") { + import CamelContextManager.template + + scenario("one-way communication") { + val actor = actorOf[Tester1].start + val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get + template.sendBody("actor:%s" format actor.id, "Martin") + assert(latch.await(5000, TimeUnit.MILLISECONDS)) + val reply = (actor !! GetRetainedMessage).get.asInstanceOf[Message] + assert(reply.body === "Martin") + } + + scenario("two-way communication") { + val actor = actorOf[Tester2].start + assert(template.requestBody("actor:%s" format actor.id, "Martin") === "Hello Martin") + } + + scenario("two-way communication via a custom route") { + val actor = actorOf[CustomIdActor].start + assert(template.requestBody("direct:custom-id-test-1", "Martin") === "Received Martin") + assert(template.requestBody("direct:custom-id-test-2", "Martin") === "Received Martin") + } + } + + private def mockEndpoint = CamelContextManager.context.getEndpoint("mock:mock", classOf[MockEndpoint]) +} + +object ActorComponentFeatureTest { + class CustomIdActor extends Actor { + self.id = "custom-id" + protected def receive = { + case msg: Message => self.reply("Received %s" format msg.body) + } + } + + class FailWithMessage extends Actor { + protected def receive = { + case msg: Message => self.reply(Failure(new Exception("test"))) + } + } + + class FailWithException extends Actor { + protected def receive = { + case msg: Message => throw new Exception("test") + } + } + + class TestRoute extends RouteBuilder { + val failWithMessage = actorOf[FailWithMessage].start + val failWithException = actorOf[FailWithException].start + def configure { + from("direct:custom-id-test-1").to("actor:custom-id") + from("direct:custom-id-test-2").to("actor:id:custom-id") + from("direct:failure-test-1") + .onException(classOf[Exception]).to("mock:mock").handled(true).end + .to("actor:uuid:%s" format failWithMessage.uuid) + from("direct:failure-test-2") + .onException(classOf[Exception]).to("mock:mock").handled(true).end + .to("actor:uuid:%s?blocking=true" format failWithException.uuid) + } } } diff --git a/akka-camel/src/test/scala/component/ActorProducerTest.scala b/akka-camel/src/test/scala/component/ActorProducerTest.scala index 300d0ca617..5e8a674e55 100644 --- a/akka-camel/src/test/scala/component/ActorProducerTest.scala +++ b/akka-camel/src/test/scala/component/ActorProducerTest.scala @@ -116,4 +116,4 @@ object ActorProducerTest { } } -} \ No newline at end of file +} diff --git a/akka-camel/src/test/scala/component/TypedActorComponentFeatureTest.scala b/akka-camel/src/test/scala/component/TypedActorComponentFeatureTest.scala new file mode 100644 index 0000000000..06f7e29173 --- /dev/null +++ b/akka-camel/src/test/scala/component/TypedActorComponentFeatureTest.scala @@ -0,0 +1,109 @@ +package se.scalablesolutions.akka.camel.component + +import org.scalatest.{BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec} + +import org.apache.camel.builder.RouteBuilder +import se.scalablesolutions.akka.actor.Actor._ +import se.scalablesolutions.akka.actor.{ActorRegistry, TypedActor} +import se.scalablesolutions.akka.camel._ +import org.apache.camel.impl.{DefaultCamelContext, SimpleRegistry} +import org.apache.camel.{ResolveEndpointFailedException, ExchangePattern, Exchange, Processor} + +/** + * @author Martin Krasser + */ +class TypedActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach { + import TypedActorComponentFeatureTest._ + import CamelContextManager.template + + override protected def beforeAll = { + val typedActor = TypedActor.newInstance(classOf[SampleTypedActor], classOf[SampleTypedActorImpl]) // not a consumer + val typedConsumer = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl]) + + val registry = new SimpleRegistry + // external registration + registry.put("ta", typedActor) + + CamelContextManager.init(new DefaultCamelContext(registry)) + CamelContextManager.context.addRoutes(new CustomRouteBuilder) + CamelContextManager.start + + // Internal registration + CamelContextManager.typedActorRegistry.put("tc", typedConsumer) + } + + override protected def afterAll = { + CamelContextManager.stop + ActorRegistry.shutdownAll + } + + feature("Communicate with an internally-registered typed actor using typed-actor-internal endpoint URIs") { + import TypedActorComponent.InternalSchema + import ExchangePattern._ + + scenario("two-way communication with method returning String") { + val result1 = template.requestBodyAndHeader("%s:tc?method=m2" format InternalSchema, "x", "test", "y") + val result2 = template.requestBodyAndHeader("%s:tc?method=m4" format InternalSchema, "x", "test", "y") + assert(result1 === "m2: x y") + assert(result2 === "m4: x y") + } + + scenario("two-way communication with method returning void") { + val result = template.requestBodyAndHeader("%s:tc?method=m5" format InternalSchema, "x", "test", "y") + assert(result === "x") // returns initial body + } + + scenario("one-way communication with method returning String") { + val result = template.send("%s:tc?method=m2" format InternalSchema, InOnly, new Processor { + def process(exchange: Exchange) = { + exchange.getIn.setBody("x") + exchange.getIn.setHeader("test", "y") + } + }); + assert(result.getPattern === InOnly) + assert(result.getIn.getBody === "m2: x y") + assert(result.getOut.getBody === null) + } + + scenario("one-way communication with method returning void") { + val result = template.send("%s:tc?method=m5" format InternalSchema, InOnly, new Processor { + def process(exchange: Exchange) = { + exchange.getIn.setBody("x") + exchange.getIn.setHeader("test", "y") + } + }); + assert(result.getPattern === InOnly) + assert(result.getIn.getBody === "x") + assert(result.getOut.getBody === null) + } + + } + + feature("Communicate with an internally-registered typed actor using typed-actor endpoint URIs") { + scenario("communication not possible") { + intercept[ResolveEndpointFailedException] { + template.requestBodyAndHeader("typed-actor:tc?method=m2", "x", "test", "y") + } + } + } + + feature("Communicate with an externally-registered typed actor using typed-actor endpoint URIs") { + scenario("two-way communication with method returning String") { + val result = template.requestBody("typed-actor:ta?method=foo", "test") + assert(result === "foo: test") + } + + scenario("two-way communication with method returning String via custom route") { + val result = template.requestBody("direct:test", "test") + assert(result === "foo: test") + } + } +} + +object TypedActorComponentFeatureTest { + class CustomRouteBuilder extends RouteBuilder { + def configure = { + from("direct:test").to("typed-actor:ta?method=foo") + } + } +} diff --git a/akka-camel/src/test/scala/support/TestSupport.scala b/akka-camel/src/test/scala/support/TestSupport.scala index 61118ef3e3..8e1322e14f 100644 --- a/akka-camel/src/test/scala/support/TestSupport.scala +++ b/akka-camel/src/test/scala/support/TestSupport.scala @@ -2,6 +2,8 @@ package se.scalablesolutions.akka.camel.support import java.util.concurrent.{TimeUnit, CountDownLatch} +import collection.mutable.Buffer + import se.scalablesolutions.akka.camel.Message import se.scalablesolutions.akka.actor.Actor @@ -54,12 +56,13 @@ trait Respond { this: Actor => } trait Retain { this: Actor => - var message: Any = _ + val messages = Buffer[Any]() def retain: Handler = { - case GetRetainedMessage => self.reply(message) + case GetRetainedMessage => self.reply(messages.last) + case GetRetainedMessages(p) => self.reply(messages.toList.filter(p)) case msg => { - message = msg + messages += msg msg } } @@ -73,3 +76,6 @@ trait Noop { this: Actor => case class SetExpectedMessageCount(num: Int) case class GetRetainedMessage() +case class GetRetainedMessages(p: Any => Boolean) { + def this() = this(_ => true) +} diff --git a/akka-core/.ensime b/akka-core/.ensime index 15e1ae85be..0b21e8eb5c 100644 --- a/akka-core/.ensime +++ b/akka-core/.ensime @@ -1,11 +1,11 @@ ( ;; Where you unpacked the ENSIME distribution. - :server-root "/home/jboner/emacs-config/lib/ensime" + :server-root "/Users/jboner/config/emacs-config/lib/ensime" ;; The command with which to invoke the ENSIME server. Change this to ;; "bin/server.bat" if your're on Windows. - :server-cmd "bin/server.bat" + :server-cmd "bin/server.sh" ;; The host to connect to. Connecting to remote ENSIME servers is not diff --git a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/configuration.java b/akka-core/src/main/java/se/scalablesolutions/akka/annotation/configuration.java deleted file mode 100644 index 9c5375398b..0000000000 --- a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/configuration.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.actor.annotation; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface configuration {} diff --git a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/inittransactionalstate.java b/akka-core/src/main/java/se/scalablesolutions/akka/annotation/inittransactionalstate.java deleted file mode 100644 index 35c5f05afe..0000000000 --- a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/inittransactionalstate.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.actor.annotation; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface inittransactionalstate {} diff --git a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/postrestart.java b/akka-core/src/main/java/se/scalablesolutions/akka/annotation/postrestart.java deleted file mode 100644 index 5eed474832..0000000000 --- a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/postrestart.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.actor.annotation; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface postrestart {} diff --git a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/prerestart.java b/akka-core/src/main/java/se/scalablesolutions/akka/annotation/prerestart.java deleted file mode 100644 index 94f9a01405..0000000000 --- a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/prerestart.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.actor.annotation; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface prerestart {} diff --git a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/shutdown.java b/akka-core/src/main/java/se/scalablesolutions/akka/annotation/shutdown.java deleted file mode 100644 index f806e7bca6..0000000000 --- a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/shutdown.java +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.actor.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface shutdown {} \ No newline at end of file diff --git a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/state.java b/akka-core/src/main/java/se/scalablesolutions/akka/annotation/state.java deleted file mode 100644 index 509d129c1b..0000000000 --- a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/state.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.actor.annotation; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface state {} diff --git a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/transactionrequired.java b/akka-core/src/main/java/se/scalablesolutions/akka/annotation/transactionrequired.java deleted file mode 100644 index c41a09ee46..0000000000 --- a/akka-core/src/main/java/se/scalablesolutions/akka/annotation/transactionrequired.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.actor.annotation; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface transactionrequired {} diff --git a/akka-core/src/main/java/se/scalablesolutions/akka/config/ActiveObjectGuiceModule.java b/akka-core/src/main/java/se/scalablesolutions/akka/config/TypedActorGuiceModule.java similarity index 85% rename from akka-core/src/main/java/se/scalablesolutions/akka/config/ActiveObjectGuiceModule.java rename to akka-core/src/main/java/se/scalablesolutions/akka/config/TypedActorGuiceModule.java index 60dfd4cadd..0c2ed11402 100644 --- a/akka-core/src/main/java/se/scalablesolutions/akka/config/ActiveObjectGuiceModule.java +++ b/akka-core/src/main/java/se/scalablesolutions/akka/config/TypedActorGuiceModule.java @@ -13,10 +13,10 @@ import com.google.inject.Singleton; /** * @author Jonas Bonér */ -public class ActiveObjectGuiceModule extends AbstractModule { +public class TypedActorGuiceModule extends AbstractModule { private final List bindings; - public ActiveObjectGuiceModule(final List bindings) { + public TypedActorGuiceModule(final List bindings) { this.bindings = bindings; } diff --git a/akka-core/src/main/java/se/scalablesolutions/akka/remote/protocol/RemoteProtocol.java b/akka-core/src/main/java/se/scalablesolutions/akka/remote/protocol/RemoteProtocol.java index 9af73c6c77..8babc16770 100644 --- a/akka-core/src/main/java/se/scalablesolutions/akka/remote/protocol/RemoteProtocol.java +++ b/akka-core/src/main/java/se/scalablesolutions/akka/remote/protocol/RemoteProtocol.java @@ -8,6 +8,75 @@ public final class RemoteProtocol { public static void registerAllExtensions( com.google.protobuf.ExtensionRegistry registry) { } + public enum ActorType + implements com.google.protobuf.ProtocolMessageEnum { + SCALA_ACTOR(0, 1), + JAVA_ACTOR(1, 2), + TYPED_ACTOR(2, 3), + ; + + + public final int getNumber() { return value; } + + public static ActorType valueOf(int value) { + switch (value) { + case 1: return SCALA_ACTOR; + case 2: return JAVA_ACTOR; + case 3: return TYPED_ACTOR; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public ActorType findValueByNumber(int number) { + return ActorType.valueOf(number) + ; } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.getDescriptor().getEnumTypes().get(0); + } + + private static final ActorType[] VALUES = { + SCALA_ACTOR, JAVA_ACTOR, TYPED_ACTOR, + }; + public static ActorType valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + private final int index; + private final int value; + private ActorType(int index, int value) { + this.index = index; + this.value = value; + } + + static { + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.getDescriptor(); + } + + // @@protoc_insertion_point(enum_scope:ActorType) + } + public enum SerializationSchemeType implements com.google.protobuf.ProtocolMessageEnum { JAVA(0, 1), @@ -53,7 +122,7 @@ public final class RemoteProtocol { } public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() { - return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.getDescriptor().getEnumTypes().get(0); + return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.getDescriptor().getEnumTypes().get(1); } private static final SerializationSchemeType[] VALUES = { @@ -120,7 +189,7 @@ public final class RemoteProtocol { } public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() { - return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.getDescriptor().getEnumTypes().get(1); + return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.getDescriptor().getEnumTypes().get(2); } private static final LifeCycleType[] VALUES = { @@ -1870,6 +1939,825 @@ public final class RemoteProtocol { // @@protoc_insertion_point(class_scope:MessageProtocol) } + public static final class ActorInfoProtocol extends + com.google.protobuf.GeneratedMessage { + // Use ActorInfoProtocol.newBuilder() to construct. + private ActorInfoProtocol() { + initFields(); + } + private ActorInfoProtocol(boolean noInit) {} + + private static final ActorInfoProtocol defaultInstance; + public static ActorInfoProtocol getDefaultInstance() { + return defaultInstance; + } + + public ActorInfoProtocol getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.internal_static_ActorInfoProtocol_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.internal_static_ActorInfoProtocol_fieldAccessorTable; + } + + // required string uuid = 1; + public static final int UUID_FIELD_NUMBER = 1; + private boolean hasUuid; + private java.lang.String uuid_ = ""; + public boolean hasUuid() { return hasUuid; } + public java.lang.String getUuid() { return uuid_; } + + // required string target = 2; + public static final int TARGET_FIELD_NUMBER = 2; + private boolean hasTarget; + private java.lang.String target_ = ""; + public boolean hasTarget() { return hasTarget; } + public java.lang.String getTarget() { return target_; } + + // required uint64 timeout = 3; + public static final int TIMEOUT_FIELD_NUMBER = 3; + private boolean hasTimeout; + private long timeout_ = 0L; + public boolean hasTimeout() { return hasTimeout; } + public long getTimeout() { return timeout_; } + + // required .ActorType actorType = 4; + public static final int ACTORTYPE_FIELD_NUMBER = 4; + private boolean hasActorType; + private se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorType actorType_; + public boolean hasActorType() { return hasActorType; } + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorType getActorType() { return actorType_; } + + // optional .TypedActorInfoProtocol typedActorInfo = 5; + public static final int TYPEDACTORINFO_FIELD_NUMBER = 5; + private boolean hasTypedActorInfo; + private se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol typedActorInfo_; + public boolean hasTypedActorInfo() { return hasTypedActorInfo; } + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol getTypedActorInfo() { return typedActorInfo_; } + + private void initFields() { + actorType_ = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorType.SCALA_ACTOR; + typedActorInfo_ = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.getDefaultInstance(); + } + public final boolean isInitialized() { + if (!hasUuid) return false; + if (!hasTarget) return false; + if (!hasTimeout) return false; + if (!hasActorType) return false; + if (hasTypedActorInfo()) { + if (!getTypedActorInfo().isInitialized()) return false; + } + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (hasUuid()) { + output.writeString(1, getUuid()); + } + if (hasTarget()) { + output.writeString(2, getTarget()); + } + if (hasTimeout()) { + output.writeUInt64(3, getTimeout()); + } + if (hasActorType()) { + output.writeEnum(4, getActorType().getNumber()); + } + if (hasTypedActorInfo()) { + output.writeMessage(5, getTypedActorInfo()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasUuid()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(1, getUuid()); + } + if (hasTarget()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(2, getTarget()); + } + if (hasTimeout()) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(3, getTimeout()); + } + if (hasActorType()) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(4, getActorType().getNumber()); + } + if (hasTypedActorInfo()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, getTypedActorInfo()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder { + private se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol result; + + // Construct using se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.newBuilder() + private Builder() {} + + private static Builder create() { + Builder builder = new Builder(); + builder.result = new se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol(); + return builder; + } + + protected se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol internalGetResult() { + return result; + } + + public Builder clear() { + if (result == null) { + throw new IllegalStateException( + "Cannot call clear() after build()."); + } + result = new se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol(); + return this; + } + + public Builder clone() { + return create().mergeFrom(result); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.getDescriptor(); + } + + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol getDefaultInstanceForType() { + return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.getDefaultInstance(); + } + + public boolean isInitialized() { + return result.isInitialized(); + } + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol build() { + if (result != null && !isInitialized()) { + throw newUninitializedMessageException(result); + } + return buildPartial(); + } + + private se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + if (!isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return buildPartial(); + } + + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol buildPartial() { + if (result == null) { + throw new IllegalStateException( + "build() has already been called on this Builder."); + } + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol returnMe = result; + result = null; + return returnMe; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol) { + return mergeFrom((se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol other) { + if (other == se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.getDefaultInstance()) return this; + if (other.hasUuid()) { + setUuid(other.getUuid()); + } + if (other.hasTarget()) { + setTarget(other.getTarget()); + } + if (other.hasTimeout()) { + setTimeout(other.getTimeout()); + } + if (other.hasActorType()) { + setActorType(other.getActorType()); + } + if (other.hasTypedActorInfo()) { + mergeTypedActorInfo(other.getTypedActorInfo()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + return this; + } + break; + } + case 10: { + setUuid(input.readString()); + break; + } + case 18: { + setTarget(input.readString()); + break; + } + case 24: { + setTimeout(input.readUInt64()); + break; + } + case 32: { + int rawValue = input.readEnum(); + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorType value = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorType.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(4, rawValue); + } else { + setActorType(value); + } + break; + } + case 42: { + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.Builder subBuilder = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.newBuilder(); + if (hasTypedActorInfo()) { + subBuilder.mergeFrom(getTypedActorInfo()); + } + input.readMessage(subBuilder, extensionRegistry); + setTypedActorInfo(subBuilder.buildPartial()); + break; + } + } + } + } + + + // required string uuid = 1; + public boolean hasUuid() { + return result.hasUuid(); + } + public java.lang.String getUuid() { + return result.getUuid(); + } + public Builder setUuid(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasUuid = true; + result.uuid_ = value; + return this; + } + public Builder clearUuid() { + result.hasUuid = false; + result.uuid_ = getDefaultInstance().getUuid(); + return this; + } + + // required string target = 2; + public boolean hasTarget() { + return result.hasTarget(); + } + public java.lang.String getTarget() { + return result.getTarget(); + } + public Builder setTarget(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasTarget = true; + result.target_ = value; + return this; + } + public Builder clearTarget() { + result.hasTarget = false; + result.target_ = getDefaultInstance().getTarget(); + return this; + } + + // required uint64 timeout = 3; + public boolean hasTimeout() { + return result.hasTimeout(); + } + public long getTimeout() { + return result.getTimeout(); + } + public Builder setTimeout(long value) { + result.hasTimeout = true; + result.timeout_ = value; + return this; + } + public Builder clearTimeout() { + result.hasTimeout = false; + result.timeout_ = 0L; + return this; + } + + // required .ActorType actorType = 4; + public boolean hasActorType() { + return result.hasActorType(); + } + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorType getActorType() { + return result.getActorType(); + } + public Builder setActorType(se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorType value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasActorType = true; + result.actorType_ = value; + return this; + } + public Builder clearActorType() { + result.hasActorType = false; + result.actorType_ = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorType.SCALA_ACTOR; + return this; + } + + // optional .TypedActorInfoProtocol typedActorInfo = 5; + public boolean hasTypedActorInfo() { + return result.hasTypedActorInfo(); + } + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol getTypedActorInfo() { + return result.getTypedActorInfo(); + } + public Builder setTypedActorInfo(se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasTypedActorInfo = true; + result.typedActorInfo_ = value; + return this; + } + public Builder setTypedActorInfo(se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.Builder builderForValue) { + result.hasTypedActorInfo = true; + result.typedActorInfo_ = builderForValue.build(); + return this; + } + public Builder mergeTypedActorInfo(se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol value) { + if (result.hasTypedActorInfo() && + result.typedActorInfo_ != se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.getDefaultInstance()) { + result.typedActorInfo_ = + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.newBuilder(result.typedActorInfo_).mergeFrom(value).buildPartial(); + } else { + result.typedActorInfo_ = value; + } + result.hasTypedActorInfo = true; + return this; + } + public Builder clearTypedActorInfo() { + result.hasTypedActorInfo = false; + result.typedActorInfo_ = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.getDefaultInstance(); + return this; + } + + // @@protoc_insertion_point(builder_scope:ActorInfoProtocol) + } + + static { + defaultInstance = new ActorInfoProtocol(true); + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.internalForceInit(); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:ActorInfoProtocol) + } + + public static final class TypedActorInfoProtocol extends + com.google.protobuf.GeneratedMessage { + // Use TypedActorInfoProtocol.newBuilder() to construct. + private TypedActorInfoProtocol() { + initFields(); + } + private TypedActorInfoProtocol(boolean noInit) {} + + private static final TypedActorInfoProtocol defaultInstance; + public static TypedActorInfoProtocol getDefaultInstance() { + return defaultInstance; + } + + public TypedActorInfoProtocol getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.internal_static_TypedActorInfoProtocol_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.internal_static_TypedActorInfoProtocol_fieldAccessorTable; + } + + // required string interface = 1; + public static final int INTERFACE_FIELD_NUMBER = 1; + private boolean hasInterface; + private java.lang.String interface_ = ""; + public boolean hasInterface() { return hasInterface; } + public java.lang.String getInterface() { return interface_; } + + // required string method = 2; + public static final int METHOD_FIELD_NUMBER = 2; + private boolean hasMethod; + private java.lang.String method_ = ""; + public boolean hasMethod() { return hasMethod; } + public java.lang.String getMethod() { return method_; } + + private void initFields() { + } + public final boolean isInitialized() { + if (!hasInterface) return false; + if (!hasMethod) return false; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (hasInterface()) { + output.writeString(1, getInterface()); + } + if (hasMethod()) { + output.writeString(2, getMethod()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasInterface()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(1, getInterface()); + } + if (hasMethod()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(2, getMethod()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder { + private se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol result; + + // Construct using se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.newBuilder() + private Builder() {} + + private static Builder create() { + Builder builder = new Builder(); + builder.result = new se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol(); + return builder; + } + + protected se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol internalGetResult() { + return result; + } + + public Builder clear() { + if (result == null) { + throw new IllegalStateException( + "Cannot call clear() after build()."); + } + result = new se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol(); + return this; + } + + public Builder clone() { + return create().mergeFrom(result); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.getDescriptor(); + } + + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol getDefaultInstanceForType() { + return se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.getDefaultInstance(); + } + + public boolean isInitialized() { + return result.isInitialized(); + } + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol build() { + if (result != null && !isInitialized()) { + throw newUninitializedMessageException(result); + } + return buildPartial(); + } + + private se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + if (!isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return buildPartial(); + } + + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol buildPartial() { + if (result == null) { + throw new IllegalStateException( + "build() has already been called on this Builder."); + } + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol returnMe = result; + result = null; + return returnMe; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol) { + return mergeFrom((se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol other) { + if (other == se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.getDefaultInstance()) return this; + if (other.hasInterface()) { + setInterface(other.getInterface()); + } + if (other.hasMethod()) { + setMethod(other.getMethod()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + return this; + } + break; + } + case 10: { + setInterface(input.readString()); + break; + } + case 18: { + setMethod(input.readString()); + break; + } + } + } + } + + + // required string interface = 1; + public boolean hasInterface() { + return result.hasInterface(); + } + public java.lang.String getInterface() { + return result.getInterface(); + } + public Builder setInterface(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasInterface = true; + result.interface_ = value; + return this; + } + public Builder clearInterface() { + result.hasInterface = false; + result.interface_ = getDefaultInstance().getInterface(); + return this; + } + + // required string method = 2; + public boolean hasMethod() { + return result.hasMethod(); + } + public java.lang.String getMethod() { + return result.getMethod(); + } + public Builder setMethod(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasMethod = true; + result.method_ = value; + return this; + } + public Builder clearMethod() { + result.hasMethod = false; + result.method_ = getDefaultInstance().getMethod(); + return this; + } + + // @@protoc_insertion_point(builder_scope:TypedActorInfoProtocol) + } + + static { + defaultInstance = new TypedActorInfoProtocol(true); + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.internalForceInit(); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:TypedActorInfoProtocol) + } + public static final class RemoteRequestProtocol extends com.google.protobuf.GeneratedMessage { // Use RemoteRequestProtocol.newBuilder() to construct. @@ -1911,64 +2799,29 @@ public final class RemoteProtocol { public boolean hasMessage() { return hasMessage; } public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.MessageProtocol getMessage() { return message_; } - // optional string method = 3; - public static final int METHOD_FIELD_NUMBER = 3; - private boolean hasMethod; - private java.lang.String method_ = ""; - public boolean hasMethod() { return hasMethod; } - public java.lang.String getMethod() { return method_; } + // required .ActorInfoProtocol actorInfo = 3; + public static final int ACTORINFO_FIELD_NUMBER = 3; + private boolean hasActorInfo; + private se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol actorInfo_; + public boolean hasActorInfo() { return hasActorInfo; } + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol getActorInfo() { return actorInfo_; } - // required string target = 4; - public static final int TARGET_FIELD_NUMBER = 4; - private boolean hasTarget; - private java.lang.String target_ = ""; - public boolean hasTarget() { return hasTarget; } - public java.lang.String getTarget() { return target_; } - - // required string uuid = 5; - public static final int UUID_FIELD_NUMBER = 5; - private boolean hasUuid; - private java.lang.String uuid_ = ""; - public boolean hasUuid() { return hasUuid; } - public java.lang.String getUuid() { return uuid_; } - - // required uint64 timeout = 6; - public static final int TIMEOUT_FIELD_NUMBER = 6; - private boolean hasTimeout; - private long timeout_ = 0L; - public boolean hasTimeout() { return hasTimeout; } - public long getTimeout() { return timeout_; } - - // optional string supervisorUuid = 7; - public static final int SUPERVISORUUID_FIELD_NUMBER = 7; - private boolean hasSupervisorUuid; - private java.lang.String supervisorUuid_ = ""; - public boolean hasSupervisorUuid() { return hasSupervisorUuid; } - public java.lang.String getSupervisorUuid() { return supervisorUuid_; } - - // required bool isActor = 8; - public static final int ISACTOR_FIELD_NUMBER = 8; - private boolean hasIsActor; - private boolean isActor_ = false; - public boolean hasIsActor() { return hasIsActor; } - public boolean getIsActor() { return isActor_; } - - // required bool isOneWay = 9; - public static final int ISONEWAY_FIELD_NUMBER = 9; + // required bool isOneWay = 4; + public static final int ISONEWAY_FIELD_NUMBER = 4; private boolean hasIsOneWay; private boolean isOneWay_ = false; public boolean hasIsOneWay() { return hasIsOneWay; } public boolean getIsOneWay() { return isOneWay_; } - // required bool isEscaped = 10; - public static final int ISESCAPED_FIELD_NUMBER = 10; - private boolean hasIsEscaped; - private boolean isEscaped_ = false; - public boolean hasIsEscaped() { return hasIsEscaped; } - public boolean getIsEscaped() { return isEscaped_; } + // optional string supervisorUuid = 5; + public static final int SUPERVISORUUID_FIELD_NUMBER = 5; + private boolean hasSupervisorUuid; + private java.lang.String supervisorUuid_ = ""; + public boolean hasSupervisorUuid() { return hasSupervisorUuid; } + public java.lang.String getSupervisorUuid() { return supervisorUuid_; } - // optional .RemoteActorRefProtocol sender = 11; - public static final int SENDER_FIELD_NUMBER = 11; + // optional .RemoteActorRefProtocol sender = 6; + public static final int SENDER_FIELD_NUMBER = 6; private boolean hasSender; private se.scalablesolutions.akka.remote.protocol.RemoteProtocol.RemoteActorRefProtocol sender_; public boolean hasSender() { return hasSender; } @@ -1976,18 +2829,16 @@ public final class RemoteProtocol { private void initFields() { message_ = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.MessageProtocol.getDefaultInstance(); + actorInfo_ = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.getDefaultInstance(); sender_ = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.RemoteActorRefProtocol.getDefaultInstance(); } public final boolean isInitialized() { if (!hasId) return false; if (!hasMessage) return false; - if (!hasTarget) return false; - if (!hasUuid) return false; - if (!hasTimeout) return false; - if (!hasIsActor) return false; + if (!hasActorInfo) return false; if (!hasIsOneWay) return false; - if (!hasIsEscaped) return false; if (!getMessage().isInitialized()) return false; + if (!getActorInfo().isInitialized()) return false; if (hasSender()) { if (!getSender().isInitialized()) return false; } @@ -2003,32 +2854,17 @@ public final class RemoteProtocol { if (hasMessage()) { output.writeMessage(2, getMessage()); } - if (hasMethod()) { - output.writeString(3, getMethod()); - } - if (hasTarget()) { - output.writeString(4, getTarget()); - } - if (hasUuid()) { - output.writeString(5, getUuid()); - } - if (hasTimeout()) { - output.writeUInt64(6, getTimeout()); - } - if (hasSupervisorUuid()) { - output.writeString(7, getSupervisorUuid()); - } - if (hasIsActor()) { - output.writeBool(8, getIsActor()); + if (hasActorInfo()) { + output.writeMessage(3, getActorInfo()); } if (hasIsOneWay()) { - output.writeBool(9, getIsOneWay()); + output.writeBool(4, getIsOneWay()); } - if (hasIsEscaped()) { - output.writeBool(10, getIsEscaped()); + if (hasSupervisorUuid()) { + output.writeString(5, getSupervisorUuid()); } if (hasSender()) { - output.writeMessage(11, getSender()); + output.writeMessage(6, getSender()); } getUnknownFields().writeTo(output); } @@ -2047,41 +2883,21 @@ public final class RemoteProtocol { size += com.google.protobuf.CodedOutputStream .computeMessageSize(2, getMessage()); } - if (hasMethod()) { + if (hasActorInfo()) { size += com.google.protobuf.CodedOutputStream - .computeStringSize(3, getMethod()); - } - if (hasTarget()) { - size += com.google.protobuf.CodedOutputStream - .computeStringSize(4, getTarget()); - } - if (hasUuid()) { - size += com.google.protobuf.CodedOutputStream - .computeStringSize(5, getUuid()); - } - if (hasTimeout()) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(6, getTimeout()); - } - if (hasSupervisorUuid()) { - size += com.google.protobuf.CodedOutputStream - .computeStringSize(7, getSupervisorUuid()); - } - if (hasIsActor()) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(8, getIsActor()); + .computeMessageSize(3, getActorInfo()); } if (hasIsOneWay()) { size += com.google.protobuf.CodedOutputStream - .computeBoolSize(9, getIsOneWay()); + .computeBoolSize(4, getIsOneWay()); } - if (hasIsEscaped()) { + if (hasSupervisorUuid()) { size += com.google.protobuf.CodedOutputStream - .computeBoolSize(10, getIsEscaped()); + .computeStringSize(5, getSupervisorUuid()); } if (hasSender()) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(11, getSender()); + .computeMessageSize(6, getSender()); } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; @@ -2247,29 +3063,14 @@ public final class RemoteProtocol { if (other.hasMessage()) { mergeMessage(other.getMessage()); } - if (other.hasMethod()) { - setMethod(other.getMethod()); - } - if (other.hasTarget()) { - setTarget(other.getTarget()); - } - if (other.hasUuid()) { - setUuid(other.getUuid()); - } - if (other.hasTimeout()) { - setTimeout(other.getTimeout()); - } - if (other.hasSupervisorUuid()) { - setSupervisorUuid(other.getSupervisorUuid()); - } - if (other.hasIsActor()) { - setIsActor(other.getIsActor()); + if (other.hasActorInfo()) { + mergeActorInfo(other.getActorInfo()); } if (other.hasIsOneWay()) { setIsOneWay(other.getIsOneWay()); } - if (other.hasIsEscaped()) { - setIsEscaped(other.getIsEscaped()); + if (other.hasSupervisorUuid()) { + setSupervisorUuid(other.getSupervisorUuid()); } if (other.hasSender()) { mergeSender(other.getSender()); @@ -2313,38 +3114,23 @@ public final class RemoteProtocol { break; } case 26: { - setMethod(input.readString()); + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.Builder subBuilder = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.newBuilder(); + if (hasActorInfo()) { + subBuilder.mergeFrom(getActorInfo()); + } + input.readMessage(subBuilder, extensionRegistry); + setActorInfo(subBuilder.buildPartial()); break; } - case 34: { - setTarget(input.readString()); - break; - } - case 42: { - setUuid(input.readString()); - break; - } - case 48: { - setTimeout(input.readUInt64()); - break; - } - case 58: { - setSupervisorUuid(input.readString()); - break; - } - case 64: { - setIsActor(input.readBool()); - break; - } - case 72: { + case 32: { setIsOneWay(input.readBool()); break; } - case 80: { - setIsEscaped(input.readBool()); + case 42: { + setSupervisorUuid(input.readString()); break; } - case 90: { + case 50: { se.scalablesolutions.akka.remote.protocol.RemoteProtocol.RemoteActorRefProtocol.Builder subBuilder = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.RemoteActorRefProtocol.newBuilder(); if (hasSender()) { subBuilder.mergeFrom(getSender()); @@ -2413,88 +3199,62 @@ public final class RemoteProtocol { return this; } - // optional string method = 3; - public boolean hasMethod() { - return result.hasMethod(); + // required .ActorInfoProtocol actorInfo = 3; + public boolean hasActorInfo() { + return result.hasActorInfo(); } - public java.lang.String getMethod() { - return result.getMethod(); + public se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol getActorInfo() { + return result.getActorInfo(); } - public Builder setMethod(java.lang.String value) { + public Builder setActorInfo(se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol value) { if (value == null) { - throw new NullPointerException(); - } - result.hasMethod = true; - result.method_ = value; + throw new NullPointerException(); + } + result.hasActorInfo = true; + result.actorInfo_ = value; return this; } - public Builder clearMethod() { - result.hasMethod = false; - result.method_ = getDefaultInstance().getMethod(); + public Builder setActorInfo(se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.Builder builderForValue) { + result.hasActorInfo = true; + result.actorInfo_ = builderForValue.build(); + return this; + } + public Builder mergeActorInfo(se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol value) { + if (result.hasActorInfo() && + result.actorInfo_ != se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.getDefaultInstance()) { + result.actorInfo_ = + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.newBuilder(result.actorInfo_).mergeFrom(value).buildPartial(); + } else { + result.actorInfo_ = value; + } + result.hasActorInfo = true; + return this; + } + public Builder clearActorInfo() { + result.hasActorInfo = false; + result.actorInfo_ = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.getDefaultInstance(); return this; } - // required string target = 4; - public boolean hasTarget() { - return result.hasTarget(); + // required bool isOneWay = 4; + public boolean hasIsOneWay() { + return result.hasIsOneWay(); } - public java.lang.String getTarget() { - return result.getTarget(); + public boolean getIsOneWay() { + return result.getIsOneWay(); } - public Builder setTarget(java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasTarget = true; - result.target_ = value; + public Builder setIsOneWay(boolean value) { + result.hasIsOneWay = true; + result.isOneWay_ = value; return this; } - public Builder clearTarget() { - result.hasTarget = false; - result.target_ = getDefaultInstance().getTarget(); + public Builder clearIsOneWay() { + result.hasIsOneWay = false; + result.isOneWay_ = false; return this; } - // required string uuid = 5; - public boolean hasUuid() { - return result.hasUuid(); - } - public java.lang.String getUuid() { - return result.getUuid(); - } - public Builder setUuid(java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasUuid = true; - result.uuid_ = value; - return this; - } - public Builder clearUuid() { - result.hasUuid = false; - result.uuid_ = getDefaultInstance().getUuid(); - return this; - } - - // required uint64 timeout = 6; - public boolean hasTimeout() { - return result.hasTimeout(); - } - public long getTimeout() { - return result.getTimeout(); - } - public Builder setTimeout(long value) { - result.hasTimeout = true; - result.timeout_ = value; - return this; - } - public Builder clearTimeout() { - result.hasTimeout = false; - result.timeout_ = 0L; - return this; - } - - // optional string supervisorUuid = 7; + // optional string supervisorUuid = 5; public boolean hasSupervisorUuid() { return result.hasSupervisorUuid(); } @@ -2515,61 +3275,7 @@ public final class RemoteProtocol { return this; } - // required bool isActor = 8; - public boolean hasIsActor() { - return result.hasIsActor(); - } - public boolean getIsActor() { - return result.getIsActor(); - } - public Builder setIsActor(boolean value) { - result.hasIsActor = true; - result.isActor_ = value; - return this; - } - public Builder clearIsActor() { - result.hasIsActor = false; - result.isActor_ = false; - return this; - } - - // required bool isOneWay = 9; - public boolean hasIsOneWay() { - return result.hasIsOneWay(); - } - public boolean getIsOneWay() { - return result.getIsOneWay(); - } - public Builder setIsOneWay(boolean value) { - result.hasIsOneWay = true; - result.isOneWay_ = value; - return this; - } - public Builder clearIsOneWay() { - result.hasIsOneWay = false; - result.isOneWay_ = false; - return this; - } - - // required bool isEscaped = 10; - public boolean hasIsEscaped() { - return result.hasIsEscaped(); - } - public boolean getIsEscaped() { - return result.getIsEscaped(); - } - public Builder setIsEscaped(boolean value) { - result.hasIsEscaped = true; - result.isEscaped_ = value; - return this; - } - public Builder clearIsEscaped() { - result.hasIsEscaped = false; - result.isEscaped_ = false; - return this; - } - - // optional .RemoteActorRefProtocol sender = 11; + // optional .RemoteActorRefProtocol sender = 6; public boolean hasSender() { return result.hasSender(); } @@ -3207,6 +3913,20 @@ public final class RemoteProtocol { public boolean hasPostRestart() { return hasPostRestart; } public java.lang.String getPostRestart() { return postRestart_; } + // optional string init = 4; + public static final int INIT_FIELD_NUMBER = 4; + private boolean hasInit; + private java.lang.String init_ = ""; + public boolean hasInit() { return hasInit; } + public java.lang.String getInit() { return init_; } + + // optional string shutdown = 5; + public static final int SHUTDOWN_FIELD_NUMBER = 5; + private boolean hasShutdown; + private java.lang.String shutdown_ = ""; + public boolean hasShutdown() { return hasShutdown; } + public java.lang.String getShutdown() { return shutdown_; } + private void initFields() { lifeCycle_ = se.scalablesolutions.akka.remote.protocol.RemoteProtocol.LifeCycleType.PERMANENT; } @@ -3227,6 +3947,12 @@ public final class RemoteProtocol { if (hasPostRestart()) { output.writeString(3, getPostRestart()); } + if (hasInit()) { + output.writeString(4, getInit()); + } + if (hasShutdown()) { + output.writeString(5, getShutdown()); + } getUnknownFields().writeTo(output); } @@ -3248,6 +3974,14 @@ public final class RemoteProtocol { size += com.google.protobuf.CodedOutputStream .computeStringSize(3, getPostRestart()); } + if (hasInit()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(4, getInit()); + } + if (hasShutdown()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(5, getShutdown()); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -3415,6 +4149,12 @@ public final class RemoteProtocol { if (other.hasPostRestart()) { setPostRestart(other.getPostRestart()); } + if (other.hasInit()) { + setInit(other.getInit()); + } + if (other.hasShutdown()) { + setShutdown(other.getShutdown()); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -3458,6 +4198,14 @@ public final class RemoteProtocol { setPostRestart(input.readString()); break; } + case 34: { + setInit(input.readString()); + break; + } + case 42: { + setShutdown(input.readString()); + break; + } } } } @@ -3526,6 +4274,48 @@ public final class RemoteProtocol { return this; } + // optional string init = 4; + public boolean hasInit() { + return result.hasInit(); + } + public java.lang.String getInit() { + return result.getInit(); + } + public Builder setInit(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasInit = true; + result.init_ = value; + return this; + } + public Builder clearInit() { + result.hasInit = false; + result.init_ = getDefaultInstance().getInit(); + return this; + } + + // optional string shutdown = 5; + public boolean hasShutdown() { + return result.hasShutdown(); + } + public java.lang.String getShutdown() { + return result.getShutdown(); + } + public Builder setShutdown(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasShutdown = true; + result.shutdown_ = value; + return this; + } + public Builder clearShutdown() { + result.hasShutdown = false; + result.shutdown_ = getDefaultInstance().getShutdown(); + return this; + } + // @@protoc_insertion_point(builder_scope:LifeCycleProtocol) } @@ -4212,6 +5002,16 @@ public final class RemoteProtocol { private static com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_MessageProtocol_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_ActorInfoProtocol_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_ActorInfoProtocol_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_TypedActorInfoProtocol_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_TypedActorInfoProtocol_fieldAccessorTable; private static com.google.protobuf.Descriptors.Descriptor internal_static_RemoteRequestProtocol_descriptor; private static @@ -4262,27 +5062,33 @@ public final class RemoteProtocol { "tProtocol\"r\n\017MessageProtocol\0225\n\023serializ" + "ationScheme\030\001 \002(\0162\030.SerializationSchemeT" + "ype\022\017\n\007message\030\002 \002(\014\022\027\n\017messageManifest\030" + - "\003 \001(\014\"\374\001\n\025RemoteRequestProtocol\022\n\n\002id\030\001 " + - "\002(\004\022!\n\007message\030\002 \002(\0132\020.MessageProtocol\022\016" + - "\n\006method\030\003 \001(\t\022\016\n\006target\030\004 \002(\t\022\014\n\004uuid\030\005" + - " \002(\t\022\017\n\007timeout\030\006 \002(\004\022\026\n\016supervisorUuid\030", - "\007 \001(\t\022\017\n\007isActor\030\010 \002(\010\022\020\n\010isOneWay\030\t \002(\010" + - "\022\021\n\tisEscaped\030\n \002(\010\022\'\n\006sender\030\013 \001(\0132\027.Re" + - "moteActorRefProtocol\"\252\001\n\023RemoteReplyProt" + - "ocol\022\n\n\002id\030\001 \002(\004\022!\n\007message\030\002 \001(\0132\020.Mess" + - "ageProtocol\022%\n\texception\030\003 \001(\0132\022.Excepti" + - "onProtocol\022\026\n\016supervisorUuid\030\004 \001(\t\022\017\n\007is" + - "Actor\030\005 \002(\010\022\024\n\014isSuccessful\030\006 \002(\010\"_\n\021Lif" + - "eCycleProtocol\022!\n\tlifeCycle\030\001 \002(\0162\016.Life" + - "CycleType\022\022\n\npreRestart\030\002 \001(\t\022\023\n\013postRes" + - "tart\030\003 \001(\t\"1\n\017AddressProtocol\022\020\n\010hostnam", - "e\030\001 \002(\t\022\014\n\004port\030\002 \002(\r\"7\n\021ExceptionProtoc" + - "ol\022\021\n\tclassname\030\001 \002(\t\022\017\n\007message\030\002 \002(\t*]" + - "\n\027SerializationSchemeType\022\010\n\004JAVA\020\001\022\013\n\007S" + - "BINARY\020\002\022\016\n\nSCALA_JSON\020\003\022\r\n\tJAVA_JSON\020\004\022" + - "\014\n\010PROTOBUF\020\005*-\n\rLifeCycleType\022\r\n\tPERMAN" + - "ENT\020\001\022\r\n\tTEMPORARY\020\002B-\n)se.scalablesolut" + - "ions.akka.remote.protocolH\001" + "\003 \001(\014\"\222\001\n\021ActorInfoProtocol\022\014\n\004uuid\030\001 \002(" + + "\t\022\016\n\006target\030\002 \002(\t\022\017\n\007timeout\030\003 \002(\004\022\035\n\tac" + + "torType\030\004 \002(\0162\n.ActorType\022/\n\016typedActorI" + + "nfo\030\005 \001(\0132\027.TypedActorInfoProtocol\";\n\026Ty", + "pedActorInfoProtocol\022\021\n\tinterface\030\001 \002(\t\022" + + "\016\n\006method\030\002 \002(\t\"\300\001\n\025RemoteRequestProtoco" + + "l\022\n\n\002id\030\001 \002(\004\022!\n\007message\030\002 \002(\0132\020.Message" + + "Protocol\022%\n\tactorInfo\030\003 \002(\0132\022.ActorInfoP" + + "rotocol\022\020\n\010isOneWay\030\004 \002(\010\022\026\n\016supervisorU" + + "uid\030\005 \001(\t\022\'\n\006sender\030\006 \001(\0132\027.RemoteActorR" + + "efProtocol\"\252\001\n\023RemoteReplyProtocol\022\n\n\002id" + + "\030\001 \002(\004\022!\n\007message\030\002 \001(\0132\020.MessageProtoco" + + "l\022%\n\texception\030\003 \001(\0132\022.ExceptionProtocol" + + "\022\026\n\016supervisorUuid\030\004 \001(\t\022\017\n\007isActor\030\005 \002(", + "\010\022\024\n\014isSuccessful\030\006 \002(\010\"\177\n\021LifeCycleProt" + + "ocol\022!\n\tlifeCycle\030\001 \002(\0162\016.LifeCycleType\022" + + "\022\n\npreRestart\030\002 \001(\t\022\023\n\013postRestart\030\003 \001(\t" + + "\022\014\n\004init\030\004 \001(\t\022\020\n\010shutdown\030\005 \001(\t\"1\n\017Addr" + + "essProtocol\022\020\n\010hostname\030\001 \002(\t\022\014\n\004port\030\002 " + + "\002(\r\"7\n\021ExceptionProtocol\022\021\n\tclassname\030\001 " + + "\002(\t\022\017\n\007message\030\002 \002(\t*=\n\tActorType\022\017\n\013SCA" + + "LA_ACTOR\020\001\022\016\n\nJAVA_ACTOR\020\002\022\017\n\013TYPED_ACTO" + + "R\020\003*]\n\027SerializationSchemeType\022\010\n\004JAVA\020\001" + + "\022\013\n\007SBINARY\020\002\022\016\n\nSCALA_JSON\020\003\022\r\n\tJAVA_JS", + "ON\020\004\022\014\n\010PROTOBUF\020\005*-\n\rLifeCycleType\022\r\n\tP" + + "ERMANENT\020\001\022\r\n\tTEMPORARY\020\002B-\n)se.scalable" + + "solutions.akka.remote.protocolH\001" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -4313,16 +5119,32 @@ public final class RemoteProtocol { new java.lang.String[] { "SerializationScheme", "Message", "MessageManifest", }, se.scalablesolutions.akka.remote.protocol.RemoteProtocol.MessageProtocol.class, se.scalablesolutions.akka.remote.protocol.RemoteProtocol.MessageProtocol.Builder.class); - internal_static_RemoteRequestProtocol_descriptor = + internal_static_ActorInfoProtocol_descriptor = getDescriptor().getMessageTypes().get(3); + internal_static_ActorInfoProtocol_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_ActorInfoProtocol_descriptor, + new java.lang.String[] { "Uuid", "Target", "Timeout", "ActorType", "TypedActorInfo", }, + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.class, + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.Builder.class); + internal_static_TypedActorInfoProtocol_descriptor = + getDescriptor().getMessageTypes().get(4); + internal_static_TypedActorInfoProtocol_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_TypedActorInfoProtocol_descriptor, + new java.lang.String[] { "Interface", "Method", }, + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.class, + se.scalablesolutions.akka.remote.protocol.RemoteProtocol.TypedActorInfoProtocol.Builder.class); + internal_static_RemoteRequestProtocol_descriptor = + getDescriptor().getMessageTypes().get(5); internal_static_RemoteRequestProtocol_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_RemoteRequestProtocol_descriptor, - new java.lang.String[] { "Id", "Message", "Method", "Target", "Uuid", "Timeout", "SupervisorUuid", "IsActor", "IsOneWay", "IsEscaped", "Sender", }, + new java.lang.String[] { "Id", "Message", "ActorInfo", "IsOneWay", "SupervisorUuid", "Sender", }, se.scalablesolutions.akka.remote.protocol.RemoteProtocol.RemoteRequestProtocol.class, se.scalablesolutions.akka.remote.protocol.RemoteProtocol.RemoteRequestProtocol.Builder.class); internal_static_RemoteReplyProtocol_descriptor = - getDescriptor().getMessageTypes().get(4); + getDescriptor().getMessageTypes().get(6); internal_static_RemoteReplyProtocol_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_RemoteReplyProtocol_descriptor, @@ -4330,15 +5152,15 @@ public final class RemoteProtocol { se.scalablesolutions.akka.remote.protocol.RemoteProtocol.RemoteReplyProtocol.class, se.scalablesolutions.akka.remote.protocol.RemoteProtocol.RemoteReplyProtocol.Builder.class); internal_static_LifeCycleProtocol_descriptor = - getDescriptor().getMessageTypes().get(5); + getDescriptor().getMessageTypes().get(7); internal_static_LifeCycleProtocol_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_LifeCycleProtocol_descriptor, - new java.lang.String[] { "LifeCycle", "PreRestart", "PostRestart", }, + new java.lang.String[] { "LifeCycle", "PreRestart", "PostRestart", "Init", "Shutdown", }, se.scalablesolutions.akka.remote.protocol.RemoteProtocol.LifeCycleProtocol.class, se.scalablesolutions.akka.remote.protocol.RemoteProtocol.LifeCycleProtocol.Builder.class); internal_static_AddressProtocol_descriptor = - getDescriptor().getMessageTypes().get(6); + getDescriptor().getMessageTypes().get(8); internal_static_AddressProtocol_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_AddressProtocol_descriptor, @@ -4346,7 +5168,7 @@ public final class RemoteProtocol { se.scalablesolutions.akka.remote.protocol.RemoteProtocol.AddressProtocol.class, se.scalablesolutions.akka.remote.protocol.RemoteProtocol.AddressProtocol.Builder.class); internal_static_ExceptionProtocol_descriptor = - getDescriptor().getMessageTypes().get(7); + getDescriptor().getMessageTypes().get(9); internal_static_ExceptionProtocol_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_ExceptionProtocol_descriptor, diff --git a/akka-core/src/main/protocol/RemoteProtocol.proto b/akka-core/src/main/protocol/RemoteProtocol.proto index c4e5a8157e..6cf9bfd534 100644 --- a/akka-core/src/main/protocol/RemoteProtocol.proto +++ b/akka-core/src/main/protocol/RemoteProtocol.proto @@ -52,21 +52,35 @@ message MessageProtocol { optional bytes messageManifest = 3; } +/** + * Defines the actor info. + */ +message ActorInfoProtocol { + required string uuid = 1; + required string target = 2; + required uint64 timeout = 3; + required ActorType actorType = 4; + optional TypedActorInfoProtocol typedActorInfo = 5; +} + +/** + * Defines the typed actor extra info. + */ +message TypedActorInfoProtocol { + required string interface = 1; + required string method = 2; +} + /** * Defines a remote message request. */ message RemoteRequestProtocol { required uint64 id = 1; required MessageProtocol message = 2; - optional string method = 3; - required string target = 4; - required string uuid = 5; - required uint64 timeout = 6; - optional string supervisorUuid = 7; - required bool isActor = 8; - required bool isOneWay = 9; - required bool isEscaped = 10; - optional RemoteActorRefProtocol sender = 11; + required ActorInfoProtocol actorInfo = 3; + required bool isOneWay = 4; + optional string supervisorUuid = 5; + optional RemoteActorRefProtocol sender = 6; } /** @@ -81,6 +95,15 @@ message RemoteReplyProtocol { required bool isSuccessful = 6; } +/** + * Defines the actor type. + */ +enum ActorType { + SCALA_ACTOR = 1; + JAVA_ACTOR = 2; + TYPED_ACTOR = 3; +} + /** * Defines the serialization scheme used to serialize the message and/or Actor instance. */ @@ -115,8 +138,6 @@ enum DispatcherType { */ message LifeCycleProtocol { required LifeCycleType lifeCycle = 1; - optional string preRestart = 2; - optional string postRestart = 3; } /** diff --git a/akka-core/src/main/resources/META-INF/aop.xml b/akka-core/src/main/resources/META-INF/aop.xml index 2f8d5159a8..bdc167ca54 100644 --- a/akka-core/src/main/resources/META-INF/aop.xml +++ b/akka-core/src/main/resources/META-INF/aop.xml @@ -1,7 +1,7 @@ - + diff --git a/akka-core/src/main/resources/logback.xml b/akka-core/src/main/resources/logback.xml new file mode 100755 index 0000000000..4635396601 --- /dev/null +++ b/akka-core/src/main/resources/logback.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + [%4p] [%d{ISO8601}] [%t] %c{1}: %m%n + + + + ./logs/akka.log + + [%4p] [%d{ISO8601}] [%t] %c{1}: %m%n + + + ./logs/akka.log.%d{yyyy-MM-dd-HH} + + + + + + + + diff --git a/akka-core/src/main/scala/actor/ActiveObject.scala b/akka-core/src/main/scala/actor/ActiveObject.scala deleted file mode 100644 index b83816f4d2..0000000000 --- a/akka-core/src/main/scala/actor/ActiveObject.scala +++ /dev/null @@ -1,852 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.actor - -import Actor._ -import se.scalablesolutions.akka.config.FaultHandlingStrategy -import se.scalablesolutions.akka.remote.protocol.RemoteProtocol.RemoteRequestProtocol -import se.scalablesolutions.akka.remote.{MessageSerializer, RemoteClient, RemoteRequestProtocolIdFactory} -import se.scalablesolutions.akka.dispatch.{MessageDispatcher, Future, CompletableFuture} -import se.scalablesolutions.akka.config.ScalaConfig._ -import se.scalablesolutions.akka.serialization.Serializer -import se.scalablesolutions.akka.util._ - -import org.codehaus.aspectwerkz.joinpoint.{MethodRtti, JoinPoint} -import org.codehaus.aspectwerkz.proxy.Proxy -import org.codehaus.aspectwerkz.annotation.{Aspect, Around} - -import java.net.InetSocketAddress -import java.lang.reflect.{InvocationTargetException, Method} - -object Annotations { - import se.scalablesolutions.akka.actor.annotation._ - val transactionrequired = classOf[transactionrequired] - val prerestart = classOf[prerestart] - val postrestart = classOf[postrestart] - val shutdown = classOf[shutdown] - val inittransactionalstate = classOf[inittransactionalstate] -} - -/** - * Configuration factory for Active Objects. - * - * FIXDOC: document ActiveObjectConfiguration - */ -final class ActiveObjectConfiguration { - private[akka] var _timeout: Long = Actor.TIMEOUT - private[akka] var _restartCallbacks: Option[RestartCallbacks] = None - private[akka] var _shutdownCallback: Option[ShutdownCallback] = None - private[akka] var _transactionRequired = false - private[akka] var _host: Option[InetSocketAddress] = None - private[akka] var _messageDispatcher: Option[MessageDispatcher] = None - - def timeout(timeout: Long) : ActiveObjectConfiguration = { - _timeout = timeout - this - } - - def restartCallbacks(pre: String, post: String) : ActiveObjectConfiguration = { - _restartCallbacks = Some(new RestartCallbacks(pre, post)) - this - } - - def shutdownCallback(down: String) : ActiveObjectConfiguration = { - _shutdownCallback = Some(new ShutdownCallback(down)) - this - } - - def makeTransactionRequired() : ActiveObjectConfiguration = { - _transactionRequired = true; - this - } - - def makeRemote(hostname: String, port: Int) : ActiveObjectConfiguration = { - _host = Some(new InetSocketAddress(hostname, port)) - this - } - - def dispatcher(messageDispatcher: MessageDispatcher) : ActiveObjectConfiguration = { - _messageDispatcher = Some(messageDispatcher) - this - } -} - -/** - * Holds RTTI (runtime type information) for the Active Object, f.e. current 'sender' - * reference, the 'senderFuture' reference etc. - *

- * In order to make use of this context you have to create a member field in your - * Active Object that has the type 'ActiveObjectContext', then an instance will - * be injected for you to use. - *

- * This class does not contain static information but is updated by the runtime system - * at runtime. - *

- * Here is an example of usage: - *

- * class Ping {
- *   // This context will be injected, holds RTTI (runtime type information)
- *   // for the current message send
- *   private ActiveObjectContext context = null;
- *
- *   public void hit(int count) {
- *     Pong pong = (Pong) context.getSender();
- *     pong.hit(count++)
- *   }
- * }
- * 
- * - * @author Jonas Bonér - */ -final class ActiveObjectContext { - private[akka] var _sender: AnyRef = _ - private[akka] var _senderFuture: CompletableFuture[Any] = _ - - /** - * Returns the current sender Active Object reference. - * Scala style getter. - */ - def sender: AnyRef = { - if (_sender eq null) throw new IllegalActorStateException("Sender reference should not be null.") - else _sender - } - - /** - * Returns the current sender Active Object reference. - * Java style getter. - */ - def getSender: AnyRef = { - if (_sender eq null) throw new IllegalActorStateException("Sender reference should not be null.") - else _sender - } - - /** - * Returns the current sender future Active Object reference. - * Scala style getter. - */ - def senderFuture: Option[CompletableFuture[Any]] = if (_senderFuture eq null) None else Some(_senderFuture) - - /** - * Returns the current sender future Active Object reference. - * Java style getter. - * This method returns 'null' if the sender future is not available. - */ - def getSenderFuture = _senderFuture -} - -/** - * Internal helper class to help pass the contextual information between threads. - * - * @author Jonas Bonér - */ -private[akka] object ActiveObjectContext { - import scala.util.DynamicVariable - private[actor] val sender = new DynamicVariable[AnyRef](null) - private[actor] val senderFuture = new DynamicVariable[CompletableFuture[Any]](null) -} - -/** - * Factory class for creating Active Objects out of plain POJOs and/or POJOs with interfaces. - * - * @author Jonas Bonér - */ -object ActiveObject extends Logging { - import Actor.actorOf - - val AKKA_CAMEL_ROUTING_SCHEME = "akka" - private[actor] val AW_PROXY_PREFIX = "$$ProxiedByAW".intern - - def newInstance[T](target: Class[T], timeout: Long): T = - newInstance(target, actorOf(new Dispatcher(false)), None, timeout) - - def newInstance[T](target: Class[T]): T = - newInstance(target, actorOf(new Dispatcher(false)), None, Actor.TIMEOUT) - - def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long): T = - newInstance(intf, target, actorOf(new Dispatcher(false)), None, timeout) - - def newInstance[T](intf: Class[T], target: AnyRef): T = - newInstance(intf, target, actorOf(new Dispatcher(false)), None, Actor.TIMEOUT) - - def newRemoteInstance[T](target: Class[T], timeout: Long, hostname: String, port: Int): T = - newInstance(target, actorOf(new Dispatcher(false)), Some(new InetSocketAddress(hostname, port)), timeout) - - def newRemoteInstance[T](target: Class[T], hostname: String, port: Int): T = - newInstance(target, actorOf(new Dispatcher(false)), Some(new InetSocketAddress(hostname, port)), Actor.TIMEOUT) - - def newInstance[T](target: Class[T], config: ActiveObjectConfiguration): T = { - val actor = actorOf(new Dispatcher(config._transactionRequired, config._restartCallbacks, config._shutdownCallback)) - if (config._messageDispatcher.isDefined) { - actor.dispatcher = config._messageDispatcher.get - } - newInstance(target, actor, config._host, config._timeout) - } - - def newInstance[T](intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration): T = { - val actor = actorOf(new Dispatcher(config._transactionRequired, config._restartCallbacks, config._shutdownCallback)) - if (config._messageDispatcher.isDefined) { - actor.dispatcher = config._messageDispatcher.get - } - newInstance(intf, target, actor, config._host, config._timeout) - } - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newInstance[T](target: Class[T], timeout: Long, restartCallbacks: Option[RestartCallbacks]): T = - newInstance(target, actorOf(new Dispatcher(false, restartCallbacks)), None, timeout) - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, restartCallbacks: Option[RestartCallbacks]): T = - newInstance(intf, target, actorOf(new Dispatcher(false, restartCallbacks)), None, timeout) - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean): T = - newInstance(target, actorOf(new Dispatcher(transactionRequired, None)), None, timeout) - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, restartCallbacks: Option[RestartCallbacks]): T = - newInstance(target, actorOf(new Dispatcher(transactionRequired, restartCallbacks)), None, timeout) - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean): T = - newInstance(intf, target, actorOf(new Dispatcher(transactionRequired, None)), None, timeout) - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean, restartCallbacks: Option[RestartCallbacks]): T = - newInstance(intf, target, actorOf(new Dispatcher(transactionRequired, restartCallbacks)), None, timeout) - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, hostname: String, port: Int): T = - newInstance(intf, target, actorOf(new Dispatcher(false, None)), Some(new InetSocketAddress(hostname, port)), timeout) - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T = - newInstance(intf, target, actorOf(new Dispatcher(false, restartCallbacks)), Some(new InetSocketAddress(hostname, port)), timeout) - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, hostname: String, port: Int): T = - newInstance(target, actorOf(new Dispatcher(transactionRequired, None)), Some(new InetSocketAddress(hostname, port)), timeout) - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T = - newInstance(target, actorOf(new Dispatcher(transactionRequired, restartCallbacks)), Some(new InetSocketAddress(hostname, port)), timeout) - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean, hostname: String, port: Int): T = - newInstance(intf, target, actorOf(new Dispatcher(transactionRequired, None)), Some(new InetSocketAddress(hostname, port)), timeout) - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean, hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T = - newInstance(intf, target, actorOf(new Dispatcher(transactionRequired, restartCallbacks)), Some(new InetSocketAddress(hostname, port)), timeout) - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newInstance[T](target: Class[T], timeout: Long, dispatcher: MessageDispatcher): T = { - val actor = actorOf(new Dispatcher(false, None)) - actor.dispatcher = dispatcher - newInstance(target, actor, None, timeout) - } - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newInstance[T](target: Class[T], timeout: Long, dispatcher: MessageDispatcher, restartCallbacks: Option[RestartCallbacks]): T = { - val actor = actorOf(new Dispatcher(false, restartCallbacks)) - actor.dispatcher = dispatcher - newInstance(target, actor, None, timeout) - } - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, dispatcher: MessageDispatcher): T = { - val actor = actorOf(new Dispatcher(false, None)) - actor.dispatcher = dispatcher - newInstance(intf, target, actor, None, timeout) - } - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, - dispatcher: MessageDispatcher, restartCallbacks: Option[RestartCallbacks]): T = { - val actor = actorOf(new Dispatcher(false, restartCallbacks)) - actor.dispatcher = dispatcher - newInstance(intf, target, actor, None, timeout) - } - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, dispatcher: MessageDispatcher): T = { - val actor = actorOf(new Dispatcher(transactionRequired, None)) - actor.dispatcher = dispatcher - newInstance(target, actor, None, timeout) - } - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, - dispatcher: MessageDispatcher, restartCallbacks: Option[RestartCallbacks]): T = { - val actor = actorOf(new Dispatcher(transactionRequired, restartCallbacks)) - actor.dispatcher = dispatcher - newInstance(target, actor, None, timeout) - } - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean, dispatcher: MessageDispatcher): T = { - val actor = actorOf(new Dispatcher(transactionRequired, None)) - actor.dispatcher = dispatcher - newInstance(intf, target, actor, None, timeout) - } - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean, - dispatcher: MessageDispatcher, restartCallbacks: Option[RestartCallbacks]): T = { - val actor = actorOf(new Dispatcher(transactionRequired, restartCallbacks)) - actor.dispatcher = dispatcher - newInstance(intf, target, actor, None, timeout) - } - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](target: Class[T], timeout: Long, dispatcher: MessageDispatcher, hostname: String, port: Int): T = { - val actor = actorOf(new Dispatcher(false, None)) - actor.dispatcher = dispatcher - newInstance(target, actor, Some(new InetSocketAddress(hostname, port)), timeout) - } - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](target: Class[T], timeout: Long, dispatcher: MessageDispatcher, - hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T = { - val actor = actorOf(new Dispatcher(false, restartCallbacks)) - actor.dispatcher = dispatcher - newInstance(target, actor, Some(new InetSocketAddress(hostname, port)), timeout) - } - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, dispatcher: MessageDispatcher, hostname: String, port: Int): T = { - val actor = actorOf(new Dispatcher(false, None)) - actor.dispatcher = dispatcher - newInstance(intf, target, actor, Some(new InetSocketAddress(hostname, port)), timeout) - } - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, dispatcher: MessageDispatcher, - hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T = { - val actor = actorOf(new Dispatcher(false, restartCallbacks)) - actor.dispatcher = dispatcher - newInstance(intf, target, actor, Some(new InetSocketAddress(hostname, port)), timeout) - } - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, - dispatcher: MessageDispatcher, hostname: String, port: Int): T = { - val actor = actorOf(new Dispatcher(transactionRequired, None)) - actor.dispatcher = dispatcher - newInstance(target, actor, Some(new InetSocketAddress(hostname, port)), timeout) - } - - @deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, dispatcher: MessageDispatcher, - hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T = { - val actor = actorOf(new Dispatcher(transactionRequired, restartCallbacks)) - actor.dispatcher = dispatcher - newInstance(target, actor, Some(new InetSocketAddress(hostname, port)), timeout) - } - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean, - dispatcher: MessageDispatcher, hostname: String, port: Int): T = { - val actor = actorOf(new Dispatcher(transactionRequired, None)) - actor.dispatcher = dispatcher - newInstance(intf, target, actor, Some(new InetSocketAddress(hostname, port)), timeout) - } - - @deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead") - def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean, - dispatcher: MessageDispatcher, hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T = { - val actor = actorOf(new Dispatcher(transactionRequired, restartCallbacks)) - actor.dispatcher = dispatcher - newInstance(intf, target, actor, Some(new InetSocketAddress(hostname, port)), timeout) - } - - private[akka] def newInstance[T](target: Class[T], actorRef: ActorRef, remoteAddress: Option[InetSocketAddress], timeout: Long): T = { - val proxy = Proxy.newInstance(target, true, false) - val context = injectActiveObjectContext(proxy) - actorRef.actor.asInstanceOf[Dispatcher].initialize(target, proxy, context) - actorRef.timeout = timeout - if (remoteAddress.isDefined) actorRef.makeRemote(remoteAddress.get) - AspectInitRegistry.register(proxy, AspectInit(target, actorRef, remoteAddress, timeout)) - actorRef.start - proxy.asInstanceOf[T] - } - - private[akka] def newInstance[T](intf: Class[T], target: AnyRef, actorRef: ActorRef, - remoteAddress: Option[InetSocketAddress], timeout: Long): T = { - val context = injectActiveObjectContext(target) - val proxy = Proxy.newInstance(Array(intf), Array(target), true, false) - actorRef.actor.asInstanceOf[Dispatcher].initialize(target.getClass, target, context) - actorRef.timeout = timeout - if (remoteAddress.isDefined) actorRef.makeRemote(remoteAddress.get) - AspectInitRegistry.register(proxy, AspectInit(intf, actorRef, remoteAddress, timeout)) - actorRef.start - proxy.asInstanceOf[T] - } - - def stop(obj: AnyRef): Unit = { - val init = AspectInitRegistry.initFor(obj) - init.actorRef.stop - } - - /** - * Get the underlying dispatcher actor for the given active object. - */ - def actorFor(obj: AnyRef): Option[ActorRef] = - ActorRegistry.actorsFor(classOf[Dispatcher]).find(a => a.actor.asInstanceOf[Dispatcher].target == Some(obj)) - - /** - * Links an other active object to this active object. - * @param supervisor the supervisor active object - * @param supervised the active object to link - */ - def link(supervisor: AnyRef, supervised: AnyRef) = { - val supervisorActor = actorFor(supervisor).getOrElse( - throw new IllegalActorStateException("Can't link when the supervisor is not an active object")) - val supervisedActor = actorFor(supervised).getOrElse( - throw new IllegalActorStateException("Can't link when the supervised is not an active object")) - supervisorActor.link(supervisedActor) - } - - /** - * Links an other active object to this active object and sets the fault handling for the supervisor. - * @param supervisor the supervisor active object - * @param supervised the active object to link - * @param handler fault handling strategy - * @param trapExceptions array of exceptions that should be handled by the supervisor - */ - def link(supervisor: AnyRef, supervised: AnyRef, handler: FaultHandlingStrategy, trapExceptions: Array[Class[_ <: Throwable]]) = { - val supervisorActor = actorFor(supervisor).getOrElse( - throw new IllegalActorStateException("Can't link when the supervisor is not an active object")) - val supervisedActor = actorFor(supervised).getOrElse( - throw new IllegalActorStateException("Can't link when the supervised is not an active object")) - supervisorActor.trapExit = trapExceptions.toList - supervisorActor.faultHandler = Some(handler) - supervisorActor.link(supervisedActor) - } - - /** - * Unlink the supervised active object from the supervisor. - * @param supervisor the supervisor active object - * @param supervised the active object to unlink - */ - def unlink(supervisor: AnyRef, supervised: AnyRef) = { - val supervisorActor = actorFor(supervisor).getOrElse( - throw new IllegalActorStateException("Can't unlink when the supervisor is not an active object")) - val supervisedActor = actorFor(supervised).getOrElse( - throw new IllegalActorStateException("Can't unlink when the supervised is not an active object")) - supervisorActor.unlink(supervisedActor) - } - - /** - * Sets the trap exit for the given supervisor active object. - * @param supervisor the supervisor active object - * @param trapExceptions array of exceptions that should be handled by the supervisor - */ - def trapExit(supervisor: AnyRef, trapExceptions: Array[Class[_ <: Throwable]]) = { - val supervisorActor = actorFor(supervisor).getOrElse( - throw new IllegalActorStateException("Can't set trap exceptions when the supervisor is not an active object")) - supervisorActor.trapExit = trapExceptions.toList - this - } - - /** - * Sets the fault handling strategy for the given supervisor active object. - * @param supervisor the supervisor active object - * @param handler fault handling strategy - */ - def faultHandler(supervisor: AnyRef, handler: FaultHandlingStrategy) = { - val supervisorActor = actorFor(supervisor).getOrElse( - throw new IllegalActorStateException("Can't set fault handler when the supervisor is not an active object")) - supervisorActor.faultHandler = Some(handler) - this - } - - private def injectActiveObjectContext(activeObject: AnyRef): Option[ActiveObjectContext] = { - def injectActiveObjectContext0(activeObject: AnyRef, clazz: Class[_]): Option[ActiveObjectContext] = { - val contextField = clazz.getDeclaredFields.toList.find(_.getType == classOf[ActiveObjectContext]) - if (contextField.isDefined) { - contextField.get.setAccessible(true) - val context = new ActiveObjectContext - contextField.get.set(activeObject, context) - Some(context) - } else { - val parent = clazz.getSuperclass - if (parent != null) injectActiveObjectContext0(activeObject, parent) - else { - log.ifTrace("Can't set 'ActiveObjectContext' for ActiveObject [" + - activeObject.getClass.getName + - "] since no field of this type could be found.") - None - } - } - } - injectActiveObjectContext0(activeObject, activeObject.getClass) - } - - private[akka] def supervise(restartStrategy: RestartStrategy, components: List[Supervise]): Supervisor = - Supervisor(SupervisorConfig(restartStrategy, components)) -} - -private[akka] object AspectInitRegistry extends ListenerManagement { - private val initializations = new java.util.concurrent.ConcurrentHashMap[AnyRef, AspectInit] - - def initFor(target: AnyRef) = { - initializations.get(target) - } - - def register(target: AnyRef, init: AspectInit) = { - val res = initializations.put(target, init) - foreachListener(_ ! AspectInitRegistered(target, init)) - res - } - - def unregister(target: AnyRef) = { - val res = initializations.remove(target) - foreachListener(_ ! AspectInitUnregistered(target, res)) - res - } -} - -private[akka] sealed trait AspectInitRegistryEvent -private[akka] case class AspectInitRegistered(proxy: AnyRef, init: AspectInit) extends AspectInitRegistryEvent -private[akka] case class AspectInitUnregistered(proxy: AnyRef, init: AspectInit) extends AspectInitRegistryEvent - -private[akka] sealed case class AspectInit( - val target: Class[_], - val actorRef: ActorRef, - val remoteAddress: Option[InetSocketAddress], - val timeout: Long) { - def this(target: Class[_], actorRef: ActorRef, timeout: Long) = this(target, actorRef, None, timeout) -} - -/** - * AspectWerkz Aspect that is turning POJOs into Active Object. - * Is deployed on a 'per-instance' basis. - * - * @author Jonas Bonér - */ -@Aspect("perInstance") -private[akka] sealed class ActiveObjectAspect { - @volatile private var isInitialized = false - @volatile private var isStopped = false - private var target: Class[_] = _ - private var actorRef: ActorRef = _ - private var remoteAddress: Option[InetSocketAddress] = _ - private var timeout: Long = _ - @volatile private var instance: AnyRef = _ - - @Around("execution(* *.*(..))") - def invoke(joinPoint: JoinPoint): AnyRef = { - if (!isInitialized) { - val init = AspectInitRegistry.initFor(joinPoint.getThis) - target = init.target - actorRef = init.actorRef - remoteAddress = init.remoteAddress - timeout = init.timeout - isInitialized = true - - } - dispatch(joinPoint) - } - - private def dispatch(joinPoint: JoinPoint) = { - if (remoteAddress.isDefined) remoteDispatch(joinPoint) - else localDispatch(joinPoint) - } - - private def localDispatch(joinPoint: JoinPoint): AnyRef = { - val rtti = joinPoint.getRtti.asInstanceOf[MethodRtti] - val isOneWay = isVoid(rtti) - val sender = ActiveObjectContext.sender.value - val senderFuture = ActiveObjectContext.senderFuture.value - - if (!actorRef.isRunning && !isStopped) { - isStopped = true - joinPoint.proceed - } else if (isOneWay) { - actorRef ! Invocation(joinPoint, true, true, sender, senderFuture) - null.asInstanceOf[AnyRef] - } else { - val result = (actorRef !! (Invocation(joinPoint, false, isOneWay, sender, senderFuture), timeout)).as[AnyRef] - if (result.isDefined) result.get - else throw new IllegalActorStateException("No result defined for invocation [" + joinPoint + "]") - } - } - - private def remoteDispatch(joinPoint: JoinPoint): AnyRef = { - val rtti = joinPoint.getRtti.asInstanceOf[MethodRtti] - val isOneWay = isVoid(rtti) - val (message: Array[AnyRef], isEscaped) = escapeArguments(rtti.getParameterValues) - val requestBuilder = RemoteRequestProtocol.newBuilder - .setId(RemoteRequestProtocolIdFactory.nextId) - .setMessage(MessageSerializer.serialize(message)) - .setMethod(rtti.getMethod.getName) - .setTarget(target.getName) - .setUuid(actorRef.uuid) - .setTimeout(timeout) - .setIsActor(false) - .setIsOneWay(isOneWay) - .setIsEscaped(false) - val id = actorRef.registerSupervisorAsRemoteActor - if (id.isDefined) requestBuilder.setSupervisorUuid(id.get) - val remoteMessage = requestBuilder.build - val future = RemoteClient.clientFor(remoteAddress.get).send(remoteMessage, None) - if (isOneWay) null // for void methods - else { - if (future.isDefined) { - future.get.await - val result = getResultOrThrowException(future.get) - if (result.isDefined) result.get - else throw new IllegalActorStateException("No result returned from call to [" + joinPoint + "]") - } else throw new IllegalActorStateException("No future returned from call to [" + joinPoint + "]") - } - } - - private def getResultOrThrowException[T](future: Future[T]): Option[T] = - if (future.exception.isDefined) { - val (_, cause) = future.exception.get - throw cause - } else future.result - - private def isVoid(rtti: MethodRtti) = rtti.getMethod.getReturnType == java.lang.Void.TYPE - - private def escapeArguments(args: Array[AnyRef]): Tuple2[Array[AnyRef], Boolean] = { - var isEscaped = false - val escapedArgs = for (arg <- args) yield { - val clazz = arg.getClass - if (clazz.getName.contains(ActiveObject.AW_PROXY_PREFIX)) { - isEscaped = true - ActiveObject.AW_PROXY_PREFIX + clazz.getSuperclass.getName - } else arg - } - (escapedArgs, isEscaped) - } -} - -/** - * Represents a snapshot of the current invocation. - * - * @author Jonas Bonér - */ -@serializable private[akka] case class Invocation( - joinPoint: JoinPoint, isOneWay: Boolean, isVoid: Boolean, sender: AnyRef, senderFuture: CompletableFuture[Any]) { - - override def toString: String = synchronized { - "Invocation [" + - "\n\t\tmethod = " + joinPoint.getRtti.asInstanceOf[MethodRtti].getMethod.getName + " @ " + joinPoint.getTarget.getClass.getName + - "\n\t\tisOneWay = " + isOneWay + - "\n\t\tisVoid = " + isVoid + - "\n\t\tsender = " + sender + - "\n\t\tsenderFuture = " + senderFuture + - "]" - } - - override def hashCode: Int = synchronized { - var result = HashCode.SEED - result = HashCode.hash(result, joinPoint) - result = HashCode.hash(result, isOneWay) - result = HashCode.hash(result, isVoid) - result = HashCode.hash(result, sender) - result = HashCode.hash(result, senderFuture) - result - } - - override def equals(that: Any): Boolean = synchronized { - that != null && - that.isInstanceOf[Invocation] && - that.asInstanceOf[Invocation].joinPoint == joinPoint && - that.asInstanceOf[Invocation].isOneWay == isOneWay && - that.asInstanceOf[Invocation].isVoid == isVoid && - that.asInstanceOf[Invocation].sender == sender && - that.asInstanceOf[Invocation].senderFuture == senderFuture - } -} - -object Dispatcher { - val ZERO_ITEM_CLASS_ARRAY = Array[Class[_]]() - val ZERO_ITEM_OBJECT_ARRAY = Array[Object]() - var crashedActorTl:ThreadLocal[Dispatcher] = new ThreadLocal(); -} - -/** - * Generic Actor managing Invocation dispatch, transaction and error management. - * - * @author Jonas Bonér - */ -private[akka] class Dispatcher(transactionalRequired: Boolean, - var restartCallbacks: Option[RestartCallbacks] = None, - var shutdownCallback: Option[ShutdownCallback] = None) extends Actor { - import Dispatcher._ - - private[actor] var target: Option[AnyRef] = None - private var zhutdown: Option[Method] = None - private var preRestart: Option[Method] = None - private var postRestart: Option[Method] = None - private var initTxState: Option[Method] = None - private var context: Option[ActiveObjectContext] = None - private var targetClass:Class[_] = _ - - def this(transactionalRequired: Boolean) = this(transactionalRequired,None) - - private[actor] def initialize(targetClass: Class[_], targetInstance: AnyRef, ctx: Option[ActiveObjectContext]) = { - - if (transactionalRequired || targetClass.isAnnotationPresent(Annotations.transactionrequired)) - self.makeTransactionRequired - self.id = targetClass.getName - this.targetClass = targetClass - target = Some(targetInstance) - context = ctx - val methods = targetInstance.getClass.getDeclaredMethods.toList - - if (self.lifeCycle.isEmpty) self.lifeCycle = Some(LifeCycle(Permanent)) - - // See if we have any config define restart callbacks - restartCallbacks match { - case None => {} - case Some(RestartCallbacks(pre, post)) => - preRestart = Some(try { - targetInstance.getClass.getDeclaredMethod(pre, ZERO_ITEM_CLASS_ARRAY: _*) - } catch { case e => throw new IllegalActorStateException( - "Could not find pre restart method [" + pre + "] \nin [" + - targetClass.getName + "]. \nIt must have a zero argument definition.") }) - postRestart = Some(try { - targetInstance.getClass.getDeclaredMethod(post, ZERO_ITEM_CLASS_ARRAY: _*) - } catch { case e => throw new IllegalActorStateException( - "Could not find post restart method [" + post + "] \nin [" + - targetClass.getName + "]. \nIt must have a zero argument definition.") }) - } - // See if we have any config define a shutdown callback - shutdownCallback match { - case None => {} - case Some(ShutdownCallback(down)) => - zhutdown = Some(try { - targetInstance.getClass.getDeclaredMethod(down, ZERO_ITEM_CLASS_ARRAY: _*) - } catch { case e => throw new IllegalStateException( - "Could not find shutdown method [" + down + "] \nin [" + - targetClass.getName + "]. \nIt must have a zero argument definition.") }) - } - - // See if we have any annotation defined restart callbacks - if (!preRestart.isDefined) preRestart = methods.find(m => m.isAnnotationPresent(Annotations.prerestart)) - if (!postRestart.isDefined) postRestart = methods.find(m => m.isAnnotationPresent(Annotations.postrestart)) - // See if we have an annotation defined shutdown callback - if (!zhutdown.isDefined) zhutdown = methods.find(m => m.isAnnotationPresent(Annotations.shutdown)) - - if (preRestart.isDefined && preRestart.get.getParameterTypes.length != 0) - throw new IllegalActorStateException( - "Method annotated with @prerestart or defined as a restart callback in \n[" + - targetClass.getName + "] must have a zero argument definition") - if (postRestart.isDefined && postRestart.get.getParameterTypes.length != 0) - throw new IllegalActorStateException( - "Method annotated with @postrestart or defined as a restart callback in \n[" + - targetClass.getName + "] must have a zero argument definition") - if (zhutdown.isDefined && zhutdown.get.getParameterTypes.length != 0) - throw new IllegalStateException( - "Method annotated with @shutdown or defined as a shutdown callback in \n[" + - targetClass.getName + "] must have a zero argument definition") - - if (preRestart.isDefined) preRestart.get.setAccessible(true) - if (postRestart.isDefined) postRestart.get.setAccessible(true) - if (zhutdown.isDefined) zhutdown.get.setAccessible(true) - - // see if we have a method annotated with @inittransactionalstate, if so invoke it - initTxState = methods.find(m => m.isAnnotationPresent(Annotations.inittransactionalstate)) - if (initTxState.isDefined && initTxState.get.getParameterTypes.length != 0) - throw new IllegalActorStateException("Method annotated with @inittransactionalstate must have a zero argument definition") - if (initTxState.isDefined) initTxState.get.setAccessible(true) - } - - def receive = { - case invocation @ Invocation(joinPoint, isOneWay, _, sender, senderFuture) => - ActiveObject.log.ifTrace("Invoking active object with message:\n" + invocation) - context.foreach { ctx => - if (sender ne null) ctx._sender = sender - if (senderFuture ne null) ctx._senderFuture = senderFuture - } - ActiveObjectContext.sender.value = joinPoint.getThis // set next sender - self.senderFuture.foreach(ActiveObjectContext.senderFuture.value = _) - if (Actor.SERIALIZE_MESSAGES) serializeArguments(joinPoint) - if (isOneWay) joinPoint.proceed - else self.reply(joinPoint.proceed) - - // Jan Kronquist: started work on issue 121 - case Link(target) => self.link(target) - case Unlink(target) => self.unlink(target) - case unexpected => throw new IllegalActorStateException( - "Unexpected message [" + unexpected + "] sent to [" + this + "]") - } - - override def preRestart(reason: Throwable) { - try { - // Since preRestart is called we know that this dispatcher - // is about to be restarted. Put the instance in a thread - // local so the new dispatcher can be initialized with the - // contents of the old. - //FIXME - This should be considered as a workaround. - crashedActorTl.set(this) - preRestart.foreach(_.invoke(target.get, ZERO_ITEM_OBJECT_ARRAY: _*)) - } catch { case e: InvocationTargetException => throw e.getCause } - } - - override def postRestart(reason: Throwable) { - try { - postRestart.foreach(_.invoke(target.get, ZERO_ITEM_OBJECT_ARRAY: _*)) - } catch { case e: InvocationTargetException => throw e.getCause } - } - - override def init = { - // Get the crashed dispatcher from thread local and intitialize this actor with the - // contents of the old dispatcher - val oldActor = crashedActorTl.get(); - if (oldActor != null) { - initialize(oldActor.targetClass, oldActor.target.get, oldActor.context) - crashedActorTl.set(null) - } - } - - override def shutdown = { - try { - zhutdown.foreach(_.invoke(target.get, ZERO_ITEM_OBJECT_ARRAY: _*)) - } catch { case e: InvocationTargetException => throw e.getCause - } finally { - AspectInitRegistry.unregister(target.get); - } - } - - override def initTransactionalState = { - try { - if (initTxState.isDefined && target.isDefined) initTxState.get.invoke(target.get, ZERO_ITEM_OBJECT_ARRAY: _*) - } catch { case e: InvocationTargetException => throw e.getCause } - } - - private def serializeArguments(joinPoint: JoinPoint) = { - val args = joinPoint.getRtti.asInstanceOf[MethodRtti].getParameterValues - var unserializable = false - var hasMutableArgument = false - for (arg <- args.toList) { - if (!arg.isInstanceOf[String] && - !arg.isInstanceOf[Byte] && - !arg.isInstanceOf[Int] && - !arg.isInstanceOf[Long] && - !arg.isInstanceOf[Float] && - !arg.isInstanceOf[Double] && - !arg.isInstanceOf[Boolean] && - !arg.isInstanceOf[Char] && - !arg.isInstanceOf[java.lang.Byte] && - !arg.isInstanceOf[java.lang.Integer] && - !arg.isInstanceOf[java.lang.Long] && - !arg.isInstanceOf[java.lang.Float] && - !arg.isInstanceOf[java.lang.Double] && - !arg.isInstanceOf[java.lang.Boolean] && - !arg.isInstanceOf[java.lang.Character]) { - hasMutableArgument = true - } - if (arg.getClass.getName.contains(ActiveObject.AW_PROXY_PREFIX)) unserializable = true - } - if (!unserializable && hasMutableArgument) { - val copyOfArgs = Serializer.Java.deepClone(args) - joinPoint.getRtti.asInstanceOf[MethodRtti].setParameterValues(copyOfArgs.asInstanceOf[Array[AnyRef]]) - } - } -} diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala index 76adf9c729..4e22e5db0b 100644 --- a/akka-core/src/main/scala/actor/Actor.scala +++ b/akka-core/src/main/scala/actor/Actor.scala @@ -9,10 +9,15 @@ import se.scalablesolutions.akka.config.Config._ import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.serialization.Serializer import se.scalablesolutions.akka.util.Helpers.{narrow, narrowSilently} -import se.scalablesolutions.akka.util.Logging +import se.scalablesolutions.akka.util.{Logging, Duration} +import se.scalablesolutions.akka.AkkaException import com.google.protobuf.Message + import java.util.concurrent.TimeUnit +import java.net.InetSocketAddress + +import scala.reflect.BeanProperty /** * Implements the Transactor abstraction. E.g. a transactional actor. @@ -32,29 +37,42 @@ trait Transactor extends Actor { * * @author Jonas Bonér */ -abstract class RemoteActor(hostname: String, port: Int) extends Actor { - self.makeRemote(hostname, port) +abstract class RemoteActor(address: InetSocketAddress) extends Actor { + def this(hostname: String, port: Int) = this(new InetSocketAddress(hostname, port)) + self.makeRemote(address) } /** * Life-cycle messages for the Actors */ @serializable sealed trait LifeCycleMessage + case class HotSwap(code: Option[Actor.Receive]) extends LifeCycleMessage + case class Restart(reason: Throwable) extends LifeCycleMessage + case class Exit(dead: ActorRef, killer: Throwable) extends LifeCycleMessage + case class Link(child: ActorRef) extends LifeCycleMessage + case class Unlink(child: ActorRef) extends LifeCycleMessage + case class UnlinkAndStop(child: ActorRef) extends LifeCycleMessage + case object ReceiveTimeout extends LifeCycleMessage + case class MaximumNumberOfRestartsWithinTimeRangeReached( - victim: ActorRef, maxNrOfRetries: Int, withinTimeRange: Int, lastExceptionCausingRestart: Throwable) extends LifeCycleMessage + @BeanProperty val victim: ActorRef, + @BeanProperty val maxNrOfRetries: Int, + @BeanProperty val withinTimeRange: Int, + @BeanProperty val lastExceptionCausingRestart: Throwable) extends LifeCycleMessage // Exceptions for Actors -class ActorStartException private[akka](message: String) extends RuntimeException(message) -class IllegalActorStateException private[akka](message: String) extends RuntimeException(message) -class ActorKilledException private[akka](message: String) extends RuntimeException(message) -class ActorInitializationException private[akka](message: String) extends RuntimeException(message) +class ActorStartException private[akka](message: String) extends AkkaException(message) +class IllegalActorStateException private[akka](message: String) extends AkkaException(message) +class ActorKilledException private[akka](message: String) extends AkkaException(message) +class ActorInitializationException private[akka](message: String) extends AkkaException(message) +class ActorTimeoutException private[akka](message: String) extends AkkaException(message) /** * Actor factory module with factory methods for creating various kinds of Actors. @@ -62,7 +80,7 @@ class ActorInitializationException private[akka](message: String) extends Runtim * @author Jonas Bonér */ object Actor extends Logging { - val TIMEOUT = config.getInt("akka.actor.timeout", 5000) + val TIMEOUT = Duration(config.getInt("akka.actor.timeout", 5), TIME_UNIT).toMillis val SERIALIZE_MESSAGES = config.getBool("akka.actor.serialize-messages", false) /** @@ -74,7 +92,7 @@ object Actor extends Logging { private[actor] val actorRefInCreation = new scala.util.DynamicVariable[Option[ActorRef]](None) /** - * Creates a Actor.actorOf out of the Actor with type T. + * Creates an ActorRef out of the Actor with type T. *
    *   import Actor._
    *   val actor = actorOf[MyActor]
@@ -87,10 +105,27 @@ object Actor extends Logging {
    *   val actor = actorOf[MyActor].start
    * 
*/ - def actorOf[T <: Actor : Manifest]: ActorRef = new LocalActorRef(manifest[T].erasure.asInstanceOf[Class[_ <: Actor]]) + def actorOf[T <: Actor : Manifest]: ActorRef = actorOf(manifest[T].erasure.asInstanceOf[Class[_ <: Actor]]) /** - * Creates a Actor.actorOf out of the Actor. Allows you to pass in a factory function + * Creates an ActorRef out of the Actor with type T. + *
+     *   import Actor._
+     *   val actor = actorOf[MyActor]
+     *   actor.start
+     *   actor ! message
+     *   actor.stop
+     * 
+ * You can create and start the actor in one statement like this: + *
+     *   val actor = actorOf[MyActor].start
+     * 
+ */ + def actorOf(clazz: Class[_ <: Actor]): ActorRef = new LocalActorRef(clazz) + + + /** + * Creates an ActorRef out of the Actor. Allows you to pass in a factory function * that creates the Actor. Please note that this function can be invoked multiple * times if for example the Actor is supervised and needs to be restarted. *

@@ -221,7 +256,7 @@ object Actor extends Logging { case object Spawn actorOf(new Actor() { def receive = { - case Spawn => body; self.stop + case Spawn => try { body } finally { self.stop } } }).start ! Spawn } @@ -292,15 +327,14 @@ trait Actor extends Logging { type Receive = Actor.Receive /* - * Option[ActorRef] representation of the 'self' ActorRef reference. - *

- * Mainly for internal use, functions as the implicit sender references when invoking - * one of the message send functions ('!', '!!' and '!!!'). - */ - @transient implicit val optionSelf: Option[ActorRef] = { - val ref = Actor.actorRefInCreation.value - Actor.actorRefInCreation.value = None - if (ref.isEmpty) throw new ActorInitializationException( + * Some[ActorRef] representation of the 'self' ActorRef reference. + *

+ * Mainly for internal use, functions as the implicit sender references when invoking + * the 'forward' function. + */ + @transient implicit val someSelf: Some[ActorRef] = { + val optRef = Actor.actorRefInCreation.value + if (optRef.isEmpty) throw new ActorInitializationException( "ActorRef for instance of actor [" + getClass.getName + "] is not in scope." + "\n\tYou can not create an instance of an actor explicitly using 'new MyActor'." + "\n\tYou have to use one of the factory methods in the 'Actor' object to create a new actor." + @@ -308,16 +342,19 @@ trait Actor extends Logging { "\n\t\t'val actor = Actor.actorOf[MyActor]', or" + "\n\t\t'val actor = Actor.actorOf(new MyActor(..))', or" + "\n\t\t'val actor = Actor.actor { case msg => .. } }'") - else ref + + val ref = optRef.asInstanceOf[Some[ActorRef]].get + ref.id = getClass.getName //FIXME: Is this needed? + optRef.asInstanceOf[Some[ActorRef]] } - /* - * Some[ActorRef] representation of the 'self' ActorRef reference. + /* + * Option[ActorRef] representation of the 'self' ActorRef reference. *

* Mainly for internal use, functions as the implicit sender references when invoking - * the 'forward' function. + * one of the message send functions ('!', '!!' and '!!!'). */ - @transient implicit val someSelf: Some[ActorRef] = optionSelf.asInstanceOf[Some[ActorRef]] + implicit def optionSelf: Option[ActorRef] = someSelf /** * The 'self' field holds the ActorRef for this actor. @@ -346,11 +383,7 @@ trait Actor extends Logging { * self.stop(..) * */ - @transient val self: ActorRef = { - val zelf = optionSelf.get - zelf.id = getClass.getName - zelf - } + @transient val self: ScalaActorRef = someSelf.get /** * User overridable callback/setting. @@ -413,26 +446,45 @@ trait Actor extends Logging { /** * Is the actor able to handle the message passed in as arguments? */ - def isDefinedAt(message: Any): Boolean = base.isDefinedAt(message) + def isDefinedAt(message: Any): Boolean = processingBehavior.isDefinedAt(message) + + /** One of the fundamental methods of the ActorsModel + * Actor assumes a new behavior, + * None reverts the current behavior to the original behavior + */ + def become(behavior: Option[Receive]) { + self.hotswap = behavior + self.checkReceiveTimeout // FIXME : how to reschedule receivetimeout on hotswap? + } + + /** Akka Java API + * One of the fundamental methods of the ActorsModel + * Actor assumes a new behavior, + * null reverts the current behavior to the original behavior + */ + def become(behavior: Receive): Unit = become(Option(behavior)) // ========================================= // ==== INTERNAL IMPLEMENTATION DETAILS ==== // ========================================= + + private[akka] def apply(msg: Any) = processingBehavior(msg) - private[akka] def base: Receive = try { - lifeCycles orElse (self.hotswap getOrElse receive) - } catch { - case e: NullPointerException => throw new IllegalActorStateException( - "The 'self' ActorRef reference for [" + getClass.getName + "] is NULL, error in the ActorRef initialization process.") - } - - private val lifeCycles: Receive = { - case HotSwap(code) => self.hotswap = code; self.checkReceiveTimeout // FIXME : how to reschedule receivetimeout on hotswap? - case Exit(dead, reason) => self.handleTrapExit(dead, reason) - case Link(child) => self.link(child) - case Unlink(child) => self.unlink(child) - case UnlinkAndStop(child) => self.unlink(child); child.stop - case Restart(reason) => throw reason + private lazy val processingBehavior: Receive = { + lazy val defaultBehavior = receive + val actorBehavior: Receive = { + case HotSwap(code) => become(code) + case Exit(dead, reason) => self.handleTrapExit(dead, reason) + case Link(child) => self.link(child) + case Unlink(child) => self.unlink(child) + case UnlinkAndStop(child) => self.unlink(child); child.stop + case Restart(reason) => throw reason + case msg if self.hotswap.isDefined && + self.hotswap.get.isDefinedAt(msg) => self.hotswap.get.apply(msg) + case msg if self.hotswap.isEmpty && + defaultBehavior.isDefinedAt(msg) => defaultBehavior.apply(msg) + } + actorBehavior } } diff --git a/akka-core/src/main/scala/actor/ActorRef.scala b/akka-core/src/main/scala/actor/ActorRef.scala index 82f035f311..6f818b9c50 100644 --- a/akka-core/src/main/scala/actor/ActorRef.scala +++ b/akka-core/src/main/scala/actor/ActorRef.scala @@ -13,22 +13,25 @@ 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 se.scalablesolutions.akka.AkkaException import RemoteActorSerialization._ import org.multiverse.api.ThreadLocalTransaction._ import org.multiverse.commitbarriers.CountDownCommitBarrier import org.multiverse.api.exceptions.DeadTransactionException +import org.codehaus.aspectwerkz.joinpoint.JoinPoint + import java.net.InetSocketAddress import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.atomic.AtomicReference -import java.util.concurrent.{ConcurrentHashMap, TimeUnit} +import java.util.concurrent.{ScheduledFuture, ConcurrentHashMap, TimeUnit} import java.util.{Map => JMap} import java.lang.reflect.Field -import jsr166x.{Deque, ConcurrentLinkedDeque} +import scala.reflect.BeanProperty import com.google.protobuf.ByteString @@ -64,7 +67,8 @@ import com.google.protobuf.ByteString * * @author Jonas Bonér */ -trait ActorRef extends TransactionManagement { +trait ActorRef extends ActorRefShared with TransactionManagement with java.lang.Comparable[ActorRef] { + scalaRef: ScalaActorRef => // Only mutable for RemoteServer in order to maintain identity across nodes @volatile protected[akka] var _uuid = UUID.newUuid.toString @@ -72,10 +76,10 @@ trait ActorRef extends TransactionManagement { @volatile protected[this] var _isShutDown = false @volatile protected[akka] var _isBeingRestarted = false @volatile protected[akka] var _homeAddress = new InetSocketAddress(RemoteServer.HOSTNAME, RemoteServer.PORT) - @volatile protected[akka] var _timeoutActor: Option[ActorRef] = None + @volatile protected[akka] var _futureTimeout: Option[ScheduledFuture[AnyRef]] = None @volatile protected[akka] var startOnCreation = false @volatile protected[akka] var registeredInRemoteNodeDuringSerialization = false - protected[this] val guard = new ReentrantGuard + protected[akka] val guard = new ReentrantGuard /** * User overridable callback/setting. @@ -87,7 +91,7 @@ trait ActorRef extends TransactionManagement { * that you can use a custom name to be able to retrieve the "correct" persisted state * upon restart, remote restart etc. */ - @volatile var id: String = _uuid + @BeanProperty @volatile var id: String = _uuid /** * User overridable callback/setting. @@ -95,7 +99,7 @@ trait ActorRef extends TransactionManagement { * Defines the default timeout for '!!' and '!!!' invocations, * e.g. the timeout for the future returned by the call to '!!' and '!!!'. */ - @volatile var timeout: Long = Actor.TIMEOUT + @BeanProperty @volatile var timeout: Long = Actor.TIMEOUT /** * User overridable callback/setting. @@ -106,55 +110,60 @@ trait ActorRef extends TransactionManagement { @volatile var receiveTimeout: Option[Long] = None /** - * User overridable callback/setting. - * - *

- * Set trapExit to the list of exception classes that the actor should be able to trap + * Akka Java API + * Defines the default timeout for an initial receive invocation. + * When specified, the receive function should be able to handle a 'ReceiveTimeout' message. + */ + def setReceiveTimeout(timeout: Long) = this.receiveTimeout = Some(timeout) + def getReceiveTimeout(): Option[Long] = receiveTimeout + + /** + * Akka Java API + * Set 'trapExit' to the list of exception classes that the actor should be able to trap * from the actor it is supervising. When the supervising actor throws these exceptions * then they will trigger a restart. *

* - * Trap no exceptions: - *

-   * trapExit = Nil
-   * 
- * * Trap all exceptions: *
-   * trapExit = List(classOf[Throwable])
+   * getContext().setTrapExit(new Class[]{Throwable.class});
    * 
* * Trap specific exceptions only: *
-   * trapExit = List(classOf[MyApplicationException], classOf[MyApplicationError])
+   * getContext().setTrapExit(new Class[]{MyApplicationException.class, MyApplicationError.class});
    * 
*/ - @volatile var trapExit: List[Class[_ <: Throwable]] = Nil + def setTrapExit(exceptions: Array[Class[_ <: Throwable]]) = trapExit = exceptions.toList + def getTrapExit(): Array[Class[_ <: Throwable]] = trapExit.toArray /** - * User overridable callback/setting. - *

- * If 'trapExit' is set for the actor to act as supervisor, then a faultHandler must be defined. + * Akka Java API + * If 'trapExit' is set for the actor to act as supervisor, then a 'faultHandler' must be defined. *

* Can be one of: *

-   *  faultHandler = Some(AllForOneStrategy(maxNrOfRetries, withinTimeRange))
+   * getContext().setFaultHandler(new AllForOneStrategy(maxNrOfRetries, withinTimeRange));
    * 
* Or: *
-   *  faultHandler = Some(OneForOneStrategy(maxNrOfRetries, withinTimeRange))
+   * getContext().setFaultHandler(new OneForOneStrategy(maxNrOfRetries, withinTimeRange));
    * 
*/ - @volatile var faultHandler: Option[FaultHandlingStrategy] = None + def setFaultHandler(handler: FaultHandlingStrategy) = this.faultHandler = Some(handler) + def getFaultHandler(): Option[FaultHandlingStrategy] = faultHandler /** - * User overridable callback/setting. - *

* Defines the life-cycle for a supervised actor. */ - @volatile var lifeCycle: Option[LifeCycle] = None + def setLifeCycle(lifeCycle: LifeCycle) = this.lifeCycle = Some(lifeCycle) + def getLifeCycle(): Option[LifeCycle] = lifeCycle + + + @volatile private[akka] var _dispatcher: MessageDispatcher = Dispatchers.defaultGlobalDispatcher /** + * Akka Java API * The default dispatcher is the Dispatchers.globalExecutorBasedEventDrivenDispatcher. * This means that all actors will share the same event-driven executor based dispatcher. *

@@ -165,7 +174,9 @@ trait ActorRef extends TransactionManagement { * The default is also that all actors that are created and spawned from within this actor * is sharing the same dispatcher as its creator. */ - @volatile private[akka] var _dispatcher: MessageDispatcher = Dispatchers.globalExecutorBasedEventDrivenDispatcher + def setDispatcher(dispatcher: MessageDispatcher) = this.dispatcher = dispatcher + def getDispatcher(): MessageDispatcher = dispatcher + /** * Holds the hot swapped partial function. @@ -204,32 +215,30 @@ trait ActorRef extends TransactionManagement { protected[akka] def currentMessage_=(msg: Option[MessageInvocation]) = guard.withGuard { _currentMessage = msg } protected[akka] def currentMessage = guard.withGuard { _currentMessage } + /** + * Comparison only takes uuid into account. + */ + def compareTo(other: ActorRef) = this.uuid.compareTo(other.uuid) + /** * Returns the uuid for the actor. */ + def getUuid() = _uuid def uuid = _uuid /** + * Akka Java API * The reference sender Actor of the last received message. * Is defined if the message was sent from another Actor, else None. */ - def sender: Option[ActorRef] = { - // Five lines of map-performance-avoidance, could be just: currentMessage map { _.sender } - val msg = currentMessage - if(msg.isEmpty) None - else msg.get.sender - } + def getSender(): Option[ActorRef] = sender /** + * Akka Java API * The reference sender future of the last received message. * Is defined if the message was sent with sent with '!!' or '!!!', else None. */ - def senderFuture: Option[CompletableFuture[Any]] = { - // Five lines of map-performance-avoidance, could be just: currentMessage map { _.senderFuture } - val msg = currentMessage - if(msg.isEmpty) None - else msg.get.senderFuture - } + def getSenderFuture(): Option[CompletableFuture[Any]] = senderFuture /** * Is the actor being restarted? @@ -257,125 +266,144 @@ trait ActorRef extends TransactionManagement { protected[akka] def uuid_=(uid: String) = _uuid = uid /** + * Akka Java API * Sends a one-way asynchronous message. E.g. fire-and-forget semantics. *

- * - * If invoked from within an actor then the actor reference is implicitly passed on as the implicit 'sender' argument. - *

- * - * This actor 'sender' reference is then available in the receiving actor in the 'sender' member variable, - * if invoked from within an Actor. If not then no sender is available. *

-   *   actor ! message
+   * actor.sendOneWay(message);
    * 
*

*/ - def !(message: Any)(implicit sender: Option[ActorRef] = None) = { - if (isRunning) postMessageToMailbox(message, sender) - else throw new ActorInitializationException( - "Actor has not been started, you need to invoke 'actor.start' before using it") - } + def sendOneWay(message: AnyRef): Unit = sendOneWay(message,null) /** - * Sends a message asynchronously and waits on a future for a reply message. + * Akka Java API + * Sends a one-way asynchronous message. E.g. fire-and-forget semantics. *

- * It waits on the reply either until it receives it (in the form of Some(replyMessage)) - * or until the timeout expires (which will return None). E.g. send-and-receive-eventually semantics. + * Allows you to pass along the sender of the messag. + *

+ *

+   * actor.sendOneWay(message, context);
+   * 
+ *

+ */ + def sendOneWay(message: AnyRef, sender: ActorRef): Unit = this.!(message)(Option(sender)) + + /** + * Akka Java API + * @see sendRequestReply(message: AnyRef, timeout: Long, sender: ActorRef) + * Uses the defualt timeout of the Actor (setTimeout()) and omits the sender reference + */ + def sendRequestReply(message: AnyRef): AnyRef = sendRequestReply(message,timeout,null) + + /** + * Akka Java API + * @see sendRequestReply(message: AnyRef, timeout: Long, sender: ActorRef) + * Uses the defualt timeout of the Actor (setTimeout()) + */ + def sendRequestReply(message: AnyRef, sender: ActorRef): AnyRef = sendRequestReply(message,timeout,sender) + + /** + * Akka Java API + * Sends a message asynchronously and waits on a future for a reply message under the hood. + *

+ * It waits on the reply either until it receives it or until the timeout expires + * (which will throw an ActorTimeoutException). E.g. send-and-receive-eventually semantics. *

* NOTE: - * Use this method with care. In most cases it is better to use '!' together with the 'sender' member field to + * Use this method with care. In most cases it is better to use 'sendOneWay' together with 'getContext().getSender()' to * implement request/response message exchanges. - * If you are sending messages using !! then you have to use self.reply(..) + *

+ * If you are sending messages using sendRequestReply then you have to use getContext().reply(..) * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ - def !!(message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Option[Any] = { - if (isRunning) { - val future = postMessageToMailboxAndCreateFutureResultWithTimeout[Any](message, timeout, sender, None) - val isActiveObject = message.isInstanceOf[Invocation] - if (isActiveObject && message.asInstanceOf[Invocation].isVoid) { - future.asInstanceOf[CompletableFuture[Option[_]]].completeWithResult(None) - } - try { - future.await - } catch { - case e: FutureTimeoutException => - if (isActiveObject) throw e - else None - } - if (future.exception.isDefined) throw future.exception.get._2 - else future.result - } else throw new ActorInitializationException( - "Actor has not been started, you need to invoke 'actor.start' before using it") + def sendRequestReply(message: AnyRef, timeout: Long, sender: ActorRef): AnyRef = { + !!(message,timeout)(Option(sender)).getOrElse(throw new ActorTimeoutException( + "Message [" + message + + "]\n\tsent to [" + actorClassName + + "]\n\tfrom [" + (if(sender ne null) sender.actorClassName else "nowhere") + + "]\n\twith timeout [" + timeout + + "]\n\ttimed out.")) + .asInstanceOf[AnyRef] } /** + * Akka Java API + * @see sendRequestReplyFuture(message: AnyRef, sender: ActorRef): Future[_] + * Uses the Actors default timeout (setTimeout()) and omits the sender + */ + def sendRequestReplyFuture(message: AnyRef): Future[_] = sendRequestReplyFuture(message,timeout,null) + + /** + * Akka Java API + * @see sendRequestReplyFuture(message: AnyRef, sender: ActorRef): Future[_] + * Uses the Actors default timeout (setTimeout()) + */ + def sendRequestReplyFuture(message: AnyRef, sender: ActorRef): Future[_] = sendRequestReplyFuture(message,timeout,sender) + + /** + * Akka Java API * Sends a message asynchronously returns a future holding the eventual reply message. *

* NOTE: - * Use this method with care. In most cases it is better to use '!' together with the 'sender' member field to + * Use this method with care. In most cases it is better to use 'sendOneWay' together with the 'getContext().getSender()' to * implement request/response message exchanges. - * If you are sending messages using !!! then you have to use self.reply(..) + *

+ * If you are sending messages using sendRequestReplyFuture then you have to use getContext().reply(..) * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ - def !!![T](message: Any)(implicit sender: Option[ActorRef] = None): Future[T] = { - if (isRunning) postMessageToMailboxAndCreateFutureResultWithTimeout[T](message, timeout, sender, None) - else throw new ActorInitializationException( - "Actor has not been started, you need to invoke 'actor.start' before using it") - } + def sendRequestReplyFuture(message: AnyRef, timeout: Long, sender: ActorRef): Future[_] = !!!(message,timeout)(Option(sender)) /** - * Forwards the message and passes the original sender actor as the sender. - *

- * Works with '!', '!!' and '!!!'. + * Akka Java API + * Forwards the message specified to this actor and preserves the original sender of the message */ - def forward(message: Any)(implicit sender: Some[ActorRef]) = { - if (isRunning) { - if (sender.get.senderFuture.isDefined) postMessageToMailboxAndCreateFutureResultWithTimeout( - message, timeout, sender.get.sender, sender.get.senderFuture) - else if (sender.get.sender.isDefined) postMessageToMailbox(message, Some(sender.get.sender.get)) - else throw new IllegalActorStateException("Can't forward message when initial sender is not an actor") - } else throw new ActorInitializationException("Actor has not been started, you need to invoke 'actor.start' before using it") - } + def forward(message: AnyRef, sender: ActorRef): Unit = + if (sender eq null) throw new IllegalArgumentException("The 'sender' argument to 'forward' can't be null") + else forward(message)(Some(sender)) + /** - * Use self.reply(..) to reply with a message to the original sender of the message currently + * Akka Java API + * Use getContext().replyUnsafe(..) to reply with a message to the original sender of the message currently * being processed. *

* Throws an IllegalStateException if unable to determine what to reply to. */ - def reply(message: Any) = if(!reply_?(message)) throw new IllegalActorStateException( - "\n\tNo sender in scope, can't reply. " + - "\n\tYou have probably: " + - "\n\t\t1. Sent a message to an Actor from an instance that is NOT an Actor." + - "\n\t\t2. Invoked a method on an Active Object from an instance NOT an Active Object." + - "\n\tElse you might want to use 'reply_?' which returns Boolean(true) if succes and Boolean(false) if no sender in scope") + def replyUnsafe(message: AnyRef) = reply(message) /** - * Use reply_?(..) to reply with a message to the original sender of the message currently + * Akka Java API + * Use getContext().replySafe(..) to reply with a message to the original sender of the message currently * being processed. *

* Returns true if reply was sent, and false if unable to determine what to reply to. */ - def reply_?(message: Any): Boolean = { - if (senderFuture.isDefined) { - senderFuture.get completeWithResult message - true - } else if (sender.isDefined) { - sender.get ! message - true - } else false - } + def replySafe(message: AnyRef): Boolean = reply_?(message) /** * Returns the class for the Actor instance that is managed by the ActorRef. */ def actorClass: Class[_ <: Actor] + /** + * Akka Java API + * Returns the class for the Actor instance that is managed by the ActorRef. + */ + def getActorClass(): Class[_ <: Actor] = actorClass + + /** * Returns the class name for the Actor instance that is managed by the ActorRef. */ def actorClassName: String + /** + * Akka Java API + * Returns the class name for the Actor instance that is managed by the ActorRef. + */ + def getActorClassName(): String = actorClassName + /** * Sets the dispatcher for this actor. Needs to be invoked before the actor is started. */ @@ -407,33 +435,74 @@ trait ActorRef extends TransactionManagement { */ def transactionConfig_=(config: TransactionConfig): Unit + /** + * Akka Java API + * Sets the transaction configuration for this actor. Needs to be invoked before the actor is started. + */ + def setTransactionConfig(config: TransactionConfig): Unit = transactionConfig = config + + /** * Get the transaction configuration for this actor. */ def transactionConfig: TransactionConfig + /** + * Akka Java API + * Get the transaction configuration for this actor. + */ + def getTransactionConfig(): TransactionConfig = transactionConfig + + /** * Returns the home address and port for this actor. */ def homeAddress: InetSocketAddress = _homeAddress - + + /** + * Akka Java API + * Returns the home address and port for this actor. + */ + def getHomeAddress(): InetSocketAddress = homeAddress + /** * Set the home address and port for this actor. */ def homeAddress_=(hostnameAndPort: Tuple2[String, Int]): Unit = homeAddress_=(new InetSocketAddress(hostnameAndPort._1, hostnameAndPort._2)) - + + /** + * Akka Java API + * Set the home address and port for this actor. + */ + def setHomeAddress(hostname: String, port: Int): Unit = homeAddress = (hostname,port) + + /** * Set the home address and port for this actor. */ def homeAddress_=(address: InetSocketAddress): Unit + /** + * Akka Java API + * Set the home address and port for this actor. + */ + def setHomeAddress(address: InetSocketAddress): Unit = homeAddress = address + + /** * Returns the remote address for the actor, if any, else None. */ def remoteAddress: Option[InetSocketAddress] protected[akka] def remoteAddress_=(addr: Option[InetSocketAddress]): Unit + /** + * Akka Java API + * Gets the remote address for the actor, if any, else None. + */ + def getRemoteAddress(): Option[InetSocketAddress] = remoteAddress + + /** * Starts up the actor and its message queue. */ @@ -457,29 +526,21 @@ trait ActorRef extends TransactionManagement { * If the 'trapExit' member field has been set to at contain at least one exception class then it will * 'trap' these exceptions and automatically restart the linked actors according to the restart strategy * defined by the 'faultHandler'. - *

- * To be invoked from within the actor itself. */ def link(actorRef: ActorRef): Unit /** * Unlink the actor. - *

- * To be invoked from within the actor itself. */ def unlink(actorRef: ActorRef): Unit /** * Atomically start and link an actor. - *

- * To be invoked from within the actor itself. */ def startLink(actorRef: ActorRef): Unit /** * Atomically start, link and make an actor remote. - *

- * To be invoked from within the actor itself. */ def startLinkRemote(actorRef: ActorRef, hostname: String, port: Int): Unit @@ -488,33 +549,39 @@ trait ActorRef extends TransactionManagement { *

* To be invoked from within the actor itself. */ - def spawn[T <: Actor : Manifest]: ActorRef + def spawn(clazz: Class[_ <: Actor]): ActorRef /** - * Atomically create (from actor class), start and make an actor remote. + * Atomically create (from actor class), make it remote and start an actor. *

* To be invoked from within the actor itself. */ - def spawnRemote[T <: Actor: Manifest](hostname: String, port: Int): ActorRef + def spawnRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef /** - * Atomically create (from actor class), start and link an actor. + * Atomically create (from actor class), link and start an actor. *

* To be invoked from within the actor itself. */ - def spawnLink[T <: Actor: Manifest]: ActorRef + def spawnLink(clazz: Class[_ <: Actor]): ActorRef - /** - * Atomically create (from actor class), start, link and make an actor remote. + /** + * Atomically create (from actor class), make it remote, link and start an actor. *

* To be invoked from within the actor itself. */ - def spawnLinkRemote[T <: Actor : Manifest](hostname: String, port: Int): ActorRef + def spawnLinkRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef /** * Returns the mailbox size. */ - def mailboxSize: Int + def mailboxSize = dispatcher.mailboxSize(this) + + /** + * Akka Java API + * Returns the mailbox size. + */ + def getMailboxSize(): Int = mailboxSize /** * Returns the supervisor, if there is one. @@ -522,9 +589,10 @@ trait ActorRef extends TransactionManagement { def supervisor: Option[ActorRef] /** - * Shuts down and removes all linked actors. + * Akka Java API + * Returns the supervisor, if there is one. */ - def shutdownLinkedActors(): Unit + def getSupervisor(): ActorRef = supervisor getOrElse null protected[akka] def invoke(messageHandle: MessageInvocation): Unit @@ -536,14 +604,15 @@ trait ActorRef extends TransactionManagement { senderOption: Option[ActorRef], senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] - protected[this] def actorInstance: AtomicReference[Actor] + protected[akka] def actorInstance: AtomicReference[Actor] protected[akka] def actor: Actor = actorInstance.get protected[akka] def supervisor_=(sup: Option[ActorRef]): Unit - protected[akka] def mailbox: Deque[MessageInvocation] - + protected[akka] def mailbox: AnyRef + protected[akka] def mailbox_=(value: AnyRef): AnyRef + protected[akka] def handleTrapExit(dead: ActorRef, reason: Throwable): Unit protected[akka] def restart(reason: Throwable, maxNrOfRetries: Int, withinTimeRange: Int): Unit @@ -570,14 +639,16 @@ trait ActorRef extends TransactionManagement { cancelReceiveTimeout receiveTimeout.foreach { time => log.debug("Scheduling timeout for %s", this) - _timeoutActor = Some(Scheduler.scheduleOnce(this, ReceiveTimeout, time, TimeUnit.MILLISECONDS)) + _futureTimeout = Some(Scheduler.scheduleOnce(this, ReceiveTimeout, time, TimeUnit.MILLISECONDS)) } } - protected[akka] def cancelReceiveTimeout = _timeoutActor.foreach { timeoutActor => - if (timeoutActor.isRunning) Scheduler.unschedule(timeoutActor) - _timeoutActor = None - log.debug("Timeout canceled for %s", this) + protected[akka] def cancelReceiveTimeout = { + if(_futureTimeout.isDefined) { + _futureTimeout.get.cancel(true) + _futureTimeout = None + log.debug("Timeout canceled for %s", this) + } } } @@ -586,9 +657,9 @@ trait ActorRef extends TransactionManagement { * * @author Jonas Bonér */ -sealed class LocalActorRef private[akka]( +class LocalActorRef private[akka]( private[this] var actorFactory: Either[Option[Class[_ <: Actor]], Option[() => Actor]] = Left(None)) - extends ActorRef { + extends ActorRef with ScalaActorRef { @volatile private[akka] var _remoteAddress: Option[InetSocketAddress] = None // only mutable to maintain identity across nodes @volatile private[akka] var _linkedActors: Option[ConcurrentHashMap[String, ActorRef]] = None @@ -599,9 +670,9 @@ sealed class LocalActorRef private[akka]( @volatile private var loader: Option[ClassLoader] = None @volatile private var maxNrOfRetriesCount: Int = 0 @volatile private var restartsWithinTimeRangeTimestamp: Long = 0L - - protected[akka] val _mailbox: Deque[MessageInvocation] = new ConcurrentLinkedDeque[MessageInvocation] - protected[this] val actorInstance = guard.withGuard { new AtomicReference[Actor](newActor) } + @volatile private var _mailbox: AnyRef = _ + + protected[akka] val actorInstance = guard.withGuard { new AtomicReference[Actor](newActor) } // Needed to be able to null out the 'val self: ActorRef' member variables to make the Actor // instance elegible for garbage collection @@ -649,7 +720,6 @@ sealed class LocalActorRef private[akka]( hotswap = __hotswap actorSelfFields._1.set(actor, this) actorSelfFields._2.set(actor, Some(this)) - actorSelfFields._3.set(actor, Some(this)) start __messages.foreach(message => this ! MessageSerializer.deserialize(message.getMessage)) checkReceiveTimeout @@ -839,8 +909,8 @@ sealed class LocalActorRef private[akka]( *

* To be invoked from within the actor itself. */ - def spawn[T <: Actor : Manifest]: ActorRef = guard.withGuard { - val actorRef = spawnButDoNotStart[T] + def spawn(clazz: Class[_ <: Actor]): ActorRef = guard.withGuard { + val actorRef = spawnButDoNotStart(clazz) actorRef.start actorRef } @@ -850,8 +920,8 @@ sealed class LocalActorRef private[akka]( *

* To be invoked from within the actor itself. */ - def spawnRemote[T <: Actor: Manifest](hostname: String, port: Int): ActorRef = guard.withGuard { - val actor = spawnButDoNotStart[T] + def spawnRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = guard.withGuard { + val actor = spawnButDoNotStart(clazz) actor.makeRemote(hostname, port) actor.start actor @@ -862,8 +932,8 @@ sealed class LocalActorRef private[akka]( *

* To be invoked from within the actor itself. */ - def spawnLink[T <: Actor: Manifest]: ActorRef = guard.withGuard { - val actor = spawnButDoNotStart[T] + def spawnLink(clazz: Class[_ <: Actor]): ActorRef = guard.withGuard { + val actor = spawnButDoNotStart(clazz) try { actor.start } finally { @@ -877,8 +947,8 @@ sealed class LocalActorRef private[akka]( *

* To be invoked from within the actor itself. */ - def spawnLinkRemote[T <: Actor : Manifest](hostname: String, port: Int): ActorRef = guard.withGuard { - val actor = spawnButDoNotStart[T] + def spawnLinkRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = guard.withGuard { + val actor = spawnButDoNotStart(clazz) try { actor.makeRemote(hostname, port) actor.start @@ -890,17 +960,9 @@ sealed class LocalActorRef private[akka]( /** * Returns the mailbox. */ - def mailbox: Deque[MessageInvocation] = _mailbox + def mailbox: AnyRef = _mailbox - /** - * Returns the mailbox size. - */ - def mailboxSize: Int = _mailbox.size - - /** - * Returns a copy of all the messages, put into a List[MessageInvocation]. - */ - def messagesInMailbox: List[MessageInvocation] = _mailbox.toArray.toList.asInstanceOf[List[MessageInvocation]] + protected[akka] def mailbox_=(value: AnyRef):AnyRef = { _mailbox = value; value } /** * Shuts down and removes all linked actors. @@ -927,10 +989,7 @@ sealed class LocalActorRef private[akka]( createRemoteRequestProtocolBuilder(this, message, true, senderOption).build, None) } else { val invocation = new MessageInvocation(this, message, senderOption, None, transactionSet.get) - if (dispatcher.usesActorMailbox) { - _mailbox.add(invocation) - invocation.send - } else invocation.send + invocation.send } } @@ -951,7 +1010,6 @@ sealed class LocalActorRef private[akka]( else new DefaultCompletableFuture[T](timeout) val invocation = new MessageInvocation( this, message, senderOption, Some(future.asInstanceOf[CompletableFuture[Any]]), transactionSet.get) - if (dispatcher.usesActorMailbox) _mailbox.add(invocation) invocation.send future } @@ -961,7 +1019,8 @@ sealed class LocalActorRef private[akka]( * Callback for the dispatcher. This is the ingle entry point to the user Actor implementation. */ protected[akka] def invoke(messageHandle: MessageInvocation): Unit = guard.withGuard { - if (isShutdown) Actor.log.warning("Actor [%s] is shut down,\n\tignoring message [%s]", toString, messageHandle) + if (isShutdown) + Actor.log.warning("Actor [%s] is shut down,\n\tignoring message [%s]", toString, messageHandle) else { currentMessage = Option(messageHandle) try { @@ -972,14 +1031,13 @@ sealed class LocalActorRef private[akka]( throw e } finally { currentMessage = None //TODO: Don't reset this, we might want to resend the message - } + } } } protected[akka] def handleTrapExit(dead: ActorRef, reason: Throwable): Unit = { if (trapExit.exists(_.isAssignableFrom(reason.getClass))) { faultHandler match { - // FIXME: implement support for maxNrOfRetries and withinTimeRange in RestartStrategy case Some(AllForOneStrategy(maxNrOfRetries, withinTimeRange)) => restartLinkedActors(reason, maxNrOfRetries, withinTimeRange) @@ -991,7 +1049,6 @@ sealed class LocalActorRef private[akka]( "\n\tto non-empty list of exception classes - can't proceed " + toString) } } else { - if (lifeCycle.isEmpty) lifeCycle = Some(LifeCycle(Permanent)) // when passing on make sure we have a lifecycle notifySupervisorWithMessage(Exit(this, reason)) // if 'trapExit' is not defined then pass the Exit on } } @@ -999,49 +1056,41 @@ sealed class LocalActorRef private[akka]( protected[akka] def restart(reason: Throwable, maxNrOfRetries: Int, withinTimeRange: Int): Unit = { if (maxNrOfRetriesCount == 0) restartsWithinTimeRangeTimestamp = System.currentTimeMillis // first time around maxNrOfRetriesCount += 1 - + val tooManyRestarts = maxNrOfRetriesCount > maxNrOfRetries val restartingHasExpired = (System.currentTimeMillis - restartsWithinTimeRangeTimestamp) > withinTimeRange - if (tooManyRestarts || restartingHasExpired) { val notification = MaximumNumberOfRestartsWithinTimeRangeReached(this, maxNrOfRetries, withinTimeRange, reason) Actor.log.warning( - "Maximum number of restarts [%s] within time range [%s] reached." + - "\n\tWill *not* restart actor [%s] anymore." + - "\n\tLast exception causing restart was" + - "\n\t[%s].", + "Maximum number of restarts [%s] within time range [%s] reached." + + "\n\tWill *not* restart actor [%s] anymore." + + "\n\tLast exception causing restart was" + + "\n\t[%s].", maxNrOfRetries, withinTimeRange, this, reason) - _supervisor.foreach { sup => + _supervisor.foreach { sup => // can supervisor handle the notification? - if (sup.isDefinedAt(notification)) notifySupervisorWithMessage(notification) + if (sup.isDefinedAt(notification)) notifySupervisorWithMessage(notification) else Actor.log.warning( "No message handler defined for system message [MaximumNumberOfRestartsWithinTimeRangeReached]" + "\n\tCan't send the message to the supervisor [%s].", sup) } - } else { + + stop + } else { _isBeingRestarted = true val failedActor = actorInstance.get guard.withGuard { - lifeCycle.get match { - case LifeCycle(scope, _, _) => { - scope match { - case Permanent => - Actor.log.info("Restarting actor [%s] configured as PERMANENT.", id) - restartLinkedActors(reason, maxNrOfRetries, withinTimeRange) - Actor.log.debug("Restarting linked actors for actor [%s].", id) - Actor.log.debug("Invoking 'preRestart' for failed actor instance [%s].", id) - failedActor.preRestart(reason) - nullOutActorRefReferencesFor(failedActor) - val freshActor = newActor - freshActor.init - freshActor.initTransactionalState - actorInstance.set(freshActor) - Actor.log.debug("Invoking 'postRestart' for new actor instance [%s].", id) - freshActor.postRestart(reason) - _isBeingRestarted = false - case Temporary => shutDownTemporaryActor(this) - } - } + lifeCycle match { + case Some(LifeCycle(Temporary)) => shutDownTemporaryActor(this) + case _ => + // either permanent or none where default is permanent + Actor.log.info("Restarting actor [%s] configured as PERMANENT.", id) + Actor.log.debug("Restarting linked actors for actor [%s].", id) + restartLinkedActors(reason, maxNrOfRetries, withinTimeRange) + Actor.log.debug("Invoking 'preRestart' for failed actor instance [%s].", id) + if (isTypedActorDispatcher(failedActor)) restartTypedActorDispatcher(failedActor, reason) + else restartActor(failedActor, reason) + _isBeingRestarted = false } } } @@ -1049,14 +1098,10 @@ sealed class LocalActorRef private[akka]( protected[akka] def restartLinkedActors(reason: Throwable, maxNrOfRetries: Int, withinTimeRange: Int) = { linkedActorsAsList.foreach { actorRef => - if (actorRef.lifeCycle.isEmpty) actorRef.lifeCycle = Some(LifeCycle(Permanent)) - actorRef.lifeCycle.get match { - case LifeCycle(scope, _, _) => { - scope match { - case Permanent => actorRef.restart(reason, maxNrOfRetries, withinTimeRange) - case Temporary => shutDownTemporaryActor(actorRef) - } - } + actorRef.lifeCycle match { + // either permanent or none where default is permanent + case Some(LifeCycle(Temporary)) => shutDownTemporaryActor(actorRef) + case _ => actorRef.restart(reason, maxNrOfRetries, withinTimeRange) } } } @@ -1081,15 +1126,34 @@ sealed class LocalActorRef private[akka]( // ========= PRIVATE FUNCTIONS ========= - private def spawnButDoNotStart[T <: Actor: Manifest]: ActorRef = guard.withGuard { - val actorRef = Actor.actorOf(manifest[T].erasure.asInstanceOf[Class[T]].newInstance) + private def isTypedActorDispatcher(a: Actor): Boolean = a.isInstanceOf[TypedActor] + + private def restartTypedActorDispatcher(failedActor: Actor, reason: Throwable) = { + failedActor.preRestart(reason) + failedActor.postRestart(reason) + } + + private def restartActor(failedActor: Actor, reason: Throwable) = { + failedActor.preRestart(reason) + nullOutActorRefReferencesFor(failedActor) + val freshActor = newActor + freshActor.init + freshActor.initTransactionalState + actorInstance.set(freshActor) + if (failedActor.isInstanceOf[TypedActor]) failedActor.asInstanceOf[TypedActor].swapInstanceInProxy(freshActor) + Actor.log.debug("Invoking 'postRestart' for new actor instance [%s].", id) + freshActor.postRestart(reason) + } + + private def spawnButDoNotStart(clazz: Class[_ <: Actor]): ActorRef = guard.withGuard { + val actorRef = Actor.actorOf(clazz.newInstance) if (!dispatcher.isInstanceOf[ThreadBasedDispatcher]) actorRef.dispatcher = dispatcher actorRef } private[this] def newActor: Actor = { + Actor.actorRefInCreation.withValue(Some(this)){ isInInitialization = true - Actor.actorRefInCreation.value = Some(this) val actor = actorFactory match { case Left(Some(clazz)) => try { @@ -1111,6 +1175,7 @@ sealed class LocalActorRef private[akka]( "Actor instance passed to ActorRef can not be 'null'") isInInitialization = false actor + } } private def joinTransaction(message: Any) = if (isTransactionSetInScope) { @@ -1120,15 +1185,16 @@ sealed class LocalActorRef private[akka]( clearTransactionSet createNewTransactionSet } else oldTxSet - Actor.log.ifTrace("Joining transaction set [" + currentTxSet + - "];\n\tactor " + toString + "\n\twith message [" + message + "]") + Actor.log.trace("Joining transaction set [" + currentTxSet + + "];\n\tactor " + toString + + "\n\twith message [" + message + "]") val mtx = ThreadLocalTransaction.getThreadLocalTransaction if ((mtx eq null) || mtx.getStatus.isDead) currentTxSet.incParties else currentTxSet.incParties(mtx, 1) } private def dispatch[T](messageHandle: MessageInvocation) = { - Actor.log.ifTrace("Invoking actor with message:\n" + messageHandle) + Actor.log.trace("Invoking actor with message:\n" + messageHandle) val message = messageHandle.message //serializeMessage(messageHandle.message) var topLevelTransaction = false val txSet: Option[CountDownCommitBarrier] = @@ -1136,7 +1202,7 @@ sealed class LocalActorRef private[akka]( else { topLevelTransaction = true // FIXME create a new internal atomic block that can wait for X seconds if top level tx if (isTransactor) { - Actor.log.ifTrace("Creating a new transaction set (top-level transaction)\n\tfor actor " + toString + + Actor.log.trace("Creating a new transaction set (top-level transaction)\n\tfor actor " + toString + "\n\twith message " + messageHandle) Some(createNewTransactionSet) } else None @@ -1148,20 +1214,20 @@ sealed class LocalActorRef private[akka]( if (isTransactor) { val txFactory = _transactionFactory.getOrElse(DefaultGlobalTransactionFactory) atomic(txFactory) { - actor.base(message) + actor(message) setTransactionSet(txSet) // restore transaction set to allow atomic block to do commit } } else { - actor.base(message) + actor(message) setTransactionSet(txSet) // restore transaction set to allow atomic block to do commit } } catch { case e: DeadTransactionException => handleExceptionInDispatch( - new TransactionSetAbortedException("Transaction set has been aborted by another participant"), + new TransactionSetAbortedException("Transaction set has been aborted by another participant"), message, topLevelTransaction) - case e => - handleExceptionInDispatch(e, message, topLevelTransaction) + case e: InterruptedException => {} // received message while actor is shutting down, ignore + case e => handleExceptionInDispatch(e, message, topLevelTransaction) } finally { clearTransaction if (topLevelTransaction) clearTransactionSet @@ -1195,39 +1261,42 @@ sealed class LocalActorRef private[akka]( } } - senderFuture.foreach(_.completeWithException(this, reason)) + senderFuture.foreach(_.completeWithException(reason)) clearTransaction if (topLevelTransaction) clearTransactionSet - notifySupervisorWithMessage(Exit(this, reason)) + if (supervisor.isDefined) notifySupervisorWithMessage(Exit(this, reason)) + else { + lifeCycle match { + case Some(LifeCycle(Temporary)) => shutDownTemporaryActor(this) + case _ => + } + } } private def notifySupervisorWithMessage(notification: LifeCycleMessage) = { // FIXME to fix supervisor restart of remote actor for oneway calls, inject a supervisor proxy that can send notification back to client - _supervisor.foreach { sup => + _supervisor.foreach { sup => if (sup.isShutdown) { // if supervisor is shut down, game over for all linked actors -// shutdownLinkedActors -// stop + shutdownLinkedActors + stop } else sup ! notification // else notify supervisor } } - + private def nullOutActorRefReferencesFor(actor: Actor) = { actorSelfFields._1.set(actor, null) actorSelfFields._2.set(actor, null) - actorSelfFields._3.set(actor, null) } - private def findActorSelfField(clazz: Class[_]): Tuple3[Field, Field, Field] = { + private def findActorSelfField(clazz: Class[_]): Tuple2[Field, Field] = { try { val selfField = clazz.getDeclaredField("self") - val optionSelfField = clazz.getDeclaredField("optionSelf") val someSelfField = clazz.getDeclaredField("someSelf") selfField.setAccessible(true) - optionSelfField.setAccessible(true) someSelfField.setAccessible(true) - (selfField, optionSelfField, someSelfField) + (selfField, someSelfField) } catch { case e: NoSuchFieldException => val parent = clazz.getSuperclass @@ -1240,7 +1309,7 @@ sealed class LocalActorRef private[akka]( private def initializeActorInstance = { actor.init // run actor init and initTransactionalState callbacks actor.initTransactionalState - Actor.log.debug("[%s] has started", toString) + Actor.log.trace("[%s] has started", toString) ActorRegistry.register(this) if (id == "N/A") id = actorClass.getName // if no name set, then use default name (class name) clearTransactionSet // clear transaction set that might have been created if atomic block has been used within the Actor constructor body @@ -1272,6 +1341,15 @@ sealed 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. @@ -1279,9 +1357,10 @@ sealed class LocalActorRef private[akka]( * @author Jonas Bonér */ private[akka] case class RemoteActorRef private[akka] ( -// uuid: String, className: String, hostname: String, port: Int, timeOut: Long, isOnRemoteHost: Boolean) extends ActorRef { uuuid: String, val className: String, val hostname: String, val port: Int, _timeout: Long, loader: Option[ClassLoader]) - extends ActorRef { + // uuid: String, className: String, hostname: String, port: Int, timeOut: Long, isOnRemoteHost: Boolean) extends ActorRef { + extends ActorRef with ScalaActorRef { + _uuid = uuuid timeout = _timeout @@ -1310,6 +1389,7 @@ private[akka] case class RemoteActorRef private[akka] ( def stop: Unit = { _isRunning = false _isShutDown = true + postMessageToMailbox(RemoteActorSystemMessage.Stop, None) } /** @@ -1334,14 +1414,14 @@ private[akka] case class RemoteActorRef private[akka] ( def unlink(actorRef: ActorRef): Unit = unsupported def startLink(actorRef: ActorRef): Unit = unsupported def startLinkRemote(actorRef: ActorRef, hostname: String, port: Int): Unit = unsupported - def spawn[T <: Actor : Manifest]: ActorRef = unsupported - def spawnRemote[T <: Actor: Manifest](hostname: String, port: Int): ActorRef = unsupported - def spawnLink[T <: Actor: Manifest]: ActorRef = unsupported - def spawnLinkRemote[T <: Actor : Manifest](hostname: String, port: Int): ActorRef = unsupported - def mailboxSize: Int = unsupported + def spawn(clazz: Class[_ <: Actor]): ActorRef = unsupported + def spawnRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = unsupported + def spawnLink(clazz: Class[_ <: Actor]): ActorRef = unsupported + def spawnLinkRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = unsupported def supervisor: Option[ActorRef] = unsupported def shutdownLinkedActors: Unit = unsupported - protected[akka] def mailbox: Deque[MessageInvocation] = unsupported + protected[akka] def mailbox: AnyRef = unsupported + protected[akka] def mailbox_=(value: AnyRef):AnyRef = unsupported protected[akka] def handleTrapExit(dead: ActorRef, reason: Throwable): Unit = unsupported protected[akka] def restart(reason: Throwable, maxNrOfRetries: Int, withinTimeRange: Int): Unit = unsupported protected[akka] def restartLinkedActors(reason: Throwable, maxNrOfRetries: Int, withinTimeRange: Int): Unit = unsupported @@ -1350,7 +1430,259 @@ private[akka] case class RemoteActorRef private[akka] ( protected[akka] def invoke(messageHandle: MessageInvocation): Unit = unsupported protected[akka] def remoteAddress_=(addr: Option[InetSocketAddress]): Unit = unsupported protected[akka] def supervisor_=(sup: Option[ActorRef]): Unit = unsupported - protected[this] def actorInstance: AtomicReference[Actor] = unsupported - + protected[akka] def actorInstance: AtomicReference[Actor] = unsupported private def unsupported = throw new UnsupportedOperationException("Not supported for RemoteActorRef") } + +/** + * This trait represents the common (external) methods for all ActorRefs + * Needed because implicit conversions aren't applied when instance imports are used + * + * i.e. + * var self: ScalaActorRef = ... + * import self._ + * //can't call ActorRef methods here unless they are declared in a common + * //superclass, which ActorRefShared is. + */ +trait ActorRefShared { + /** + * Returns the uuid for the actor. + */ + def uuid: String + + /** + * Shuts down and removes all linked actors. + */ + def shutdownLinkedActors(): Unit +} + +/** + * This trait represents the Scala Actor API + * There are implicit conversions in ../actor/Implicits.scala + * from ActorRef -> ScalaActorRef and back + */ +trait ScalaActorRef extends ActorRefShared { ref: ActorRef => + /** + * Identifier for actor, does not have to be a unique one. Default is the 'uuid'. + *

+ * This field is used for logging, AspectRegistry.actorsFor(id), identifier for remote + * actor in RemoteServer etc.But also as the identifier for persistence, which means + * that you can use a custom name to be able to retrieve the "correct" persisted state + * upon restart, remote restart etc. + */ + def id: String + def id_=(id: String):Unit + + /** + * User overridable callback/setting. + *

+ * Defines the life-cycle for a supervised actor. + */ + @volatile var lifeCycle: Option[LifeCycle] = None + + /** + * User overridable callback/setting. + * + *

+ * Set trapExit to the list of exception classes that the actor should be able to trap + * from the actor it is supervising. When the supervising actor throws these exceptions + * then they will trigger a restart. + *

+ * + * Trap no exceptions: + *

+   * trapExit = Nil
+   * 
+ * + * Trap all exceptions: + *
+   * trapExit = List(classOf[Throwable])
+   * 
+ * + * Trap specific exceptions only: + *
+   * trapExit = List(classOf[MyApplicationException], classOf[MyApplicationError])
+   * 
+ */ + @volatile var trapExit: List[Class[_ <: Throwable]] = Nil + + + /** + * User overridable callback/setting. + *

+ * If 'trapExit' is set for the actor to act as supervisor, then a faultHandler must be defined. + *

+ * Can be one of: + *

+   *  faultHandler = Some(AllForOneStrategy(maxNrOfRetries, withinTimeRange))
+   * 
+ * Or: + *
+   *  faultHandler = Some(OneForOneStrategy(maxNrOfRetries, withinTimeRange))
+   * 
+ */ + @volatile var faultHandler: Option[FaultHandlingStrategy] = None + + + /** + * The reference sender Actor of the last received message. + * Is defined if the message was sent from another Actor, else None. + */ + def sender: Option[ActorRef] = { + // Five lines of map-performance-avoidance, could be just: currentMessage map { _.sender } + val msg = currentMessage + if(msg.isEmpty) None + else msg.get.sender + } + + /** + * The reference sender future of the last received message. + * Is defined if the message was sent with sent with '!!' or '!!!', else None. + */ + def senderFuture(): Option[CompletableFuture[Any]] = { + // Five lines of map-performance-avoidance, could be just: currentMessage map { _.senderFuture } + val msg = currentMessage + if(msg.isEmpty) None + else msg.get.senderFuture + } + + + /** + * Sends a one-way asynchronous message. E.g. fire-and-forget semantics. + *

+ * + * If invoked from within an actor then the actor reference is implicitly passed on as the implicit 'sender' argument. + *

+ * + * This actor 'sender' reference is then available in the receiving actor in the 'sender' member variable, + * if invoked from within an Actor. If not then no sender is available. + *

+   *   actor ! message
+   * 
+ *

+ */ + def !(message: Any)(implicit sender: Option[ActorRef] = None): Unit = { + if (isRunning) postMessageToMailbox(message, sender) + else throw new ActorInitializationException( + "Actor has not been started, you need to invoke 'actor.start' before using it") + } + + /** + * Sends a message asynchronously and waits on a future for a reply message. + *

+ * It waits on the reply either until it receives it (in the form of Some(replyMessage)) + * or until the timeout expires (which will return None). E.g. send-and-receive-eventually semantics. + *

+ * NOTE: + * Use this method with care. In most cases it is better to use '!' together with the 'sender' member field to + * implement request/response message exchanges. + * If you are sending messages using !! then you have to use self.reply(..) + * to send a reply message to the original sender. If not then the sender will block until the timeout expires. + */ + def !!(message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Option[Any] = { + if (isRunning) { + val future = postMessageToMailboxAndCreateFutureResultWithTimeout[Any](message, timeout, sender, None) + val isTypedActor = message.isInstanceOf[JoinPoint] + if (isTypedActor && TypedActor.isOneWay(message.asInstanceOf[JoinPoint])) { + future.asInstanceOf[CompletableFuture[Option[_]]].completeWithResult(None) + } + try { + future.await + } catch { + case e: FutureTimeoutException => + if (isTypedActor) throw e + else None + } + if (future.exception.isDefined) throw future.exception.get + else future.result + } else throw new ActorInitializationException( + "Actor has not been started, you need to invoke 'actor.start' before using it") + } + + + /** + * Sends a message asynchronously returns a future holding the eventual reply message. + *

+ * NOTE: + * Use this method with care. In most cases it is better to use '!' together with the 'sender' member field to + * implement request/response message exchanges. + * If you are sending messages using !!! then you have to use self.reply(..) + * to send a reply message to the original sender. If not then the sender will block until the timeout expires. + */ + def !!![T](message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Future[T] = { + if (isRunning) postMessageToMailboxAndCreateFutureResultWithTimeout[T](message, timeout, sender, None) + else throw new ActorInitializationException( + "Actor has not been started, you need to invoke 'actor.start' before using it") + } + + /** + * Forwards the message and passes the original sender actor as the sender. + *

+ * Works with '!', '!!' and '!!!'. + */ + def forward(message: Any)(implicit sender: Some[ActorRef]) = { + if (isRunning) { + if (sender.get.senderFuture.isDefined) postMessageToMailboxAndCreateFutureResultWithTimeout( + message, timeout, sender.get.sender, sender.get.senderFuture) + else if (sender.get.sender.isDefined) postMessageToMailbox(message, Some(sender.get.sender.get)) + else throw new IllegalActorStateException("Can't forward message when initial sender is not an actor") + } else throw new ActorInitializationException("Actor has not been started, you need to invoke 'actor.start' before using it") + } + + /** + * Use self.reply(..) to reply with a message to the original sender of the message currently + * being processed. + *

+ * Throws an IllegalStateException if unable to determine what to reply to. + */ + def reply(message: Any) = if(!reply_?(message)) throw new IllegalActorStateException( + "\n\tNo sender in scope, can't reply. " + + "\n\tYou have probably: " + + "\n\t\t1. Sent a message to an Actor from an instance that is NOT an Actor." + + "\n\t\t2. Invoked a method on an TypedActor from an instance NOT an TypedActor." + + "\n\tElse you might want to use 'reply_?' which returns Boolean(true) if succes and Boolean(false) if no sender in scope") + + /** + * Use reply_?(..) to reply with a message to the original sender of the message currently + * being processed. + *

+ * Returns true if reply was sent, and false if unable to determine what to reply to. + */ + def reply_?(message: Any): Boolean = { + if (senderFuture.isDefined) { + senderFuture.get completeWithResult message + true + } else if (sender.isDefined) { + sender.get ! message + true + } else false + } + + + + /** + * Atomically create (from actor class) and start an actor. + */ + def spawn[T <: Actor : Manifest]: ActorRef = + spawn(manifest[T].erasure.asInstanceOf[Class[_ <: Actor]]) + + /** + * Atomically create (from actor class), start and make an actor remote. + */ + def spawnRemote[T <: Actor: Manifest](hostname: String, port: Int): ActorRef = + spawnRemote(manifest[T].erasure.asInstanceOf[Class[_ <: Actor]],hostname,port) + + + /** + * Atomically create (from actor class), start and link an actor. + */ + def spawnLink[T <: Actor: Manifest]: ActorRef = + spawnLink(manifest[T].erasure.asInstanceOf[Class[_ <: Actor]]) + + + /** + * Atomically create (from actor class), start, link and make an actor remote. + */ + def spawnLinkRemote[T <: Actor : Manifest](hostname: String, port: Int): ActorRef = + spawnLinkRemote(manifest[T].erasure.asInstanceOf[Class[_ <: Actor]],hostname,port) +} diff --git a/akka-core/src/main/scala/actor/ActorRegistry.scala b/akka-core/src/main/scala/actor/ActorRegistry.scala index 88113a30a0..51de155723 100644 --- a/akka-core/src/main/scala/actor/ActorRegistry.scala +++ b/akka-core/src/main/scala/actor/ActorRegistry.scala @@ -29,19 +29,15 @@ case class ActorUnregistered(actor: ActorRef) extends ActorRegistryEvent * @author Jonas Bonér */ object ActorRegistry extends ListenerManagement { - - private val refComparator = new java.util.Comparator[ActorRef]{ - def compare(a: ActorRef,b: ActorRef) = a.uuid.compareTo(b.uuid) - } - private val actorsByUUID = new ConcurrentHashMap[String, ActorRef] private val actorsById = new ConcurrentHashMap[String, JSet[ActorRef]] - private val actorsByClassName = new ConcurrentHashMap[String, JSet[ActorRef]] + + private val Naught = Array[ActorRef]() //Nil for Arrays /** * Returns all actors in the system. */ - def actors: List[ActorRef] = filter(_ => true) + def actors: Array[ActorRef] = filter(_ => true) /** * Invokes a function for all actors. @@ -51,16 +47,30 @@ object ActorRegistry extends ListenerManagement { while (elements.hasMoreElements) f(elements.nextElement) } + /** + * Invokes the function on all known actors until it returns Some + * Returns None if the function never returns Some + */ + def find[T](f: PartialFunction[ActorRef,T]) : Option[T] = { + val elements = actorsByUUID.elements + while (elements.hasMoreElements) { + val element = elements.nextElement + if(f isDefinedAt element) + return Some(f(element)) + } + None + } + /** * Finds all actors that are subtypes of the class passed in as the Manifest argument and supproting passed message. */ - def actorsFor[T <: Actor](message: Any)(implicit manifest: Manifest[T] ): List[ActorRef] = + def actorsFor[T <: Actor](message: Any)(implicit manifest: Manifest[T] ): Array[ActorRef] = filter(a => manifest.erasure.isAssignableFrom(a.actor.getClass) && a.isDefinedAt(message)) /** * Finds all actors that satisfy a predicate. */ - def filter(p: ActorRef => Boolean): List[ActorRef] = { + def filter(p: ActorRef => Boolean): Array[ActorRef] = { val all = new ListBuffer[ActorRef] val elements = actorsByUUID.elements while (elements.hasMoreElements) { @@ -69,37 +79,34 @@ object ActorRegistry extends ListenerManagement { all += actorId } } - all.toList + all.toArray } /** * Finds all actors that are subtypes of the class passed in as the Manifest argument. */ - def actorsFor[T <: Actor](implicit manifest: Manifest[T]): List[ActorRef] = - filter(a => manifest.erasure.isAssignableFrom(a.actor.getClass)) + def actorsFor[T <: Actor](implicit manifest: Manifest[T]): Array[ActorRef] = + actorsFor[T](manifest.erasure.asInstanceOf[Class[T]]) /** * Finds any actor that matches T. */ def actorFor[T <: Actor](implicit manifest: Manifest[T]): Option[ActorRef] = - actorsFor[T](manifest).headOption + find({ case a:ActorRef if manifest.erasure.isAssignableFrom(a.actor.getClass) => a }) /** - * Finds all actors of the exact type specified by the class passed in as the Class argument. + * Finds all actors of type or sub-type specified by the class passed in as the Class argument. */ - def actorsFor[T <: Actor](clazz: Class[T]): List[ActorRef] = { - if (actorsByClassName.containsKey(clazz.getName)) { - actorsByClassName.get(clazz.getName).toArray.toList.asInstanceOf[List[ActorRef]] - } else Nil - } + def actorsFor[T <: Actor](clazz: Class[T]): Array[ActorRef] = + filter(a => clazz.isAssignableFrom(a.actor.getClass)) /** * Finds all actors that has a specific id. */ - def actorsFor(id: String): List[ActorRef] = { + def actorsFor(id: String): Array[ActorRef] = { if (actorsById.containsKey(id)) { - actorsById.get(id).toArray.toList.asInstanceOf[List[ActorRef]] - } else Nil + actorsById.get(id).toArray(Naught) + } else Naught } /** @@ -114,27 +121,22 @@ object ActorRegistry extends ListenerManagement { * Registers an actor in the ActorRegistry. */ def register(actor: ActorRef) = { - // UUID - actorsByUUID.put(actor.uuid, actor) - // ID val id = actor.id if (id eq null) throw new IllegalActorStateException("Actor.id is null " + actor) - if (actorsById.containsKey(id)) actorsById.get(id).add(actor) + + val set = actorsById get id + if (set ne null) set add actor else { - val set = new ConcurrentSkipListSet[ActorRef](refComparator) - set.add(actor) - actorsById.put(id, set) + val newSet = new ConcurrentSkipListSet[ActorRef] + newSet add actor + val oldSet = actorsById.putIfAbsent(id,newSet) + // Parry for two simultaneous putIfAbsent(id,newSet) + if (oldSet ne null) oldSet add actor } - // Class name - val className = actor.actorClassName - if (actorsByClassName.containsKey(className)) actorsByClassName.get(className).add(actor) - else { - val set = new ConcurrentSkipListSet[ActorRef](refComparator) - set.add(actor) - actorsByClassName.put(className, set) - } + // UUID + actorsByUUID.put(actor.uuid, actor) // notify listeners foreachListener(_ ! ActorRegistered(actor)) @@ -146,11 +148,10 @@ object ActorRegistry extends ListenerManagement { def unregister(actor: ActorRef) = { actorsByUUID remove actor.uuid - val id = actor.id - if (actorsById.containsKey(id)) actorsById.get(id).remove(actor) + val set = actorsById get actor.id + if (set ne null) set remove actor - val className = actor.actorClassName - if (actorsByClassName.containsKey(className)) actorsByClassName.get(className).remove(actor) + //FIXME: safely remove set if empty, leaks memory // notify listeners foreachListener(_ ! ActorUnregistered(actor)) @@ -159,12 +160,11 @@ object ActorRegistry extends ListenerManagement { /** * Shuts down and unregisters all actors in the system. */ - def shutdownAll = { + def shutdownAll() { log.info("Shutting down all actors in the system...") foreach(_.stop) actorsByUUID.clear actorsById.clear - actorsByClassName.clear log.info("All actors have been shut down and unregistered from ActorRegistry") } } diff --git a/akka-core/src/main/scala/actor/Agent.scala b/akka-core/src/main/scala/actor/Agent.scala index b800c94f23..df358cdfc4 100644 --- a/akka-core/src/main/scala/actor/Agent.scala +++ b/akka-core/src/main/scala/actor/Agent.scala @@ -5,11 +5,12 @@ package se.scalablesolutions.akka.actor import se.scalablesolutions.akka.stm.Ref +import se.scalablesolutions.akka.AkkaException import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.CountDownLatch -class AgentException private[akka](message: String) extends RuntimeException(message) +class AgentException private[akka](message: String) extends AkkaException(message) /** * The Agent class was strongly inspired by the agent principle in Clojure. diff --git a/akka-core/src/main/scala/actor/BootableActorLoaderService.scala b/akka-core/src/main/scala/actor/BootableActorLoaderService.scala index 22878b416a..dfb8541396 100644 --- a/akka-core/src/main/scala/actor/BootableActorLoaderService.scala +++ b/akka-core/src/main/scala/actor/BootableActorLoaderService.scala @@ -26,7 +26,7 @@ class AkkaDeployClassLoader(urls : List[URL], parent : ClassLoader) extends URLC } def listClassesInPackage(jar : URL, pkg : String) = { - val f = new File(jar.getFile) + val f = new File(jar.getFile) val jf = new JarFile(f) try { val es = jf.entries @@ -84,11 +84,14 @@ trait BootableActorLoaderService extends Bootable with Logging { } abstract override def onLoad = { + applicationLoader.foreach(_ => log.info("Creating /deploy class-loader")) + + super.onLoad + for (loader <- applicationLoader; clazz <- BOOT_CLASSES) { log.info("Loading boot class [%s]", clazz) loader.loadClass(clazz).newInstance } - super.onLoad } abstract override def onUnload = { diff --git a/akka-core/src/main/scala/actor/Implicits.scala b/akka-core/src/main/scala/actor/Implicits.scala new file mode 100644 index 0000000000..16bce4b016 --- /dev/null +++ b/akka-core/src/main/scala/actor/Implicits.scala @@ -0,0 +1,15 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka + +import actor.{ScalaActorRef, ActorRef} + +package object actor { + implicit def actorRef2Scala(ref: ActorRef): ScalaActorRef = + ref.asInstanceOf[ScalaActorRef] + + implicit def scala2ActorRef(ref: ScalaActorRef): ActorRef = + ref.asInstanceOf[ActorRef] +} \ No newline at end of file diff --git a/akka-core/src/main/scala/actor/Scheduler.scala b/akka-core/src/main/scala/actor/Scheduler.scala index 6a7187afdc..50db44a1d0 100644 --- a/akka-core/src/main/scala/actor/Scheduler.scala +++ b/akka-core/src/main/scala/actor/Scheduler.scala @@ -15,83 +15,117 @@ */ package se.scalablesolutions.akka.actor -import _root_.scala.collection.JavaConversions +import scala.collection.JavaConversions import java.util.concurrent._ import se.scalablesolutions.akka.util.Logging +import se.scalablesolutions.akka.AkkaException -object Scheduler { +object Scheduler extends Logging { import Actor._ - case object UnSchedule case class SchedulerException(msg: String, e: Throwable) extends RuntimeException(msg, e) private var service = Executors.newSingleThreadScheduledExecutor(SchedulerThreadFactory) - private val schedulers = new ConcurrentHashMap[ActorRef, ActorRef] - def schedule(receiver: ActorRef, message: AnyRef, initialDelay: Long, delay: Long, timeUnit: TimeUnit): ActorRef = { + log.info("Starting up Scheduler") + + /** + * Schedules to send the specified message to the receiver after initialDelay and then repeated after delay + */ + def schedule(receiver: ActorRef, message: AnyRef, initialDelay: Long, delay: Long, timeUnit: TimeUnit): ScheduledFuture[AnyRef] = { + log.trace( + "Schedule scheduled event\n\tevent = [%s]\n\treceiver = [%s]\n\tinitialDelay = [%s]\n\tdelay = [%s]\n\ttimeUnit = [%s]", + message, receiver, initialDelay, delay, timeUnit) try { - val future = service.scheduleAtFixedRate( + service.scheduleAtFixedRate( new Runnable { def run = receiver ! message }, initialDelay, delay, timeUnit).asInstanceOf[ScheduledFuture[AnyRef]] - createAndStoreScheduleActorForFuture(future) - val scheduler = actorOf(new ScheduleActor(future)).start - schedulers.put(scheduler, scheduler) - scheduler } catch { - case e => throw SchedulerException(message + " could not be scheduled on " + receiver, e) + case e: Exception => throw SchedulerException(message + " could not be scheduled on " + receiver, e) } } - def scheduleOnce(receiver: ActorRef, message: AnyRef, delay: Long, timeUnit: TimeUnit): ActorRef = { + /** + * Schedules to run specified function to the receiver after initialDelay and then repeated after delay, + * avoid blocking operations since this is executed in the schedulers thread + */ + def schedule(f: () => Unit, initialDelay: Long, delay: Long, timeUnit: TimeUnit): ScheduledFuture[AnyRef] = + schedule(new Runnable { def run = f() }, initialDelay, delay, timeUnit) + + /** + * Schedules to run specified runnable to the receiver after initialDelay and then repeated after delay, + * avoid blocking operations since this is executed in the schedulers thread + */ + def schedule(runnable: Runnable, initialDelay: Long, delay: Long, timeUnit: TimeUnit): ScheduledFuture[AnyRef] = { + log.trace( + "Schedule scheduled event\n\trunnable = [%s]\n\tinitialDelay = [%s]\n\tdelay = [%s]\n\ttimeUnit = [%s]", + runnable, initialDelay, delay, timeUnit) + try { - val future = service.schedule( - new Runnable { def run = receiver ! message }, delay, timeUnit).asInstanceOf[ScheduledFuture[AnyRef]] - createAndStoreScheduleActorForFuture(future) + service.scheduleAtFixedRate(runnable,initialDelay, delay, timeUnit).asInstanceOf[ScheduledFuture[AnyRef]] } catch { - case e => throw SchedulerException(message + " could not be scheduled on " + receiver, e) + case e: Exception => throw SchedulerException("Failed to schedule a Runnable", e) } } - private def createAndStoreScheduleActorForFuture(future: ScheduledFuture[AnyRef]): ActorRef = { - val scheduler = actorOf(new ScheduleActor(future)).start - schedulers.put(scheduler, scheduler) - scheduler + /** + * Schedules to send the specified message to the receiver after delay + */ + def scheduleOnce(receiver: ActorRef, message: AnyRef, delay: Long, timeUnit: TimeUnit): ScheduledFuture[AnyRef] = { + log.trace( + "Schedule one-time event\n\tevent = [%s]\n\treceiver = [%s]\n\tdelay = [%s]\n\ttimeUnit = [%s]", + message, receiver, delay, timeUnit) + try { + service.schedule( + new Runnable { def run = receiver ! message }, + delay, timeUnit).asInstanceOf[ScheduledFuture[AnyRef]] + } catch { + case e: Exception => throw SchedulerException( message + " could not be scheduleOnce'd on " + receiver, e) + } } - def unschedule(scheduleActor: ActorRef) = { - scheduleActor ! UnSchedule - schedulers.remove(scheduleActor) + /** + * Schedules a function to be run after delay, + * avoid blocking operations since the runnable is executed in the schedulers thread + */ + def scheduleOnce(f: () => Unit, delay: Long, timeUnit: TimeUnit): ScheduledFuture[AnyRef] = + scheduleOnce(new Runnable { def run = f() }, delay, timeUnit) + + /** + * Schedules a runnable to be run after delay, + * avoid blocking operations since the runnable is executed in the schedulers thread + */ + def scheduleOnce(runnable: Runnable, delay: Long, timeUnit: TimeUnit): ScheduledFuture[AnyRef] = { + log.trace( + "Schedule one-time event\n\trunnable = [%s]\n\tdelay = [%s]\n\ttimeUnit = [%s]", + runnable, delay, timeUnit) + try { + service.schedule(runnable,delay, timeUnit).asInstanceOf[ScheduledFuture[AnyRef]] + } catch { + case e: Exception => throw SchedulerException("Failed to scheduleOnce a Runnable", e) + } } def shutdown = { - import scala.collection.JavaConversions._ - schedulers.values.foreach(_ ! UnSchedule) - schedulers.clear + log.info("Shutting down Scheduler") service.shutdown } def restart = { + log.info("Restarting Scheduler") shutdown service = Executors.newSingleThreadScheduledExecutor(SchedulerThreadFactory) } } -private class ScheduleActor(future: ScheduledFuture[AnyRef]) extends Actor with Logging { - def receive = { - case Scheduler.UnSchedule => - future.cancel(true) - self.stop - } -} - private object SchedulerThreadFactory extends ThreadFactory { private var count = 0 val threadFactory = Executors.defaultThreadFactory() def newThread(r: Runnable): Thread = { val thread = threadFactory.newThread(r) - thread.setName("Scheduler-" + count) + thread.setName("akka:scheduler-" + count) thread.setDaemon(true) thread } diff --git a/akka-core/src/main/scala/actor/SerializationProtocol.scala b/akka-core/src/main/scala/actor/SerializationProtocol.scala index 13e8230638..403589aef3 100644 --- a/akka-core/src/main/scala/actor/SerializationProtocol.scala +++ b/akka-core/src/main/scala/actor/SerializationProtocol.scala @@ -73,9 +73,16 @@ object ActorSerialization { def fromBinary[T <: Actor](bytes: Array[Byte])(implicit format: Format[T]): ActorRef = fromBinaryToLocalActorRef(bytes, format) - def toBinary[T <: Actor](a: ActorRef)(implicit format: Format[T]): Array[Byte] = { + def toBinary[T <: Actor](a: ActorRef)(implicit format: Format[T]): Array[Byte] = toSerializedActorRefProtocol(a, format).toByteArray - } + + // wrapper for implicits to be used by Java + def fromBinaryJ[T <: Actor](bytes: Array[Byte], format: Format[T]): ActorRef = + fromBinary(bytes)(format) + + // wrapper for implicits to be used by Java + def toBinaryJ[T <: Actor](a: ActorRef, format: Format[T]): Array[Byte] = + toBinary(a)(format) private def toSerializedActorRefProtocol[T <: Actor](actorRef: ActorRef, format: Format[T]): SerializedActorRefProtocol = { val lifeCycleProtocol: Option[LifeCycleProtocol] = { @@ -85,14 +92,9 @@ object ActorSerialization { } val builder = LifeCycleProtocol.newBuilder actorRef.lifeCycle match { - case Some(LifeCycle(scope, None, _)) => + case Some(LifeCycle(scope)) => setScope(builder, scope) Some(builder.build) - case Some(LifeCycle(scope, Some(callbacks), _)) => - setScope(builder, scope) - builder.setPreRestart(callbacks.preRestart) - builder.setPostRestart(callbacks.postRestart) - Some(builder.build) case None => None } } @@ -122,7 +124,8 @@ object ActorSerialization { private def fromBinaryToLocalActorRef[T <: Actor](bytes: Array[Byte], format: Format[T]): ActorRef = fromProtobufToLocalActorRef(SerializedActorRefProtocol.newBuilder.mergeFrom(bytes).build, format, None) - private def fromProtobufToLocalActorRef[T <: Actor](protocol: SerializedActorRefProtocol, format: Format[T], loader: Option[ClassLoader]): ActorRef = { + private def fromProtobufToLocalActorRef[T <: Actor]( + protocol: SerializedActorRefProtocol, format: Format[T], loader: Option[ClassLoader]): ActorRef = { Actor.log.debug("Deserializing SerializedActorRefProtocol to LocalActorRef:\n" + protocol) val serializer = @@ -133,12 +136,8 @@ object ActorSerialization { val lifeCycle = if (protocol.hasLifeCycle) { val lifeCycleProtocol = protocol.getLifeCycle - val restartCallbacks = - if (lifeCycleProtocol.hasPreRestart || lifeCycleProtocol.hasPostRestart) - Some(RestartCallbacks(lifeCycleProtocol.getPreRestart, lifeCycleProtocol.getPostRestart)) - else None - Some(if (lifeCycleProtocol.getLifeCycle == LifeCycleType.PERMANENT) LifeCycle(Permanent, restartCallbacks) - else if (lifeCycleProtocol.getLifeCycle == LifeCycleType.TEMPORARY) LifeCycle(Temporary, restartCallbacks) + Some(if (lifeCycleProtocol.getLifeCycle == LifeCycleType.PERMANENT) LifeCycle(Permanent) + else if (lifeCycleProtocol.getLifeCycle == LifeCycleType.TEMPORARY) LifeCycle(Temporary) else throw new IllegalActorStateException("LifeCycle type is not valid: " + lifeCycleProtocol.getLifeCycle)) } else None @@ -225,26 +224,30 @@ object RemoteActorSerialization { .build } - def createRemoteRequestProtocolBuilder(ar: ActorRef, - message: Any, isOneWay: Boolean, senderOption: Option[ActorRef]): RemoteRequestProtocol.Builder = { - import ar._ - val protocol = RemoteRequestProtocol.newBuilder - .setId(RemoteRequestProtocolIdFactory.nextId) - .setMessage(MessageSerializer.serialize(message)) + def createRemoteRequestProtocolBuilder(actorRef: ActorRef, message: Any, isOneWay: Boolean, senderOption: Option[ActorRef]): + RemoteRequestProtocol.Builder = { + import actorRef._ + + val actorInfo = ActorInfoProtocol.newBuilder + .setUuid(uuid) .setTarget(actorClassName) .setTimeout(timeout) - .setUuid(uuid) - .setIsActor(true) + .setActorType(ActorType.SCALA_ACTOR) + .build + + val request = RemoteRequestProtocol.newBuilder + .setId(RemoteRequestProtocolIdFactory.nextId) + .setMessage(MessageSerializer.serialize(message)) + .setActorInfo(actorInfo) .setIsOneWay(isOneWay) - .setIsEscaped(false) val id = registerSupervisorAsRemoteActor - if (id.isDefined) protocol.setSupervisorUuid(id.get) + if (id.isDefined) request.setSupervisorUuid(id.get) senderOption.foreach { sender => RemoteServer.getOrCreateServer(sender.homeAddress).register(sender.uuid, sender) - protocol.setSender(toRemoteActorRefProtocol(sender)) + request.setSender(toRemoteActorRefProtocol(sender)) } - protocol + request } } diff --git a/akka-core/src/main/scala/actor/Supervisor.scala b/akka-core/src/main/scala/actor/Supervisor.scala index ede7d380db..b146a74c12 100644 --- a/akka-core/src/main/scala/actor/Supervisor.scala +++ b/akka-core/src/main/scala/actor/Supervisor.scala @@ -8,12 +8,13 @@ import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy} import se.scalablesolutions.akka.util.Logging import se.scalablesolutions.akka.remote.RemoteServer +import se.scalablesolutions.akka.AkkaException import Actor._ import java.util.concurrent.{CopyOnWriteArrayList, ConcurrentHashMap} import java.net.InetSocketAddress -class SupervisorException private[akka](message: String) extends RuntimeException(message) +class SupervisorException private[akka](message: String) extends AkkaException(message) /** * Factory object for creating supervisors declarative. It creates instances of the 'Supervisor' class. @@ -180,7 +181,7 @@ final class SupervisorActor private[akka] ( handler: FaultHandlingStrategy, trapExceptions: List[Class[_ <: Throwable]]) extends Actor { import self._ - + trapExit = trapExceptions faultHandler = Some(handler) diff --git a/akka-core/src/main/scala/actor/TypedActor.scala b/akka-core/src/main/scala/actor/TypedActor.scala new file mode 100644 index 0000000000..adb794741c --- /dev/null +++ b/akka-core/src/main/scala/actor/TypedActor.scala @@ -0,0 +1,720 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.actor + +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, Dispatchers} +import se.scalablesolutions.akka.config.ScalaConfig._ +import se.scalablesolutions.akka.serialization.Serializer +import se.scalablesolutions.akka.util._ + +import org.codehaus.aspectwerkz.joinpoint.{MethodRtti, JoinPoint} +import org.codehaus.aspectwerkz.proxy.Proxy +import org.codehaus.aspectwerkz.annotation.{Aspect, Around} + +import java.net.InetSocketAddress +import java.lang.reflect.{InvocationTargetException, Method, Field} + +import scala.reflect.BeanProperty + +/** + * TypedActor is a type-safe actor made out of a POJO with interface. + * Void methods are turned into fire-forget messages. + * Non-void methods are turned into request-reply messages with the exception of methods returning + * a 'Future' which will be sent using request-reply-with-future semantics and need to return the + * result using the 'future(..)' method: 'return future(... future result ...);'. + * + * Here is an example of usage (in Java): + *

+ * class TestActorImpl extends TypedActor implements TestActor {
+ * 
+ *   public void hit(int count) {
+ *     Pong pong = (Pong) getContext().getSender();
+ *     pong.hit(count++);
+ *   }
+ *
+ *   public Future square(int x) {
+ *     return future(x * x);
+ *   }
+ * 
+ *   @Override
+ *   public void init() {
+ *     ... // optional initialization on start
+ *   }
+ *
+ *   @Override
+ *   public void shutdown() {
+ *     ... // optional cleanup on stop
+ *   }
+ *
+ *   ... // more life-cycle callbacks if needed
+ * }
+ *
+ * // create the ping actor
+ * TestActor actor = TypedActor.newInstance(TestActor.class, TestActorImpl.class);
+ *
+ * actor.hit(1); // use the actor
+ * actor.hit(1);
+ *
+ * // This method will return immediately when called, caller should wait on the Future for the result
+ * Future future = actor.square(10);
+ * future.await();
+ * Integer result = future.get();
+ * 
+ * // stop the actor
+ * TypedActor.stop(actor);
+ * 
+ * + * Here is an example of usage (in Scala): + *
+ * class TestActorImpl extends TypedActor with TestActor {
+ * 
+ *   def hit(count: Int) = {
+ *     val pong = context.sender.asInstanceOf[Pong]
+ *     pong.hit(count += 1)
+ *   }
+ *
+ *   def square(x: Int): Future[Integer] = future(x * x)
+ * 
+ *   override def init = {
+ *     ... // optional initialization on start
+ *   }
+ *
+ *   override def shutdown = {
+ *     ... // optional cleanup on stop
+ *   }
+ *
+ *   ... // more life-cycle callbacks if needed
+ * }
+ *
+ * // create the ping actor
+ * val ping = TypedActor.newInstance(classOf[Ping], classOf[PingImpl])
+ *
+ * ping.hit(1) // use the actor
+ * ping.hit(1)
+ *
+ * // This method will return immediately when called, caller should wait on the Future for the result
+ * val future = actor.square(10)
+ * future.await
+ * val result: Int = future.get
+ * 
+ * // stop the actor
+ * TypedActor.stop(ping)
+ * 
+ * + * @author Jonas Bonér + */ +abstract class TypedActor extends Actor { + val DELEGATE_FIELD_NAME = "DELEGATE_0".intern + + @volatile private[actor] var proxy: AnyRef = _ + @volatile private var proxyDelegate: Field = _ + + /** + * Holds RTTI (runtime type information) for the TypedActor, f.e. current 'sender' + * reference, the 'senderFuture' reference etc. + *

+ * This class does not contain static information but is updated by the runtime system + * at runtime. + *

+ * You can get a hold of the context using either the 'getContext()' or 'context' + * methods from the 'TypedActor' base class. + *

+ * + * Here is an example of usage (in Java): + *

+   * class PingImpl extends TypedActor implements Ping {
+   *   public void hit(int count) {
+   *     Pong pong = (Pong) getContext().getSender();
+   *     pong.hit(count++);
+   *   }
+   * }
+   * 
+ * + * Here is an example of usage (in Scala): + *
+   * class PingImpl extends TypedActor with Ping {
+   *   def hit(count: Int) = {
+   *     val pong = context.sender.asInstanceOf[Pong]
+   *     pong.hit(count += 1)
+   *   }
+   * }
+   * 
+ */ + @BeanProperty val context: TypedActorContext = new TypedActorContext(self) + + /** + * This method is used to resolve the Future for TypedActor methods that are defined to return a + * {@link se.scalablesolutions.akka.actor.dispatch.Future }. + *

+ * Here is an example: + *

+   *   class MyTypedActorImpl extends TypedActor implements MyTypedActor {
+   *     public Future square(int x) {
+   *       return future(x * x);
+   *    }
+   *  }
+   *
+   *  MyTypedActor actor = TypedActor.actorOf(MyTypedActor.class, MyTypedActorImpl.class);
+   *
+   *  // This method will return immediately when called, caller should wait on the Future for the result
+   *  Future future = actor.square(10);
+   *  future.await();
+   *  Integer result = future.get();
+   * 
+ */ + def future[T](value: T): Future[T] = + self.senderFuture + .map{f => f.completeWithResult(value); f } + .getOrElse(throw new IllegalActorStateException("No sender future in scope")) + .asInstanceOf[Future[T]] + + def receive = { + case joinPoint: JoinPoint => + SenderContextInfo.senderActorRef.value = self + SenderContextInfo.senderProxy.value = proxy + + if (Actor.SERIALIZE_MESSAGES) serializeArguments(joinPoint) + if (TypedActor.isOneWay(joinPoint)) joinPoint.proceed + else self.reply(joinPoint.proceed) + + case Link(proxy) => self.link(proxy) + case Unlink(proxy) => self.unlink(proxy) + case unexpected => throw new IllegalActorStateException( + "Unexpected message [" + unexpected + "] sent to [" + this + "]") + } + + /** + * Rewrite target instance in AspectWerkz Proxy. + */ + private[actor] def swapInstanceInProxy(newInstance: Actor) = proxyDelegate.set(proxy, newInstance) + + private[akka] def initialize(typedActorProxy: AnyRef) = { + proxy = typedActorProxy + proxyDelegate = { + val field = proxy.getClass.getDeclaredField(DELEGATE_FIELD_NAME) + field.setAccessible(true) + field + } + } + + private def serializeArguments(joinPoint: JoinPoint) = { + val args = joinPoint.getRtti.asInstanceOf[MethodRtti].getParameterValues + var unserializable = false + var hasMutableArgument = false + for (arg <- args.toList) { + if (!arg.isInstanceOf[String] && + !arg.isInstanceOf[Byte] && + !arg.isInstanceOf[Int] && + !arg.isInstanceOf[Long] && + !arg.isInstanceOf[Float] && + !arg.isInstanceOf[Double] && + !arg.isInstanceOf[Boolean] && + !arg.isInstanceOf[Char] && + !arg.isInstanceOf[java.lang.Byte] && + !arg.isInstanceOf[java.lang.Integer] && + !arg.isInstanceOf[java.lang.Long] && + !arg.isInstanceOf[java.lang.Float] && + !arg.isInstanceOf[java.lang.Double] && + !arg.isInstanceOf[java.lang.Boolean] && + !arg.isInstanceOf[java.lang.Character]) hasMutableArgument = true + if (arg.getClass.getName.contains(TypedActor.AW_PROXY_PREFIX)) unserializable = true + } + if (!unserializable && hasMutableArgument) { + val copyOfArgs = Serializer.Java.deepClone(args) + joinPoint.getRtti.asInstanceOf[MethodRtti].setParameterValues(copyOfArgs.asInstanceOf[Array[AnyRef]]) + } + } +} + +/** + * Transactional TypedActor. All messages send to this actor as sent in a transaction. If an enclosing transaction + * exists it will be joined, if not then a new transaction will be created. + * + * @author Jonas Bonér + */ +abstract class TypedTransactor extends TypedActor { + self.makeTransactionRequired +} + +/** + * Holds RTTI (runtime type information) for the TypedActor, f.e. current 'sender' + * reference, the 'senderFuture' reference etc. + *

+ * This class does not contain static information but is updated by the runtime system + * at runtime. + *

+ * You can get a hold of the context using either the 'getContext()' or 'context' + * methods from the 'TypedActor' base class. + *

+ * Here is an example of usage (from Java): + *

+ * class PingImpl extends TypedActor implements Ping {
+ *   public void hit(int count) {
+ *     Pong pong = (Pong) getContext().getSender();
+ *     pong.hit(count++);
+ *   }
+ * }
+ * 
+ * + * Here is an example of usage (in Scala): + *
+ * class PingImpl extends TypedActor with Ping {
+ *   def hit(count: Int) = {
+ *     val pong = context.sender.asInstanceOf[Pong]
+ *     pong.hit(count += 1)
+ *   }
+ * }
+ * 
+ * + * @author Jonas Bonér + */ +final class TypedActorContext(private val actorRef: ActorRef) { + private[akka] var _sender: AnyRef = _ + + /** + * Returns the current sender reference. + * Scala style getter. + */ + def sender: AnyRef = { + if (_sender eq null) throw new IllegalActorStateException("Sender reference should not be null.") + else _sender + } + + /** + * Returns the current sender reference. + * Java style getter. + */ + def getSender: AnyRef = { + if (_sender eq null) throw new IllegalActorStateException("Sender reference should not be null.") + else _sender + } + + /** + * Returns the current sender future TypedActor reference. + * Scala style getter. + */ + def senderFuture: Option[CompletableFuture[Any]] = actorRef.senderFuture + + /** + * Returns the current sender future TypedActor reference. + * Java style getter. + * This method returns 'null' if the sender future is not available. + */ + def getSenderFuture = senderFuture +} + +/** + * Configuration factory for TypedActors. + * + * @author Jonas Bonér + */ +final class TypedActorConfiguration { + private[akka] var _timeout: Long = Actor.TIMEOUT + 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 = { + _timeout = timeout.toMillis + this + } + + def makeTransactionRequired() : TypedActorConfiguration = { + _transactionRequired = true; + this + } + + def makeRemote(hostname: String, port: Int) : TypedActorConfiguration = { + _host = Some(new InetSocketAddress(hostname, port)) + this + } + + 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 + } +} + +/** + * Factory class for creating TypedActors out of plain POJOs and/or POJOs with interfaces. + * + * @author Jonas Bonér + */ +object TypedActor extends Logging { + import Actor.actorOf + + val ZERO_ITEM_CLASS_ARRAY = Array[Class[_]]() + val ZERO_ITEM_OBJECT_ARRAY = Array[Object]() + + val AKKA_CAMEL_ROUTING_SCHEME = "akka".intern + private[actor] val AW_PROXY_PREFIX = "$$ProxiedByAW".intern + + def newInstance[T](intfClass: Class[T], targetClass: Class[_]): T = { + newInstance(intfClass, targetClass, None, Actor.TIMEOUT) + } + + def newRemoteInstance[T](intfClass: Class[T], targetClass: Class[_], hostname: String, port: Int): T = { + newInstance(intfClass, targetClass, Some(new InetSocketAddress(hostname, port)), Actor.TIMEOUT) + } + + def newInstance[T](intfClass: Class[T], targetClass: Class[_], timeout: Long = Actor.TIMEOUT): T = { + newInstance(intfClass, targetClass, None, timeout) + } + + def newRemoteInstance[T](intfClass: Class[T], targetClass: Class[_], timeout: Long = Actor.TIMEOUT, hostname: String, port: Int): T = { + newInstance(intfClass, targetClass, Some(new InetSocketAddress(hostname, port)), timeout) + } + + def newInstance[T](intfClass: Class[T], targetClass: Class[_], config: TypedActorConfiguration): T = { + val actorRef = actorOf(newTypedActor(targetClass)) + val typedActor = actorRef.actorInstance.get.asInstanceOf[TypedActor] + val proxy = Proxy.newInstance(Array(intfClass), Array(typedActor), true, false) + typedActor.initialize(proxy) + if (config._messageDispatcher.isDefined) actorRef.dispatcher = config._messageDispatcher.get + if (config._threadBasedDispatcher.isDefined) actorRef.dispatcher = Dispatchers.newThreadBasedDispatcher(actorRef) + AspectInitRegistry.register(proxy, AspectInit(intfClass, typedActor, actorRef, None, config.timeout)) + actorRef.start + proxy.asInstanceOf[T] + } + + private[akka] def newInstance[T](intfClass: Class[T], targetClass: Class[_], + remoteAddress: Option[InetSocketAddress], timeout: Long): T = { + val actorRef = actorOf(newTypedActor(targetClass)) + val typedActor = actorRef.actorInstance.get.asInstanceOf[TypedActor] + val proxy = Proxy.newInstance(Array(intfClass), Array(typedActor), true, false) + typedActor.initialize(proxy) + actorRef.timeout = timeout + if (remoteAddress.isDefined) actorRef.makeRemote(remoteAddress.get) + AspectInitRegistry.register(proxy, AspectInit(intfClass, typedActor, actorRef, remoteAddress, timeout)) + actorRef.start + proxy.asInstanceOf[T] + } + +/* + // NOTE: currently not used - but keep it around + private[akka] def newInstance[T <: TypedActor](targetClass: Class[T], + remoteAddress: Option[InetSocketAddress], timeout: Long): T = { + val proxy = { + val instance = Proxy.newInstance(targetClass, true, false) + if (instance.isInstanceOf[TypedActor]) instance.asInstanceOf[TypedActor] + else throw new IllegalActorStateException("Actor [" + targetClass.getName + "] is not a sub class of 'TypedActor'") + } + val context = injectTypedActorContext(proxy) + actorRef.actor.asInstanceOf[Dispatcher].initialize(targetClass, proxy, proxy, context) + actorRef.timeout = timeout + if (remoteAddress.isDefined) actorRef.makeRemote(remoteAddress.get) + AspectInitRegistry.register(proxy, AspectInit(targetClass, proxy, actorRef, remoteAddress, timeout)) + actorRef.start + proxy.asInstanceOf[T] + } +*/ + + /** + * Stops the current Typed Actor. + */ + def stop(proxy: AnyRef): Unit = AspectInitRegistry.unregister(proxy) + + /** + * Get the underlying dispatcher actor for the given Typed Actor. + */ + def actorFor(proxy: AnyRef): Option[ActorRef] = + ActorRegistry + .actorsFor(classOf[TypedActor]) + .find(a => a.actor.asInstanceOf[TypedActor].proxy == proxy) + + /** + * Links an other Typed Actor to this Typed Actor. + * @param supervisor the supervisor Typed Actor + * @param supervised the Typed Actor to link + */ + def link(supervisor: AnyRef, supervised: AnyRef) = { + val supervisorActor = actorFor(supervisor).getOrElse( + throw new IllegalActorStateException("Can't link when the supervisor is not an Typed Actor")) + val supervisedActor = actorFor(supervised).getOrElse( + throw new IllegalActorStateException("Can't link when the supervised is not an Typed Actor")) + supervisorActor.link(supervisedActor) + } + + /** + * Links an other Typed Actor to this Typed Actor and sets the fault handling for the supervisor. + * @param supervisor the supervisor Typed Actor + * @param supervised the Typed Actor to link + * @param handler fault handling strategy + * @param trapExceptions array of exceptions that should be handled by the supervisor + */ + def link(supervisor: AnyRef, supervised: AnyRef, + handler: FaultHandlingStrategy, trapExceptions: Array[Class[_ <: Throwable]]) = { + val supervisorActor = actorFor(supervisor).getOrElse( + throw new IllegalActorStateException("Can't link when the supervisor is not an Typed Actor")) + val supervisedActor = actorFor(supervised).getOrElse( + throw new IllegalActorStateException("Can't link when the supervised is not an Typed Actor")) + supervisorActor.trapExit = trapExceptions.toList + supervisorActor.faultHandler = Some(handler) + supervisorActor.link(supervisedActor) + } + + /** + * Unlink the supervised Typed Actor from the supervisor. + * @param supervisor the supervisor Typed Actor + * @param supervised the Typed Actor to unlink + */ + def unlink(supervisor: AnyRef, supervised: AnyRef) = { + val supervisorActor = actorFor(supervisor).getOrElse( + throw new IllegalActorStateException("Can't unlink when the supervisor is not an Typed Actor")) + val supervisedActor = actorFor(supervised).getOrElse( + throw new IllegalActorStateException("Can't unlink when the supervised is not an Typed Actor")) + supervisorActor.unlink(supervisedActor) + } + + /** + * Sets the trap exit for the given supervisor Typed Actor. + * @param supervisor the supervisor Typed Actor + * @param trapExceptions array of exceptions that should be handled by the supervisor + */ + def trapExit(supervisor: AnyRef, trapExceptions: Array[Class[_ <: Throwable]]) = { + val supervisorActor = actorFor(supervisor).getOrElse( + throw new IllegalActorStateException("Can't set trap exceptions when the supervisor is not an Typed Actor")) + supervisorActor.trapExit = trapExceptions.toList + this + } + + /** + * Sets the fault handling strategy for the given supervisor Typed Actor. + * @param supervisor the supervisor Typed Actor + * @param handler fault handling strategy + */ + def faultHandler(supervisor: AnyRef, handler: FaultHandlingStrategy) = { + val supervisorActor = actorFor(supervisor).getOrElse( + throw new IllegalActorStateException("Can't set fault handler when the supervisor is not an Typed Actor")) + supervisorActor.faultHandler = Some(handler) + this + } + + def isTransactional(clazz: Class[_]): Boolean = { + if (clazz == null) false + else if (clazz.isAssignableFrom(classOf[TypedTransactor])) true + else isTransactional(clazz.getSuperclass) + } + + private[akka] def newTypedActor(targetClass: Class[_]): TypedActor = { + val instance = targetClass.newInstance + val typedActor = + if (instance.isInstanceOf[TypedActor]) instance.asInstanceOf[TypedActor] + else throw new IllegalArgumentException("Actor [" + targetClass.getName + "] is not a sub class of 'TypedActor'") + typedActor.init + import se.scalablesolutions.akka.stm.local.atomic + atomic { + typedActor.initTransactionalState + } + typedActor + } + + private[akka] def isOneWay(joinPoint: JoinPoint): Boolean = + isOneWay(joinPoint.getRtti.asInstanceOf[MethodRtti]) + + private[akka] def isOneWay(methodRtti: MethodRtti): Boolean = + methodRtti.getMethod.getReturnType == java.lang.Void.TYPE + + private[akka] def returnsFuture_?(methodRtti: MethodRtti): Boolean = + classOf[Future[_]].isAssignableFrom(methodRtti.getMethod.getReturnType) + + private[akka] def supervise(restartStrategy: RestartStrategy, components: List[Supervise]): Supervisor = + Supervisor(SupervisorConfig(restartStrategy, components)) +} + +/** + * AspectWerkz Aspect that is turning POJO into TypedActor. + *

+ * Is deployed on a 'perInstance' basis with the pointcut 'execution(* *.*(..))', + * e.g. all methods on the instance. + * + * @author Jonas Bonér + */ +@Aspect("perInstance") +private[akka] sealed class TypedActorAspect { + @volatile private var isInitialized = false + @volatile private var isStopped = false + private var interfaceClass: Class[_] = _ + private var typedActor: TypedActor = _ + private var actorRef: ActorRef = _ + private var remoteAddress: Option[InetSocketAddress] = _ + private var timeout: Long = _ + private var uuid: String = _ + @volatile private var instance: TypedActor = _ + + @Around("execution(* *.*(..))") + def invoke(joinPoint: JoinPoint): AnyRef = { + if (!isInitialized) initialize(joinPoint) + dispatch(joinPoint) + } + + private def dispatch(joinPoint: JoinPoint) = { + if (remoteAddress.isDefined) remoteDispatch(joinPoint) + else localDispatch(joinPoint) + } + + private def localDispatch(joinPoint: JoinPoint): AnyRef = { + val methodRtti = joinPoint.getRtti.asInstanceOf[MethodRtti] + val isOneWay = TypedActor.isOneWay(methodRtti) + val senderActorRef = Some(SenderContextInfo.senderActorRef.value) + val senderProxy = Some(SenderContextInfo.senderProxy.value) + + typedActor.context._sender = senderProxy + if (!actorRef.isRunning && !isStopped) { + isStopped = true + joinPoint.proceed + + } else if (isOneWay) { + actorRef.!(joinPoint)(senderActorRef) + null.asInstanceOf[AnyRef] + + } else if (TypedActor.returnsFuture_?(methodRtti)) { + actorRef.!!!(joinPoint, timeout)(senderActorRef) + + } else { + val result = (actorRef.!!(joinPoint, timeout)(senderActorRef)).as[AnyRef] + if (result.isDefined) result.get + else throw new ActorTimeoutException("Invocation to [" + joinPoint + "] timed out.") + } + } + + private def remoteDispatch(joinPoint: JoinPoint): AnyRef = { + val methodRtti = joinPoint.getRtti.asInstanceOf[MethodRtti] + val isOneWay = TypedActor.isOneWay(methodRtti) + val (message: Array[AnyRef], isEscaped) = escapeArguments(methodRtti.getParameterValues) + + val typedActorInfo = TypedActorInfoProtocol.newBuilder + .setInterface(interfaceClass.getName) + .setMethod(methodRtti.getMethod.getName) + .build + + val actorInfo = ActorInfoProtocol.newBuilder + .setUuid(uuid) + .setTarget(typedActor.getClass.getName) + .setTimeout(timeout) + .setActorType(ActorType.TYPED_ACTOR) + .setTypedActorInfo(typedActorInfo) + .build + + val requestBuilder = RemoteRequestProtocol.newBuilder + .setId(RemoteRequestProtocolIdFactory.nextId) + .setMessage(MessageSerializer.serialize(message)) + .setActorInfo(actorInfo) + .setIsOneWay(isOneWay) + + val id = actorRef.registerSupervisorAsRemoteActor + if (id.isDefined) requestBuilder.setSupervisorUuid(id.get) + + val remoteMessage = requestBuilder.build + + val future = RemoteClient.clientFor(remoteAddress.get).send(remoteMessage, None) + + if (isOneWay) null // for void methods + else { + if (future.isDefined) { + future.get.await + val result = getResultOrThrowException(future.get) + if (result.isDefined) result.get + else throw new IllegalActorStateException("No result returned from call to [" + joinPoint + "]") + } else throw new IllegalActorStateException("No future returned from call to [" + joinPoint + "]") + } + } + + private def getResultOrThrowException[T](future: Future[T]): Option[T] = + if (future.exception.isDefined) throw future.exception.get + else future.result + + private def escapeArguments(args: Array[AnyRef]): Tuple2[Array[AnyRef], Boolean] = { + var isEscaped = false + val escapedArgs = for (arg <- args) yield { + val clazz = arg.getClass + if (clazz.getName.contains(TypedActor.AW_PROXY_PREFIX)) { + isEscaped = true + TypedActor.AW_PROXY_PREFIX + clazz.getSuperclass.getName + } else arg + } + (escapedArgs, isEscaped) + } + + private def initialize(joinPoint: JoinPoint): Unit = { + val init = AspectInitRegistry.initFor(joinPoint.getThis) + interfaceClass = init.interfaceClass + typedActor = init.targetInstance + actorRef = init.actorRef + uuid = actorRef.uuid + remoteAddress = init.remoteAddress + timeout = init.timeout + isInitialized = true + } +} + +/** + * Internal helper class to help pass the contextual information between threads. + * + * @author Jonas Bonér + */ +private[akka] object SenderContextInfo { + import scala.util.DynamicVariable + private[actor] val senderActorRef = new DynamicVariable[ActorRef](null) + private[actor] val senderProxy = new DynamicVariable[AnyRef](null) +} + +/** + * @author Jonas Bonér + */ +private[akka] object AspectInitRegistry extends ListenerManagement { + private val initializations = new java.util.concurrent.ConcurrentHashMap[AnyRef, AspectInit] + + def initFor(proxy: AnyRef) = initializations.get(proxy) + + def register(proxy: AnyRef, init: AspectInit) = { + val res = initializations.put(proxy, init) + foreachListener(_ ! AspectInitRegistered(proxy, init)) + res + } + + /** + * Unregisters initialization and stops its ActorRef. + */ + def unregister(proxy: AnyRef): AspectInit = { + val init = initializations.remove(proxy) + foreachListener(_ ! AspectInitUnregistered(proxy, init)) + init.actorRef.stop + init + } +} + +private[akka] sealed trait AspectInitRegistryEvent +private[akka] case class AspectInitRegistered(proxy: AnyRef, init: AspectInit) extends AspectInitRegistryEvent +private[akka] case class AspectInitUnregistered(proxy: AnyRef, init: AspectInit) extends AspectInitRegistryEvent + +/** + * @author Jonas Bonér + */ +private[akka] sealed case class AspectInit( + val interfaceClass: Class[_], + val targetInstance: TypedActor, + val actorRef: ActorRef, + val remoteAddress: Option[InetSocketAddress], + val timeout: Long) { + def this(interfaceClass: Class[_], targetInstance: TypedActor, actorRef: ActorRef, timeout: Long) = + this(interfaceClass, targetInstance, actorRef, None, timeout) +} + diff --git a/akka-core/src/main/scala/actor/UntypedActor.scala b/akka-core/src/main/scala/actor/UntypedActor.scala new file mode 100644 index 0000000000..e94ea94b3e --- /dev/null +++ b/akka-core/src/main/scala/actor/UntypedActor.scala @@ -0,0 +1,158 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.actor + +import se.scalablesolutions.akka.dispatch._ +import se.scalablesolutions.akka.stm.global._ +import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy} +import se.scalablesolutions.akka.config.ScalaConfig._ + +import java.net.InetSocketAddress + +import scala.reflect.BeanProperty + +/** + * Subclass this abstract class to create a MDB-style untyped actor. + *

+ * This class is meant to be used from Java. + *

+ * Here is an example on how to create and use an UntypedActor: + *

+ *  public class SampleUntypedActor extends UntypedActor {
+ *    public void onReceive(Object message) throws Exception {
+ *      if (message instanceof String) {
+ *        String msg = (String)message;
+ *
+ *        if (msg.equals("UseReply")) {
+ *          // Reply to original sender of message using the 'replyUnsafe' method
+ *          getContext().replyUnsafe(msg + ":" + getContext().getUuid());
+ *
+ *        } else if (msg.equals("UseSender") && getContext().getSender().isDefined()) {
+ *          // Reply to original sender of message using the sender reference
+ *          // also passing along my own refererence (the context)
+ *          getContext().getSender().get().sendOneWay(msg, context);
+ *
+ *        } else if (msg.equals("UseSenderFuture") && getContext().getSenderFuture().isDefined()) {
+ *          // Reply to original sender of message using the sender future reference
+ *          getContext().getSenderFuture().get().completeWithResult(msg);
+ *
+ *        } else if (msg.equals("SendToSelf")) {
+ *          // Send message to the actor itself recursively
+ *          getContext().sendOneWay(msg)
+ *
+ *        } else if (msg.equals("ForwardMessage")) {
+ *          // Retreive an actor from the ActorRegistry by ID and get an ActorRef back
+ *          ActorRef actorRef = ActorRegistry.actorsFor("some-actor-id").head();
+ *
+ *        } else throw new IllegalArgumentException("Unknown message: " + message);
+ *      } else throw new IllegalArgumentException("Unknown message: " + message);
+ *    }
+ *
+ *    public static void main(String[] args) {
+ *      ActorRef actor = UntypedActor.actorOf(SampleUntypedActor.class);
+ *      actor.start();
+ *      actor.sendOneWay("SendToSelf");
+ *      actor.stop();
+ *    }
+ *  }
+ * 
+ * + * @author Jonas Bonér + */ +abstract class UntypedActor extends Actor { + def getContext(): ActorRef = self + + final protected def receive = { + case msg => onReceive(msg) + } + + @throws(classOf[Exception]) + def onReceive(message: Any): Unit +} + +/** + * Implements the Transactor abstraction. E.g. a transactional UntypedActor. + * + * @author Jonas Bonér + */ +abstract class UntypedTransactor extends UntypedActor { + self.makeTransactionRequired +} + +/** + * Factory closure for an UntypedActor, to be used with 'UntypedActor.actorOf(factory)'. + * + * @author Jonas Bonér + */ +trait UntypedActorFactory { + def create: UntypedActor +} + +/** + * Extend this abstract class to create a remote UntypedActor. + * + * @author Jonas Bonér + */ +abstract class RemoteUntypedActor(address: InetSocketAddress) extends UntypedActor { + def this(hostname: String, port: Int) = this(new InetSocketAddress(hostname, port)) + self.makeRemote(address) +} + +/** + * Factory object for creating and managing 'UntypedActor's. Meant to be used from Java. + *

+ * Example on how to create an actor: + *

+ *   ActorRef actor = UntypedActor.actorOf(MyUntypedActor.class);
+ *   actor.start();
+ *   actor.sendOneWay(message, context)
+ *   actor.stop();
+ * 
+ * You can create and start the actor in one statement like this: + *
+ *   ActorRef actor = UntypedActor.actorOf(MyUntypedActor.class).start();
+ * 
+ * + * @author Jonas Bonér + */ +object UntypedActor { + /** + * Creates an ActorRef out of the Actor type represented by the class provided. + * Example in Java: + *
+   *   ActorRef actor = UntypedActor.actorOf(MyUntypedActor.class);
+   *   actor.start();
+   *   actor.sendOneWay(message, context);
+   *   actor.stop();
+   * 
+ * You can create and start the actor in one statement like this: + *
+   *   val actor = actorOf(classOf[MyActor]).start
+   * 
+ */ + def actorOf[T <: Actor](clazz: Class[T]): ActorRef = Actor.actorOf(clazz) + + /** + * NOTE: Use this convenience method with care, do NOT make it possible to get a reference to the + * UntypedActor instance directly, but only through its 'ActorRef' wrapper reference. + *

+ * Creates an ActorRef out of the Actor. Allows you to pass in the instance for the UntypedActor. + * Only use this method when you need to pass in constructor arguments into the 'UntypedActor'. + *

+ * You use it by implementing the UntypedActorFactory interface. + * Example in Java: + *

+   *   ActorRef actor = UntypedActor.actorOf(new UntypedActorFactory() {
+   *     public UntypedActor create() {
+   *       return new MyUntypedActor("service:name", 5);
+   *     }
+   *   });
+   *   actor.start();
+   *   actor.sendOneWay(message, context);
+   *   actor.stop();
+   * 
+ */ + def actorOf(factory: UntypedActorFactory): ActorRef = Actor.actorOf(factory.create) +} \ No newline at end of file diff --git a/akka-core/src/main/scala/config/Config.scala b/akka-core/src/main/scala/config/Config.scala index 68842ad1e3..aca46e6249 100644 --- a/akka-core/src/main/scala/config/Config.scala +++ b/akka-core/src/main/scala/config/Config.scala @@ -5,10 +5,11 @@ package se.scalablesolutions.akka.config import se.scalablesolutions.akka.util.Logging +import se.scalablesolutions.akka.AkkaException import net.lag.configgy.{Config => CConfig, Configgy, ParseException} -class ConfigurationException(message: String) extends RuntimeException(message) +class ConfigurationException(message: String) extends AkkaException(message) /** * Loads up the configuration (from the akka.conf file). @@ -82,6 +83,8 @@ object Config extends Logging { if (VERSION != CONFIG_VERSION) throw new ConfigurationException( "Akka JAR version [" + VERSION + "] is different than the provided config ('akka.conf') version [" + CONFIG_VERSION + "]") + val TIME_UNIT = config.getString("akka.time-unit", "seconds") + val startTime = System.currentTimeMillis def uptime = (System.currentTimeMillis - startTime) / 1000 } diff --git a/akka-core/src/main/scala/config/Configuration.scala b/akka-core/src/main/scala/config/Configuration.scala index faa5c912f1..e257c739a9 100644 --- a/akka-core/src/main/scala/config/Configuration.scala +++ b/akka-core/src/main/scala/config/Configuration.scala @@ -5,7 +5,7 @@ package se.scalablesolutions.akka.config /* -import se.scalablesolutions.akka.kernel.{ActiveObject, ActiveObjectProxy} +import se.scalablesolutions.akka.kernel.{TypedActor, TypedActorProxy} import com.google.inject.{AbstractModule} import java.util.{List => JList, ArrayList} import scala.reflect.BeanProperty @@ -55,6 +55,6 @@ class Component(@BeanProperty val intf: Class[_], @BeanProperty val target: Class[_], @BeanProperty val lifeCycle: LifeCycle, @BeanProperty val timeout: Int) extends Server { - def newWorker(proxy: ActiveObjectProxy) = se.scalablesolutions.akka.kernel.Supervise(proxy.server, lifeCycle.transform) + def newWorker(proxy: TypedActorProxy) = se.scalablesolutions.akka.kernel.Supervise(proxy.server, lifeCycle.transform) } */ diff --git a/akka-core/src/main/scala/config/Configurator.scala b/akka-core/src/main/scala/config/Configurator.scala index db92c5f35b..ba7e1f35f2 100644 --- a/akka-core/src/main/scala/config/Configurator.scala +++ b/akka-core/src/main/scala/config/Configurator.scala @@ -6,14 +6,14 @@ package se.scalablesolutions.akka.config import ScalaConfig.{RestartStrategy, Component} -private[akka] trait ActiveObjectConfiguratorBase { +private[akka] trait TypedActorConfiguratorBase { def getExternalDependency[T](clazz: Class[T]): T - def configure(restartStrategy: RestartStrategy, components: List[Component]): ActiveObjectConfiguratorBase + def configure(restartStrategy: RestartStrategy, components: List[Component]): TypedActorConfiguratorBase - def inject: ActiveObjectConfiguratorBase + def inject: TypedActorConfiguratorBase - def supervise: ActiveObjectConfiguratorBase + def supervise: TypedActorConfiguratorBase def reset diff --git a/akka-core/src/main/scala/config/SupervisionConfig.scala b/akka-core/src/main/scala/config/SupervisionConfig.scala index 1f5fd15a9b..2f25f4ed33 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} import se.scalablesolutions.akka.dispatch.MessageDispatcher sealed abstract class FaultHandlingStrategy @@ -42,16 +42,7 @@ object ScalaConfig { case object AllForOne extends FailOverScheme case object OneForOne extends FailOverScheme - case class LifeCycle(scope: Scope, - restartCallbacks: Option[RestartCallbacks] = None, - shutdownCallback: Option[ShutdownCallback] = None) extends ConfigElement - case class RestartCallbacks(preRestart: String, postRestart: String) { - if ((preRestart eq null) || (postRestart eq null)) throw new IllegalArgumentException("Restart callback methods can't be null") - } - case class ShutdownCallback(shutdown: String) { - if (shutdown eq null) throw new IllegalArgumentException("Shutdown callback method can't be null") - } - + case class LifeCycle(scope: Scope) extends ConfigElement case object Permanent extends Scope case object Temporary extends Scope @@ -137,26 +128,12 @@ object JavaConfig { scheme.transform, maxNrOfRetries, withinTimeRange, trapExceptions.toList) } - class LifeCycle(@BeanProperty val scope: Scope, - @BeanProperty val restartCallbacks: RestartCallbacks, - @BeanProperty val shutdownCallback: ShutdownCallback) extends ConfigElement { - def this(scope: Scope) = this(scope, null, null) - def this(scope: Scope, restartCallbacks: RestartCallbacks) = this(scope, restartCallbacks, null) - def this(scope: Scope, shutdownCallback: ShutdownCallback) = this(scope, null, shutdownCallback) + class LifeCycle(@BeanProperty val scope: Scope) extends ConfigElement { def transform = { - val restartCallbacksOption = if (restartCallbacks eq null) None else Some(restartCallbacks.transform) - val shutdownCallbackOption = if (shutdownCallback eq null) None else Some(shutdownCallback.transform) - se.scalablesolutions.akka.config.ScalaConfig.LifeCycle(scope.transform, restartCallbacksOption, shutdownCallbackOption) + se.scalablesolutions.akka.config.ScalaConfig.LifeCycle(scope.transform) } } - class RestartCallbacks(@BeanProperty val preRestart: String, @BeanProperty val postRestart: String) { - def transform = se.scalablesolutions.akka.config.ScalaConfig.RestartCallbacks(preRestart, postRestart) - } - class ShutdownCallback(@BeanProperty val shutdown: String) { - def transform = se.scalablesolutions.akka.config.ScalaConfig.ShutdownCallback(shutdown) - } - abstract class Scope extends ConfigElement { def transform: se.scalablesolutions.akka.config.ScalaConfig.Scope } diff --git a/akka-core/src/main/scala/config/ActiveObjectConfigurator.scala b/akka-core/src/main/scala/config/TypedActorConfigurator.scala similarity index 56% rename from akka-core/src/main/scala/config/ActiveObjectConfigurator.scala rename to akka-core/src/main/scala/config/TypedActorConfigurator.scala index 88e495bbd0..d639d21f5f 100644 --- a/akka-core/src/main/scala/config/ActiveObjectConfigurator.scala +++ b/akka-core/src/main/scala/config/TypedActorConfigurator.scala @@ -12,54 +12,55 @@ import java.util.{ArrayList} import com.google.inject._ /** - * Configurator for the Active Objects. Used to do declarative configuration of supervision. - * It also does dependency injection with and into Active Objects using dependency injection + * Configurator for the TypedActors. Used to do declarative configuration of supervision. + * It also does dependency injection with and into TypedActors using dependency injection * frameworks such as Google Guice or Spring. *

- * If you don't want declarative configuration then you should use the ActiveObject + * If you don't want declarative configuration then you should use the TypedActor * factory methods. * * @author Jonas Bonér */ -class ActiveObjectConfigurator { +class TypedActorConfigurator { import scala.collection.JavaConversions._ // TODO: make pluggable once we have f.e a SpringConfigurator - private val INSTANCE = new ActiveObjectGuiceConfigurator + private val INSTANCE = new TypedActorGuiceConfigurator /** - * Returns the a list with all active objects that has been put under supervision for the class specified. + * Returns the a list with all typed actors that has been put under supervision for the class specified. * - * @param clazz the class for the active object - * @return a list with all the active objects for the class + * @param clazz the class for the typed actor + * @return a list with all the typed actors for the class */ - def getInstances[T](clazz: Class[T]): JList[T] = INSTANCE.getInstance(clazz).foldLeft(new ArrayList[T]){ (l, i) => l add i ; l } + def getInstances[T](clazz: Class[T]): JList[T] = + INSTANCE.getInstance(clazz).foldLeft(new ArrayList[T]){ (l, i) => l add i ; l } /** - * Returns the first item in a list of all active objects that has been put under supervision for the class specified. + * Returns the first item in a list of all typed actors that has been put under supervision for the class specified. * - * @param clazz the class for the active object - * @return the active object for the class + * @param clazz the class for the typed actor + * @return the typed actor for the class */ def getInstance[T](clazz: Class[T]): T = INSTANCE.getInstance(clazz).head - def configure(restartStrategy: RestartStrategy, components: Array[Component]): ActiveObjectConfigurator = { + def configure(restartStrategy: RestartStrategy, components: Array[Component]): TypedActorConfigurator = { INSTANCE.configure( restartStrategy.transform, components.toList.asInstanceOf[scala.List[Component]].map(_.transform)) this } - def inject: ActiveObjectConfigurator = { + def inject: TypedActorConfigurator = { INSTANCE.inject this } - def supervise: ActiveObjectConfigurator = { + def supervise: TypedActorConfigurator = { INSTANCE.supervise this } - def addExternalGuiceModule(module: Module): ActiveObjectConfigurator = { + def addExternalGuiceModule(module: Module): TypedActorConfigurator = { INSTANCE.addExternalGuiceModule(module) this } diff --git a/akka-core/src/main/scala/config/ActiveObjectGuiceConfigurator.scala b/akka-core/src/main/scala/config/TypedActorGuiceConfigurator.scala similarity index 59% rename from akka-core/src/main/scala/config/ActiveObjectGuiceConfigurator.scala rename to akka-core/src/main/scala/config/TypedActorGuiceConfigurator.scala index 54174b6030..cced864721 100644 --- a/akka-core/src/main/scala/config/ActiveObjectGuiceConfigurator.scala +++ b/akka-core/src/main/scala/config/TypedActorGuiceConfigurator.scala @@ -4,25 +4,27 @@ package se.scalablesolutions.akka.config -import com.google.inject._ - +import se.scalablesolutions.akka.actor._ import se.scalablesolutions.akka.config.ScalaConfig._ -import se.scalablesolutions.akka.actor.{Supervisor, ActiveObject, Dispatcher, ActorRef, Actor, IllegalActorStateException} import se.scalablesolutions.akka.remote.RemoteServer import se.scalablesolutions.akka.util.Logging +import org.codehaus.aspectwerkz.proxy.Proxy + import scala.collection.mutable.HashMap import java.net.InetSocketAddress import java.lang.reflect.Method +import com.google.inject._ + /** - * This is an class for internal usage. Instead use the se.scalablesolutions.akka.config.ActiveObjectConfigurator - * class for creating ActiveObjects. + * This is an class for internal usage. Instead use the se.scalablesolutions.akka.config.TypedActorConfigurator + * class for creating TypedActors. * * @author Jonas Bonér */ -private[akka] class ActiveObjectGuiceConfigurator extends ActiveObjectConfiguratorBase with Logging { +private[akka] class TypedActorGuiceConfigurator extends TypedActorConfiguratorBase with Logging { private var injector: Injector = _ private var supervisor: Option[Supervisor] = None private var restartStrategy: RestartStrategy = _ @@ -30,22 +32,22 @@ private[akka] class ActiveObjectGuiceConfigurator extends ActiveObjectConfigurat private var supervised: List[Supervise] = Nil private var bindings: List[DependencyBinding] = Nil private var configRegistry = new HashMap[Class[_], Component] // TODO is configRegistry needed? - private var activeObjectRegistry = new HashMap[Class[_], Tuple3[AnyRef, AnyRef, Component]] + private var typedActorRegistry = new HashMap[Class[_], Tuple3[AnyRef, AnyRef, Component]] private var modules = new java.util.ArrayList[Module] private var methodToUriRegistry = new HashMap[Method, String] /** * Returns the active abject that has been put under supervision for the class specified. * - * @param clazz the class for the active object - * @return the active objects for the class + * @param clazz the class for the typed actor + * @return the typed actors for the class */ def getInstance[T](clazz: Class[T]): List[T] = synchronized { - log.debug("Retrieving active object [%s]", clazz.getName) + log.debug("Retrieving typed actor [%s]", clazz.getName) if (injector eq null) throw new IllegalActorStateException( "inject() and/or supervise() must be called before invoking getInstance(clazz)") val (proxy, targetInstance, component) = - activeObjectRegistry.getOrElse(clazz, throw new IllegalActorStateException( + typedActorRegistry.getOrElse(clazz, throw new IllegalActorStateException( "Class [" + clazz.getName + "] has not been put under supervision" + "\n(by passing in the config to the 'configure' and then invoking 'supervise') method")) injector.injectMembers(targetInstance) @@ -53,7 +55,7 @@ private[akka] class ActiveObjectGuiceConfigurator extends ActiveObjectConfigurat } def isDefined(clazz: Class[_]): Boolean = synchronized { - activeObjectRegistry.get(clazz).isDefined + typedActorRegistry.get(clazz).isDefined } override def getExternalDependency[T](clazz: Class[T]): T = synchronized { @@ -67,72 +69,90 @@ private[akka] class ActiveObjectGuiceConfigurator extends ActiveObjectConfigurat } override def configure(restartStrategy: RestartStrategy, components: List[Component]): - ActiveObjectConfiguratorBase = synchronized { + TypedActorConfiguratorBase = synchronized { this.restartStrategy = restartStrategy this.components = components.toArray.toList.asInstanceOf[List[Component]] bindings = for (component <- this.components) yield { - if (component.intf.isDefined) newDelegatingProxy(component) - else newSubclassingProxy(component) + newDelegatingProxy(component) +// if (component.intf.isDefined) newDelegatingProxy(component) +// else newSubclassingProxy(component) } val deps = new java.util.ArrayList[DependencyBinding](bindings.size) for (b <- bindings) deps.add(b) - modules.add(new ActiveObjectGuiceModule(deps)) + modules.add(new TypedActorGuiceModule(deps)) this } +/* private def newSubclassingProxy(component: Component): DependencyBinding = { - val targetClass = component.target - val actorRef = Actor.actorOf(new Dispatcher(component.transactionRequired, - component.lifeCycle.restartCallbacks, - component.lifeCycle.shutdownCallback)) + val targetClass = + if (component.target.isInstanceOf[Class[_ <: TypedActor]]) component.target.asInstanceOf[Class[_ <: TypedActor]] + else throw new IllegalArgumentException("TypedActor [" + component.target.getName + "] must be a subclass of TypedActor") + val actorRef = Actor.actorOf(new Dispatcher(component.transactionRequired)) if (component.dispatcher.isDefined) actorRef.dispatcher = component.dispatcher.get val remoteAddress = if (component.remoteAddress.isDefined) Some(new InetSocketAddress(component.remoteAddress.get.hostname, component.remoteAddress.get.port)) else None - val proxy = ActiveObject.newInstance(targetClass, actorRef, remoteAddress, component.timeout).asInstanceOf[AnyRef] - remoteAddress.foreach(address => RemoteServer.registerActiveObject(address, targetClass.getName, proxy)) + val proxy = TypedActor.newInstance(targetClass, actorRef, remoteAddress, component.timeout).asInstanceOf[AnyRef] + remoteAddress.foreach(address => RemoteServer.registerTypedActor(address, targetClass.getName, proxy)) supervised ::= Supervise(actorRef, component.lifeCycle) - activeObjectRegistry.put(targetClass, (proxy, proxy, component)) + typedActorRegistry.put(targetClass, (proxy, proxy, component)) new DependencyBinding(targetClass, proxy) } - +*/ private def newDelegatingProxy(component: Component): DependencyBinding = { - val targetClass = component.intf.get - val targetInstance = component.target.newInstance.asInstanceOf[AnyRef] // TODO: perhaps need to put in registry component.target.getConstructor(Array[Class[_]](): _*).setAccessible(true) - val actorRef = Actor.actorOf(new Dispatcher(component.transactionRequired, - component.lifeCycle.restartCallbacks, - component.lifeCycle.shutdownCallback)) + val interfaceClass = if (component.intf.isDefined) component.intf.get + else throw new IllegalActorStateException("No interface for TypedActor specified") + val implementationClass = component.target + val timeout = component.timeout + + val actorRef = Actor.actorOf(TypedActor.newTypedActor(implementationClass)) + actorRef.timeout = timeout if (component.dispatcher.isDefined) actorRef.dispatcher = component.dispatcher.get + val typedActor = actorRef.actorInstance.get.asInstanceOf[TypedActor] + + val proxy = Proxy.newInstance(Array(interfaceClass), Array(typedActor), true, false) + val remoteAddress = if (component.remoteAddress.isDefined) Some(new InetSocketAddress(component.remoteAddress.get.hostname, component.remoteAddress.get.port)) else None - val proxy = ActiveObject.newInstance( - targetClass, targetInstance, actorRef, remoteAddress, component.timeout).asInstanceOf[AnyRef] - remoteAddress.foreach(address => RemoteServer.registerActiveObject(address, targetClass.getName, proxy)) + + remoteAddress.foreach { address => + actorRef.makeRemote(remoteAddress.get) + RemoteServer.registerTypedActor(address, implementationClass.getName, proxy) + } + + AspectInitRegistry.register( + proxy, + AspectInit(interfaceClass, typedActor, actorRef, remoteAddress, timeout)) + typedActor.initialize(proxy) + actorRef.start + supervised ::= Supervise(actorRef, component.lifeCycle) - activeObjectRegistry.put(targetClass, (proxy, targetInstance, component)) - new DependencyBinding(targetClass, proxy) + + typedActorRegistry.put(interfaceClass, (proxy, typedActor, component)) + new DependencyBinding(interfaceClass, proxy) } - override def inject: ActiveObjectConfiguratorBase = synchronized { + override def inject: TypedActorConfiguratorBase = synchronized { if (injector ne null) throw new IllegalActorStateException("inject() has already been called on this configurator") injector = Guice.createInjector(modules) this } - override def supervise: ActiveObjectConfiguratorBase = synchronized { + override def supervise: TypedActorConfiguratorBase = synchronized { if (injector eq null) inject - supervisor = Some(ActiveObject.supervise(restartStrategy, supervised)) + supervisor = Some(TypedActor.supervise(restartStrategy, supervised)) this } /** * Add additional services to be wired in. *

-   * activeObjectConfigurator.addExternalGuiceModule(new AbstractModule {
+   * typedActorConfigurator.addExternalGuiceModule(new AbstractModule {
    *   protected void configure() {
    *     bind(Foo.class).to(FooImpl.class).in(Scopes.SINGLETON);
    *     bind(BarImpl.class);
@@ -141,7 +161,7 @@ private[akka] class ActiveObjectGuiceConfigurator extends ActiveObjectConfigurat
    *   }})
    * 
*/ - def addExternalGuiceModule(module: Module): ActiveObjectConfiguratorBase = synchronized { + def addExternalGuiceModule(module: Module): TypedActorConfiguratorBase = synchronized { modules.add(module) this } @@ -151,7 +171,7 @@ private[akka] class ActiveObjectGuiceConfigurator extends ActiveObjectConfigurat def reset = synchronized { modules = new java.util.ArrayList[Module] configRegistry = new HashMap[Class[_], Component] - activeObjectRegistry = new HashMap[Class[_], Tuple3[AnyRef, AnyRef, Component]] + typedActorRegistry = new HashMap[Class[_], Tuple3[AnyRef, AnyRef, Component]] methodToUriRegistry = new HashMap[Method, String] injector = null restartStrategy = null diff --git a/akka-core/src/main/scala/dataflow/DataFlowVariable.scala b/akka-core/src/main/scala/dataflow/DataFlowVariable.scala index baf3e33f6e..cd7ba704ad 100644 --- a/akka-core/src/main/scala/dataflow/DataFlowVariable.scala +++ b/akka-core/src/main/scala/dataflow/DataFlowVariable.scala @@ -10,6 +10,7 @@ import java.util.concurrent.{ConcurrentLinkedQueue, LinkedBlockingQueue} import se.scalablesolutions.akka.actor.{Actor, ActorRef} import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.dispatch.CompletableFuture +import se.scalablesolutions.akka.AkkaException /** * Implements Oz-style dataflow (single assignment) variables. @@ -155,7 +156,7 @@ import se.scalablesolutions.akka.dispatch.CompletableFuture /** * @author Jonas Bonér */ - class DataFlowVariableException(msg: String) extends RuntimeException(msg) + class DataFlowVariableException(msg: String) extends AkkaException(msg) } diff --git a/akka-core/src/main/scala/dispatch/Dispatchers.scala b/akka-core/src/main/scala/dispatch/Dispatchers.scala index e938e36e4e..82c51c57de 100644 --- a/akka-core/src/main/scala/dispatch/Dispatchers.scala +++ b/akka-core/src/main/scala/dispatch/Dispatchers.scala @@ -1,11 +1,15 @@ /** - * Copyright (C) 2009-2010 Scalable Solutions AB + * Copyright (C) 2009-2010 Scalable Solutions AB */ package se.scalablesolutions.akka.dispatch import se.scalablesolutions.akka.actor.{Actor, ActorRef} +import se.scalablesolutions.akka.util.Logging import se.scalablesolutions.akka.config.Config.config +import net.lag.configgy.ConfigMap +import se.scalablesolutions.akka.util.UUID +import java.util.concurrent.ThreadPoolExecutor.{AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy, DiscardPolicy} /** * Scala API. Dispatcher factory. @@ -39,9 +43,15 @@ import se.scalablesolutions.akka.config.Config.config * * @author Jonas Bonér */ -object Dispatchers { +object Dispatchers extends Logging { val THROUGHPUT = config.getInt("akka.actor.throughput", 5) + lazy val defaultGlobalDispatcher = { + config.getConfigMap("akka.actor.default-dispatcher").flatMap(from).getOrElse(globalExecutorBasedEventDrivenDispatcher) + } + + object globalHawtDispatcher extends HawtDispatcher + object globalExecutorBasedEventDrivenDispatcher extends ExecutorBasedEventDrivenDispatcher("global") { override def register(actor: ActorRef) = { if (isShutdown) init @@ -50,8 +60,18 @@ object Dispatchers { } object globalReactorBasedSingleThreadEventDrivenDispatcher extends ReactorBasedSingleThreadEventDrivenDispatcher("global") + object globalReactorBasedThreadPoolEventDrivenDispatcher extends ReactorBasedThreadPoolEventDrivenDispatcher("global") + /** + * Creates an event-driven dispatcher based on the excellent HawtDispatch library. + *

+ * Can be beneficial to use the HawtDispatcher.pin(self) to "pin" an actor to a specific thread. + *

+ * See the ScalaDoc for the {@link se.scalablesolutions.akka.dispatch.HawtDispatcher} for details. + */ + def newHawtDispatcher(aggregate: Boolean) = new HawtDispatcher(aggregate) + /** * Creates a executor-based event-driven dispatcher serving multiple (millions) of actors through a thread pool. *

@@ -91,4 +111,74 @@ object Dispatchers { * E.g. each actor consumes its own thread. */ def newThreadBasedDispatcher(actor: ActorRef) = new ThreadBasedDispatcher(actor) + + /** + * Utility function that tries to load the specified dispatcher config from the akka.conf + * or else use the supplied default dispatcher + */ + def fromConfig(key: String, default: => MessageDispatcher = defaultGlobalDispatcher): MessageDispatcher = + config.getConfigMap(key).flatMap(from).getOrElse(default) + + /* + * Creates of obtains a dispatcher from a ConfigMap according to the format below + * + * default-dispatcher { + * type = "GlobalExecutorBasedEventDriven" # Must be one of the following, all "Global*" are non-configurable + * # ReactorBasedSingleThreadEventDriven, (ExecutorBasedEventDrivenWorkStealing), ExecutorBasedEventDriven, + * # ReactorBasedThreadPoolEventDriven, Hawt, GlobalReactorBasedSingleThreadEventDriven, + * # GlobalReactorBasedThreadPoolEventDriven, GlobalExecutorBasedEventDriven, GlobalHawt + * keep-alive-ms = 60000 # Keep alive time for threads + * core-pool-size-factor = 1.0 # No of core threads ... ceil(available processors * factor) + * max-pool-size-factor = 4.0 # Max no of threads ... ceil(available processors * factor) + * executor-bounds = -1 # Makes the Executor bounded, -1 is unbounded + * allow-core-timeout = on # Allow core threads to time out + * rejection-policy = "caller-runs" # abort, caller-runs, discard-oldest, discard + * throughput = 5 # Throughput for ExecutorBasedEventDrivenDispatcher + * aggregate = off # Aggregate on/off for HawtDispatchers + * } + * ex: from(config.getConfigMap(identifier).get) + * + * Gotcha: Only configures the dispatcher if possible + * Returns: None if "type" isn't specified in the config + * Throws: IllegalArgumentException if the value of "type" is not valid + */ + def from(cfg: ConfigMap): Option[MessageDispatcher] = { + lazy val name = cfg.getString("name",UUID.newUuid.toString) + + val dispatcher: Option[MessageDispatcher] = cfg.getString("type") map { + case "ReactorBasedSingleThreadEventDriven" => newReactorBasedSingleThreadEventDrivenDispatcher(name) + case "ExecutorBasedEventDrivenWorkStealing" => newExecutorBasedEventDrivenWorkStealingDispatcher(name) + case "ExecutorBasedEventDriven" => newExecutorBasedEventDrivenDispatcher(name,cfg.getInt("throughput",THROUGHPUT)) + case "ReactorBasedThreadPoolEventDriven" => newReactorBasedThreadPoolEventDrivenDispatcher(name) + case "Hawt" => newHawtDispatcher(cfg.getBool("aggregate").getOrElse(true)) + case "GlobalReactorBasedSingleThreadEventDriven" => globalReactorBasedSingleThreadEventDrivenDispatcher + case "GlobalReactorBasedThreadPoolEventDriven" => globalReactorBasedThreadPoolEventDrivenDispatcher + case "GlobalExecutorBasedEventDriven" => globalExecutorBasedEventDrivenDispatcher + case "GlobalHawt" => globalHawtDispatcher + + case unknown => throw new IllegalArgumentException("Unknown dispatcher type %s" format unknown) + } + + dispatcher foreach { + case d: ThreadPoolBuilder => d.configureIfPossible( builder => { + + cfg.getInt("keep-alive-ms").foreach(builder.setKeepAliveTimeInMillis(_)) + cfg.getDouble("core-pool-size-factor").foreach(builder.setCorePoolSizeFromFactor(_)) + cfg.getDouble("max-pool-size-factor").foreach(builder.setMaxPoolSizeFromFactor(_)) + cfg.getInt("executor-bounds").foreach(builder.setExecutorBounds(_)) + cfg.getBool("allow-core-timeout").foreach(builder.setAllowCoreThreadTimeout(_)) + + cfg.getString("rejection-policy").map({ + case "abort" => new AbortPolicy() + case "caller-runs" => new CallerRunsPolicy() + case "discard-oldest" => new DiscardOldestPolicy() + case "discard" => new DiscardPolicy() + case x => throw new IllegalArgumentException("[%s] is not a valid rejectionPolicy!" format x) + }).foreach(builder.setRejectionPolicy(_)) + }) + case _ => + } + + dispatcher + } } diff --git a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala index 1d34083e0a..1f03c1eba2 100644 --- a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala +++ b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala @@ -5,6 +5,7 @@ package se.scalablesolutions.akka.dispatch import se.scalablesolutions.akka.actor.{ActorRef, IllegalActorStateException} +import jsr166x.ConcurrentLinkedDeque /** * Default settings are: @@ -67,15 +68,34 @@ class ExecutorBasedEventDrivenDispatcher(_name: String, throughput: Int = Dispat val name = "akka:event-driven:dispatcher:" + _name init - def dispatch(invocation: MessageInvocation) = dispatch(invocation.receiver) + def dispatch(invocation: MessageInvocation) = { + getMailbox(invocation.receiver).add(invocation) + dispatch(invocation.receiver) + } + + /** + * @return the mailbox associated with the actor + */ + private def getMailbox(receiver: ActorRef) = receiver.mailbox.asInstanceOf[ConcurrentLinkedDeque[MessageInvocation]] + + override def mailboxSize(actorRef: ActorRef) = getMailbox(actorRef).size + + override def register(actorRef: ActorRef) = { + // The actor will need a ConcurrentLinkedDeque based mailbox + if( actorRef.mailbox eq null ) { + actorRef.mailbox = new ConcurrentLinkedDeque[MessageInvocation]() + } + super.register(actorRef) + } def dispatch(receiver: ActorRef): Unit = if (active) { + executor.execute(new Runnable() { def run = { var lockAcquiredOnce = false var finishedBeforeMailboxEmpty = false val lock = receiver.dispatcherLock - val mailbox = receiver.mailbox + val mailbox = getMailbox(receiver) // this do-while loop is required to prevent missing new messages between the end of the inner while // loop and releasing the lock do { @@ -92,8 +112,10 @@ class ExecutorBasedEventDrivenDispatcher(_name: String, throughput: Int = Dispat } while ((lockAcquiredOnce && !finishedBeforeMailboxEmpty && !mailbox.isEmpty)) } }) - } else log.warning( - "%s is shut down,\n\tignoring the rest of the messages in the mailbox of\n\t%s", toString, receiver) + } else { + log.warning("%s is shut down,\n\tignoring the rest of the messages in the mailbox of\n\t%s", toString, receiver) + } + /** * Process the messages in the mailbox of the given actor. @@ -102,15 +124,16 @@ class ExecutorBasedEventDrivenDispatcher(_name: String, throughput: Int = Dispat */ def processMailbox(receiver: ActorRef): Boolean = { var processedMessages = 0 - var messageInvocation = receiver.mailbox.poll + val mailbox = getMailbox(receiver) + var messageInvocation = mailbox.poll while (messageInvocation != null) { messageInvocation.invoke processedMessages += 1 // check if we simply continue with other messages, or reached the throughput limit - if (throughput <= 0 || processedMessages < throughput) messageInvocation = receiver.mailbox.poll + if (throughput <= 0 || processedMessages < throughput) messageInvocation = mailbox.poll else { messageInvocation = null - return !receiver.mailbox.isEmpty + return !mailbox.isEmpty } } false @@ -128,11 +151,9 @@ class ExecutorBasedEventDrivenDispatcher(_name: String, throughput: Int = Dispat references.clear } - def usesActorMailbox = true - def ensureNotActive(): Unit = if (active) throw new IllegalActorStateException( "Can't build a new thread pool for a dispatcher that is already up and running") - + override def toString = "ExecutorBasedEventDrivenDispatcher[" + name + "]" // FIXME: should we have an unbounded queue and not bounded as default ???? diff --git a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala index 76138dce35..b9ff5d92f4 100644 --- a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala +++ b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala @@ -7,6 +7,7 @@ package se.scalablesolutions.akka.dispatch import java.util.concurrent.CopyOnWriteArrayList import se.scalablesolutions.akka.actor.{Actor, ActorRef, IllegalActorStateException} +import jsr166x.ConcurrentLinkedDeque /** * An executor based event driven dispatcher which will try to redistribute work from busy actors to idle actors. It is assumed @@ -44,7 +45,16 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher(_name: String) extends Mess val name = "akka:event-driven-work-stealing:dispatcher:" + _name init + + /** + * @return the mailbox associated with the actor + */ + private def getMailbox(receiver: ActorRef) = receiver.mailbox.asInstanceOf[ConcurrentLinkedDeque[MessageInvocation]] + + override def mailboxSize(actorRef: ActorRef) = getMailbox(actorRef).size + def dispatch(invocation: MessageInvocation) = if (active) { + getMailbox(invocation.receiver).add(invocation) executor.execute(new Runnable() { def run = { if (!tryProcessMailbox(invocation.receiver)) { @@ -76,7 +86,7 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher(_name: String) extends Mess lock.unlock } } - } while ((lockAcquiredOnce && !receiver.mailbox.isEmpty)) + } while ((lockAcquiredOnce && !getMailbox(receiver).isEmpty)) return lockAcquiredOnce } @@ -85,10 +95,11 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher(_name: String) extends Mess * Process the messages in the mailbox of the given actor. */ private def processMailbox(receiver: ActorRef) = { - var messageInvocation = receiver.mailbox.poll + val mailbox = getMailbox(receiver) + var messageInvocation = mailbox.poll while (messageInvocation != null) { messageInvocation.invoke - messageInvocation = receiver.mailbox.poll + messageInvocation = mailbox.poll } } @@ -116,7 +127,7 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher(_name: String) extends Mess for (i <- 0 to actors.length) { val index = (i + startIndex) % actors.length val actor = actors(index) - if (actor != receiver && actor.mailbox.isEmpty) return (Some(actor), index) + if (actor != receiver && getMailbox(actor).isEmpty) return (Some(actor), index) } (None, startIndex) // nothing found, reuse same start index next time } @@ -139,7 +150,7 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher(_name: String) extends Mess * Steal a message from the receiver and give it to the thief. */ private def donateMessage(receiver: ActorRef, thief: ActorRef): Boolean = { - val donated = receiver.mailbox.pollLast + val donated = getMailbox(receiver).pollLast if (donated ne null) { if (donated.senderFuture.isDefined) thief.self.postMessageToMailboxAndCreateFutureResultWithTimeout[Any]( donated.message, receiver.timeout, donated.sender, donated.senderFuture) @@ -164,11 +175,15 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher(_name: String) extends Mess "Can't build a new thread pool for a dispatcher that is already up and running") override def toString = "ExecutorBasedEventDrivenWorkStealingDispatcher[" + name + "]" - + private[akka] def init = withNewThreadPoolWithLinkedBlockingQueueWithUnboundedCapacity.buildThreadPool override def register(actorRef: ActorRef) = { verifyActorsAreOfSameType(actorRef) + // The actor will need a ConcurrentLinkedDeque based mailbox + if( actorRef.mailbox == null ) { + actorRef.mailbox = new ConcurrentLinkedDeque[MessageInvocation]() + } pooledActors.add(actorRef) super.register(actorRef) } @@ -178,8 +193,6 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher(_name: String) extends Mess super.unregister(actorRef) } - def usesActorMailbox = true - private def verifyActorsAreOfSameType(actorOfId: ActorRef) = { actorType match { case None => actorType = Some(actorOfId.actor.getClass) diff --git a/akka-core/src/main/scala/dispatch/Future.scala b/akka-core/src/main/scala/dispatch/Future.scala index d1b4f9572b..17c63bcd57 100644 --- a/akka-core/src/main/scala/dispatch/Future.scala +++ b/akka-core/src/main/scala/dispatch/Future.scala @@ -4,11 +4,12 @@ package se.scalablesolutions.akka.dispatch -import java.util.concurrent.locks.ReentrantLock +import se.scalablesolutions.akka.AkkaException +import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.TimeUnit -class FutureTimeoutException(message: String) extends RuntimeException(message) +class FutureTimeoutException(message: String) extends AkkaException(message) object Futures { @@ -25,7 +26,7 @@ object Futures { try { promise completeWithResult body } catch { - case e => promise completeWithException (None, e) + case e => promise completeWithException e } promise } @@ -77,12 +78,12 @@ sealed trait Future[T] { def isExpired: Boolean def timeoutInNanos: Long def result: Option[T] - def exception: Option[Tuple2[AnyRef, Throwable]] + def exception: Option[Throwable] } trait CompletableFuture[T] extends Future[T] { def completeWithResult(result: T) - def completeWithException(toBlame: AnyRef, exception: Throwable) + def completeWithException(exception: Throwable) } // Based on code from the actorom actor framework by Sergio Bossa [http://code.google.com/p/actorom/]. @@ -96,7 +97,7 @@ class DefaultCompletableFuture[T](timeout: Long) extends CompletableFuture[T] { private val _signal = _lock.newCondition private var _completed: Boolean = _ private var _result: Option[T] = None - private var _exception: Option[Tuple2[AnyRef, Throwable]] = None + private var _exception: Option[Throwable] = None def await = try { _lock.lock @@ -147,7 +148,7 @@ class DefaultCompletableFuture[T](timeout: Long) extends CompletableFuture[T] { _lock.unlock } - def exception: Option[Tuple2[AnyRef, Throwable]] = try { + def exception: Option[Throwable] = try { _lock.lock _exception } finally { @@ -165,11 +166,11 @@ class DefaultCompletableFuture[T](timeout: Long) extends CompletableFuture[T] { _lock.unlock } - def completeWithException(toBlame: AnyRef, exception: Throwable) = try { + def completeWithException(exception: Throwable) = try { _lock.lock if (!_completed) { _completed = true - _exception = Some((toBlame, exception)) + _exception = Some(exception) } } finally { _signal.signalAll diff --git a/akka-core/src/main/scala/dispatch/HawtDispatcher.scala b/akka-core/src/main/scala/dispatch/HawtDispatcher.scala new file mode 100644 index 0000000000..45e4468b3d --- /dev/null +++ b/akka-core/src/main/scala/dispatch/HawtDispatcher.scala @@ -0,0 +1,249 @@ +/** + * Copyright (C) 2010, Progress Software Corporation and/or its + * subsidiaries or affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package se.scalablesolutions.akka.dispatch + +import se.scalablesolutions.akka.actor.ActorRef +import org.fusesource.hawtdispatch.DispatchQueue +import org.fusesource.hawtdispatch.ScalaDispatch._ +import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.CountDownLatch +import org.fusesource.hawtdispatch.DispatchQueue.QueueType +import org.fusesource.hawtdispatch.ListEventAggregator + +/** + * Holds helper methods for working with actors that are using + * a HawtDispatcher as it's dispatcher. + */ +object HawtDispatcher { + + private val retained = new AtomicInteger() + @volatile private var shutdownLatch: CountDownLatch = _ + + private def retainNonDaemon = { + if( retained.getAndIncrement == 0 ) { + shutdownLatch = new CountDownLatch(1) + new Thread("HawtDispatch Non-Daemon") { + override def run = { + try { + shutdownLatch.await + } catch { + case _ => + } + println("done"); + } + }.start() + } + } + + private def releaseNonDaemon = { + if( retained.decrementAndGet == 0 ) { + shutdownLatch.countDown + shutdownLatch = null + } + } + + /** + * @return the mailbox associated with the actor + */ + private def mailbox(actorRef: ActorRef) = { + actorRef.mailbox.asInstanceOf[HawtDispatcherMailbox] + } + + /** + * @return the dispatch queue associated with the actor + */ + def queue(actorRef: ActorRef) = { + mailbox(actorRef).queue + } + + + /** + *

+ * Pins an actor to a random thread queue. Once pinned the actor will always execute + * on the same thread. + *

+ * + *

+ * This method can only succeed if the actor it's dispatcher is set to a HawtDispatcher and it has been started + *

+ * + * @return true if the actor was pinned + */ + def pin(actorRef: ActorRef) = { + actorRef.mailbox match { + case x:HawtDispatcherMailbox=> + x.queue.setTargetQueue( getRandomThreadQueue ) + true + case _ => false + } + } + + /** + *

+ * Unpins the actor so that all threads in the hawt dispatch thread pool + * compete to execute him. + *

+ * + *

+ * This method can only succeed if the actor it's dispatcher is set to a HawtDispatcher and it has been started + *

+ * @return true if the actor was unpinned + */ + def unpin(actorRef: ActorRef) = { + target(actorRef, globalQueue) + } + + + /** + * @return true if the actor was pinned to a thread. + */ + def pinned(actorRef: ActorRef):Boolean = { + actorRef.mailbox match { + case x:HawtDispatcherMailbox=> + x.queue.getTargetQueue.getQueueType == QueueType.THREAD_QUEUE + case _ => false + } + } + + /** + *

+ * Updates the actor's target dispatch queue to the value specified. This allows + * you to do odd things like targeting another serial queue. + *

+ * + *

+ * This method can only succeed if the actor it's dispatcher is set to a HawtDispatcher and it has been started + *

+ * @return true if the actor was unpinned + */ + def target(actorRef: ActorRef, parent:DispatchQueue) = { + actorRef.mailbox match { + case x:HawtDispatcherMailbox=> + x.queue.setTargetQueue( parent ) + true + case _ => false + } + } + +} + +/** + *

+ * A HawtDispatch based MessageDispatcher. Actors with this dispatcher are executed + * on the HawtDispatch fixed sized thread pool. The number of of threads will match + * the number of cores available on your system. + * + *

+ *

+ * Actors using this dispatcher are restricted to only executing non blocking + * operations. The actor cannot synchronously call another actor or call 3rd party + * libraries that can block for a long time. You should use non blocking IO APIs + * instead of blocking IO apis to avoid blocking that actor for an extended amount + * of time. + *

+ * + *

+ * This dispatcher delivers messages to the actors in the order that they + * were producer at the sender. + *

+ * + *

+ * HawtDispatch supports processing Non blocking Socket IO in both the reactor + * and proactor styles. For more details, see the HawtDispacherEchoServer.scala + * example. + *

+ * + * @author Hiram Chirino + */ +class HawtDispatcher(val aggregate:Boolean=true, val parent:DispatchQueue=globalQueue) extends MessageDispatcher { + import HawtDispatcher._ + private val active = new AtomicBoolean(false) + + def start = { + if( active.compareAndSet(false, true) ) { + retainNonDaemon + } + } + + def shutdown = { + if( active.compareAndSet(true, false) ) { + releaseNonDaemon + } + } + + def isShutdown = !active.get + + def dispatch(invocation: MessageInvocation) = if(active.get()) { + mailbox(invocation.receiver).dispatch(invocation) + } else { + log.warning("%s is shut down,\n\tignoring the the messages sent to\n\t%s", toString, invocation.receiver) + } + + // hawtdispatch does not have a way to get queue sizes, getting an accurate + // size can cause extra contention.. is this really needed? + // TODO: figure out if this can be optional in akka + override def mailboxSize(actorRef: ActorRef) = 0 + + override def register(actorRef: ActorRef) = { + if( actorRef.mailbox == null ) { + val queue = parent.createSerialQueue(actorRef.toString) + if( aggregate ) { + actorRef.mailbox = new AggregatingHawtDispatcherMailbox(queue) + } else { + actorRef.mailbox = new HawtDispatcherMailbox(queue) + } + } + super.register(actorRef) + } + + override def toString = "HawtDispatchEventDrivenDispatcher" + +} + +class HawtDispatcherMailbox(val queue:DispatchQueue) { + def dispatch(invocation: MessageInvocation):Unit = { + queue { + invocation.invoke + } + } +} + +class AggregatingHawtDispatcherMailbox(queue:DispatchQueue) extends HawtDispatcherMailbox(queue) { + private val source = createSource(new ListEventAggregator[MessageInvocation](), queue) + source.setEventHandler (^{drain_source} ) + source.resume + + private def drain_source = { + source.getData.foreach { invocation => + invocation.invoke + } + } + + override def dispatch(invocation: MessageInvocation):Unit = { + if ( getCurrentQueue == null ) { + // we are being call from a non hawtdispatch thread, can't aggregate + // it's events + super.dispatch(invocation) + } else { + // we are being call from a hawtdispatch thread, use the dispatch source + // so that multiple invocations issues on this thread will aggregate and then once + // the thread runs out of work, they get transferred as a batch to the other thread. + source.merge(invocation) + } + } +} diff --git a/akka-core/src/main/scala/dispatch/MessageHandling.scala b/akka-core/src/main/scala/dispatch/MessageHandling.scala index a73f2b691b..92926bb253 100644 --- a/akka-core/src/main/scala/dispatch/MessageHandling.scala +++ b/akka-core/src/main/scala/dispatch/MessageHandling.scala @@ -79,7 +79,7 @@ trait MessageDispatcher extends Logging { } def canBeShutDown: Boolean = references.isEmpty def isShutdown: Boolean - def usesActorMailbox : Boolean + def mailboxSize(actorRef: ActorRef):Int = 0 } /** diff --git a/akka-core/src/main/scala/dispatch/ReactorBasedSingleThreadEventDrivenDispatcher.scala b/akka-core/src/main/scala/dispatch/ReactorBasedSingleThreadEventDrivenDispatcher.scala index 8a951a4e72..d0850aa830 100644 --- a/akka-core/src/main/scala/dispatch/ReactorBasedSingleThreadEventDrivenDispatcher.scala +++ b/akka-core/src/main/scala/dispatch/ReactorBasedSingleThreadEventDrivenDispatcher.scala @@ -14,7 +14,7 @@ import java.util.{LinkedList, List} class ReactorBasedSingleThreadEventDrivenDispatcher(_name: String) extends AbstractReactorBasedEventDrivenDispatcher("akka:event-driven:reactor:single-thread:dispatcher:" + _name) { - + def start = if (!active) { log.debug("Starting up %s", toString) active = true @@ -41,8 +41,6 @@ class ReactorBasedSingleThreadEventDrivenDispatcher(_name: String) def isShutdown = !active - def usesActorMailbox = false - override def toString = "ReactorBasedSingleThreadEventDrivenDispatcher[" + name + "]" class Demultiplexer(private val messageQueue: ReactiveMessageQueue) extends MessageDemultiplexer { diff --git a/akka-core/src/main/scala/dispatch/ReactorBasedThreadPoolEventDrivenDispatcher.scala b/akka-core/src/main/scala/dispatch/ReactorBasedThreadPoolEventDrivenDispatcher.scala index 370426b2fd..530184d4b2 100644 --- a/akka-core/src/main/scala/dispatch/ReactorBasedThreadPoolEventDrivenDispatcher.scala +++ b/akka-core/src/main/scala/dispatch/ReactorBasedThreadPoolEventDrivenDispatcher.scala @@ -139,8 +139,6 @@ class ReactorBasedThreadPoolEventDrivenDispatcher(_name: String) else nrOfBusyMessages < 100 } - def usesActorMailbox = false - def ensureNotActive(): Unit = if (active) throw new IllegalActorStateException( "Can't build a new thread pool for a dispatcher that is already up and running") diff --git a/akka-core/src/main/scala/dispatch/ThreadBasedDispatcher.scala b/akka-core/src/main/scala/dispatch/ThreadBasedDispatcher.scala index 7355012b1f..012c4899d8 100644 --- a/akka-core/src/main/scala/dispatch/ThreadBasedDispatcher.scala +++ b/akka-core/src/main/scala/dispatch/ThreadBasedDispatcher.scala @@ -40,8 +40,6 @@ class ThreadBasedDispatcher(private val actor: ActorRef) extends MessageDispatch def isShutdown = !active - def usesActorMailbox = false - def shutdown = if (active) { log.debug("Shutting down %s", toString) active = false diff --git a/akka-core/src/main/scala/dispatch/ThreadPoolBuilder.scala b/akka-core/src/main/scala/dispatch/ThreadPoolBuilder.scala index a111ae87a5..7cc96400a8 100644 --- a/akka-core/src/main/scala/dispatch/ThreadPoolBuilder.scala +++ b/akka-core/src/main/scala/dispatch/ThreadPoolBuilder.scala @@ -10,9 +10,9 @@ import atomic.{AtomicLong, AtomicInteger} import ThreadPoolExecutor.CallerRunsPolicy import se.scalablesolutions.akka.actor.IllegalActorStateException -import se.scalablesolutions.akka.util.Logging +import se.scalablesolutions.akka.util.{Logger, Logging} -trait ThreadPoolBuilder { +trait ThreadPoolBuilder extends Logging { val name: String private val NR_START_THREADS = 16 @@ -22,7 +22,7 @@ trait ThreadPoolBuilder { private var threadPoolBuilder: ThreadPoolExecutor = _ private var boundedExecutorBound = -1 - private var inProcessOfBuilding = false + @volatile private var inProcessOfBuilding = false private var blockingQueue: BlockingQueue[Runnable] = _ private lazy val threadFactory = new MonitorableThreadFactory(name) @@ -34,6 +34,15 @@ trait ThreadPoolBuilder { def buildThreadPool(): Unit = synchronized { ensureNotActive inProcessOfBuilding = false + + log.debug("Creating a %s with config [core-pool:%d,max-pool:%d,timeout:%d,allowCoreTimeout:%s,rejectPolicy:%s]", + getClass.getName, + threadPoolBuilder.getCorePoolSize, + threadPoolBuilder.getMaximumPoolSize, + threadPoolBuilder.getKeepAliveTime(MILLISECONDS), + threadPoolBuilder.allowsCoreThreadTimeOut, + threadPoolBuilder.getRejectedExecutionHandler.getClass.getSimpleName) + if (boundedExecutorBound > 0) { val boundedExecutor = new BoundedExecutorDecorator(threadPoolBuilder, boundedExecutorBound) boundedExecutorBound = -1 @@ -102,46 +111,81 @@ trait ThreadPoolBuilder { this } + def configureIfPossible(f: (ThreadPoolBuilder) => Unit): Boolean = synchronized { + if(inProcessOfBuilding) { + f(this) + true + } + else { + log.warning("Tried to configure an already started ThreadPoolBuilder of type [%s]",getClass.getName) + false + } + } + /** * Default is 16. */ - def setCorePoolSize(size: Int): ThreadPoolBuilder = synchronized { - ensureNotActive - verifyInConstructionPhase - threadPoolBuilder.setCorePoolSize(size) - this - } + def setCorePoolSize(size: Int): ThreadPoolBuilder = + setThreadPoolExecutorProperty(_.setCorePoolSize(size)) /** * Default is 128. */ - def setMaxPoolSize(size: Int): ThreadPoolBuilder = synchronized { - ensureNotActive - verifyInConstructionPhase - threadPoolBuilder.setMaximumPoolSize(size) - this - } + def setMaxPoolSize(size: Int): ThreadPoolBuilder = + setThreadPoolExecutorProperty(_.setMaximumPoolSize(size)) + /** - * Default is 60000 (one minute). + * Sets the core pool size to (availableProcessors * multipliers).ceil.toInt */ - def setKeepAliveTimeInMillis(time: Long): ThreadPoolBuilder = synchronized { - ensureNotActive - verifyInConstructionPhase - threadPoolBuilder.setKeepAliveTime(time, MILLISECONDS) - this + def setCorePoolSizeFromFactor(multiplier: Double): ThreadPoolBuilder = + setThreadPoolExecutorProperty(_.setCorePoolSize(procs(multiplier))) + + /** + * Sets the max pool size to (availableProcessors * multipliers).ceil.toInt + */ + def setMaxPoolSizeFromFactor(multiplier: Double): ThreadPoolBuilder = + setThreadPoolExecutorProperty(_.setMaximumPoolSize(procs(multiplier))) + + /** + * Sets the bound, -1 is unbounded + */ + def setExecutorBounds(bounds: Int): Unit = synchronized { + this.boundedExecutorBound = bounds } + + protected def procs(multiplier: Double): Int = + (Runtime.getRuntime.availableProcessors * multiplier).ceil.toInt + + /** + * Default is 60000 (one minute). + */ + def setKeepAliveTimeInMillis(time: Long): ThreadPoolBuilder = + setThreadPoolExecutorProperty(_.setKeepAliveTime(time, MILLISECONDS)) /** * Default ThreadPoolExecutor.CallerRunsPolicy. To allow graceful backing off when pool is overloaded. */ - def setRejectionPolicy(policy: RejectedExecutionHandler): ThreadPoolBuilder = synchronized { + def setRejectionPolicy(policy: RejectedExecutionHandler): ThreadPoolBuilder = + setThreadPoolExecutorProperty(_.setRejectedExecutionHandler(policy)) + + /** + * Default false, set to true to conserve thread for potentially unused dispatchers + */ + def setAllowCoreThreadTimeout(allow: Boolean) = + setThreadPoolExecutorProperty(_.allowCoreThreadTimeOut(allow)) + + /** + * Default ThreadPoolExecutor.CallerRunsPolicy. To allow graceful backing off when pool is overloaded. + */ + protected def setThreadPoolExecutorProperty(f: (ThreadPoolExecutor) => Unit): ThreadPoolBuilder = synchronized { ensureNotActive verifyInConstructionPhase - threadPoolBuilder.setRejectedExecutionHandler(policy) + f(threadPoolBuilder) this } + protected def verifyNotInConstructionPhase = { if (inProcessOfBuilding) throw new IllegalActorStateException("Is already in the process of building a thread pool") inProcessOfBuilding = true @@ -234,18 +278,19 @@ trait ThreadPoolBuilder { extends Thread(runnable, name + "-" + MonitorableThread.created.incrementAndGet) with Logging { setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { - def uncaughtException(thread: Thread, cause: Throwable) = log.error(cause, "UNCAUGHT in thread [%s]", thread.getName) + def uncaughtException(thread: Thread, cause: Throwable) = + log.error(cause, "UNCAUGHT in thread [%s]", thread.getName) }) override def run = { val debug = MonitorableThread.debugLifecycle - log.debug("Created %s", getName) + log.debug("Created thread %s", getName) try { MonitorableThread.alive.incrementAndGet super.run } finally { MonitorableThread.alive.decrementAndGet - log.debug("Exiting %s", getName) + log.debug("Exiting thread %s", getName) } } } diff --git a/akka-core/src/main/scala/remote/BootableRemoteActorService.scala b/akka-core/src/main/scala/remote/BootableRemoteActorService.scala index a971652590..643dbd2f33 100644 --- a/akka-core/src/main/scala/remote/BootableRemoteActorService.scala +++ b/akka-core/src/main/scala/remote/BootableRemoteActorService.scala @@ -25,14 +25,14 @@ trait BootableRemoteActorService extends Bootable with Logging { def startRemoteService = remoteServerThread.start - abstract override def onLoad = { - super.onLoad //Initialize BootableActorLoaderService before remote service + abstract override def onLoad = { if (config.getBool("akka.remote.server.service", true)) { if (config.getBool("akka.remote.cluster.service", true)) Cluster.start(self.applicationLoader) log.info("Initializing Remote Actors Service...") startRemoteService log.info("Remote Actors Service initialized") } + super.onLoad } abstract override def onUnload = { 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 4c18dcc6c8..2c810068a2 100644 --- a/akka-core/src/main/scala/remote/RemoteClient.scala +++ b/akka-core/src/main/scala/remote/RemoteClient.scala @@ -7,8 +7,8 @@ package se.scalablesolutions.akka.remote import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._ import se.scalablesolutions.akka.actor.{Exit, Actor, ActorRef, RemoteActorRef, IllegalActorStateException} import se.scalablesolutions.akka.dispatch.{DefaultCompletableFuture, CompletableFuture} -import se.scalablesolutions.akka.util.{UUID, Logging} -import se.scalablesolutions.akka.config.Config.config +import se.scalablesolutions.akka.config.Config._ +import se.scalablesolutions.akka.AkkaException import org.jboss.netty.channel._ import group.DefaultChannelGroup @@ -19,12 +19,15 @@ import org.jboss.netty.handler.codec.compression.{ZlibDecoder, ZlibEncoder} import org.jboss.netty.handler.codec.protobuf.{ProtobufDecoder, ProtobufEncoder} import org.jboss.netty.handler.timeout.ReadTimeoutHandler import org.jboss.netty.util.{TimerTask, Timeout, HashedWheelTimer} +import org.jboss.netty.handler.ssl.SslHandler import java.net.{SocketAddress, InetSocketAddress} import java.util.concurrent.{TimeUnit, Executors, ConcurrentMap, ConcurrentHashMap, ConcurrentSkipListSet} import java.util.concurrent.atomic.AtomicLong import scala.collection.mutable.{HashSet, HashMap} +import scala.reflect.BeanProperty +import se.scalablesolutions.akka.util.{ListenerManagement, UUID, Logging, Duration} /** * Atomic remote request/reply message id generator. @@ -42,48 +45,59 @@ object RemoteRequestProtocolIdFactory { * Life-cycle events for RemoteClient. */ sealed trait RemoteClientLifeCycleEvent -case class RemoteClientError(cause: Throwable, host: String, port: Int) extends RemoteClientLifeCycleEvent -case class RemoteClientDisconnected(host: String, port: Int) extends RemoteClientLifeCycleEvent -case class RemoteClientConnected(host: String, 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 AkkaException(message) /** + * The RemoteClient object manages RemoteClient instances and gives you an API to lookup remote actor handles. + * * @author Jonas Bonér */ object RemoteClient extends Logging { - val READ_TIMEOUT = config.getInt("akka.remote.client.read-timeout", 10000) - val RECONNECT_DELAY = config.getInt("akka.remote.client.reconnect-delay", 5000) + val READ_TIMEOUT = Duration(config.getInt("akka.remote.client.read-timeout", 1), TIME_UNIT) + val RECONNECT_DELAY = Duration(config.getInt("akka.remote.client.reconnect-delay", 5), TIME_UNIT) private val remoteClients = new HashMap[String, RemoteClient] - private val remoteActors = new HashMap[RemoteServer.Address, HashSet[String]] + private val remoteActors = new HashMap[RemoteServer.Address, HashSet[String]] // FIXME: simplify overloaded methods when we have Scala 2.8 - def actorFor(className: String, hostname: String, port: Int): ActorRef = - actorFor(className, className, 5000L, hostname, port, None) + def actorFor(classNameOrServiceId: String, hostname: String, port: Int): ActorRef = + actorFor(classNameOrServiceId, classNameOrServiceId, 5000L, hostname, port, None) - def actorFor(className: String, hostname: String, port: Int, loader: ClassLoader): ActorRef = - actorFor(className, className, 5000L, hostname, port, Some(loader)) + def actorFor(classNameOrServiceId: String, hostname: String, port: Int, loader: ClassLoader): ActorRef = + actorFor(classNameOrServiceId, classNameOrServiceId, 5000L, hostname, port, Some(loader)) - def actorFor(uuid: String, className: String, hostname: String, port: Int): ActorRef = - actorFor(uuid, className, 5000L, hostname, port, None) + def actorFor(serviceId: String, className: String, hostname: String, port: Int): ActorRef = + actorFor(serviceId, className, 5000L, hostname, port, None) - def actorFor(uuid: String, className: String, hostname: String, port: Int, loader: ClassLoader): ActorRef = - actorFor(uuid, className, 5000L, hostname, port, Some(loader)) + def actorFor(serviceId: String, className: String, hostname: String, port: Int, loader: ClassLoader): ActorRef = + actorFor(serviceId, className, 5000L, hostname, port, Some(loader)) - def actorFor(className: String, timeout: Long, hostname: String, port: Int): ActorRef = - actorFor(className, className, timeout, hostname, port, None) + def actorFor(classNameOrServiceId: String, timeout: Long, hostname: String, port: Int): ActorRef = + actorFor(classNameOrServiceId, classNameOrServiceId, timeout, hostname, port, None) - def actorFor(className: String, timeout: Long, hostname: String, port: Int, loader: ClassLoader): ActorRef = - actorFor(className, className, timeout, hostname, port, Some(loader)) + def actorFor(classNameOrServiceId: String, timeout: Long, hostname: String, port: Int, loader: ClassLoader): ActorRef = + actorFor(classNameOrServiceId, classNameOrServiceId, timeout, hostname, port, Some(loader)) - def actorFor(uuid: String, className: String, timeout: Long, hostname: String, port: Int): ActorRef = - RemoteActorRef(uuid, className, hostname, port, timeout, None) + def actorFor(serviceId: String, className: String, timeout: Long, hostname: String, port: Int): ActorRef = + RemoteActorRef(serviceId, className, hostname, port, timeout, None) - private[akka] def actorFor(uuid: String, className: String, timeout: Long, hostname: String, port: Int, loader: ClassLoader): ActorRef = - RemoteActorRef(uuid, className, hostname, port, timeout, Some(loader)) + private[akka] def actorFor(serviceId: String, className: String, timeout: Long, hostname: String, port: Int, loader: ClassLoader): ActorRef = + RemoteActorRef(serviceId, className, hostname, port, timeout, Some(loader)) - private[akka] def actorFor(uuid: String, className: String, timeout: Long, hostname: String, port: Int, loader: Option[ClassLoader]): ActorRef = - RemoteActorRef(uuid, className, hostname, port, timeout, loader) + private[akka] def actorFor(serviceId: String, className: String, timeout: Long, hostname: String, port: Int, loader: Option[ClassLoader]): ActorRef = + RemoteActorRef(serviceId, className, hostname, port, timeout, loader) def clientFor(hostname: String, port: Int): RemoteClient = clientFor(new InetSocketAddress(hostname, port), None) @@ -137,7 +151,7 @@ object RemoteClient extends Logging { actorsFor(RemoteServer.Address(hostname, port)) += uuid } - // TODO: add RemoteClient.unregister for ActiveObject, but first need a @shutdown callback + // TODO: add RemoteClient.unregister for TypedActor, but first need a @shutdown callback private[akka] def unregister(hostname: String, port: Int, uuid: String) = synchronized { val set = actorsFor(RemoteServer.Address(hostname, port)) set -= uuid @@ -156,15 +170,16 @@ object RemoteClient extends Logging { } /** + * RemoteClient represents a connection to a RemoteServer. Is used to send messages to remote actors on the RemoteServer. + * * @author Jonas Bonér */ -class RemoteClient private[akka] (val hostname: String, val port: Int, loader: Option[ClassLoader]) extends Logging { +class RemoteClient private[akka] (val hostname: String, val port: Int, val loader: Option[ClassLoader] = None) extends Logging with ListenerManagement { val name = "RemoteClient@" + hostname + "::" + port @volatile private[remote] var isRunning = false private val futures = new ConcurrentHashMap[Long, CompletableFuture[_]] private val supervisors = new ConcurrentHashMap[String, ActorRef] - private[remote] val listeners = new ConcurrentSkipListSet[ActorRef] private val channelFactory = new NioClientSocketChannelFactory( Executors.newCachedThreadPool, @@ -189,7 +204,7 @@ class RemoteClient private[akka] (val hostname: String, val port: Int, loader: O val channel = connection.awaitUninterruptibly.getChannel openChannels.add(channel) if (!connection.isSuccess) { - listeners.toArray.foreach(l => l.asInstanceOf[ActorRef] ! RemoteClientError(connection.getCause, hostname, port)) + foreachListener(l => l ! RemoteClientError(connection.getCause, hostname, port)) log.error(connection.getCause, "Remote client connection to [%s:%s] has failed", hostname, port) } isRunning = true @@ -206,9 +221,13 @@ class RemoteClient private[akka] (val hostname: String, val port: Int, loader: O } } - def registerListener(actorRef: ActorRef) = listeners.add(actorRef) + @deprecated("Use addListener instead") + def registerListener(actorRef: ActorRef) = addListener(actorRef) - def deregisterListener(actorRef: ActorRef) = listeners.remove(actorRef) + @deprecated("Use removeListener instead") + def deregisterListener(actorRef: ActorRef) = removeListener(actorRef) + + override def foreachListener(f: (ActorRef) => Unit): Unit = super.foreachListener(f) def send[T](request: RemoteRequestProtocol, senderFuture: Option[CompletableFuture[T]]): Option[CompletableFuture[T]] = if (isRunning) { if (request.getIsOneWay) { @@ -217,53 +236,61 @@ class RemoteClient private[akka] (val hostname: String, val port: Int, loader: O } else { futures.synchronized { val futureResult = if (senderFuture.isDefined) senderFuture.get - else new DefaultCompletableFuture[T](request.getTimeout) + else new DefaultCompletableFuture[T](request.getActorInfo.getTimeout) futures.put(request.getId, futureResult) connection.getChannel.write(request) Some(futureResult) } } } else { - val exception = new IllegalStateException("Remote client is not running, make sure you have invoked 'RemoteClient.connect' before using it.") - listeners.toArray.foreach(l => l.asInstanceOf[ActorRef] ! RemoteClientError(exception, hostname, port)) + val exception = new RemoteClientException("Remote client is not running, make sure you have invoked 'RemoteClient.connect' before using it.") + foreachListener(l => l ! RemoteClientError(exception, hostname, port)) throw exception } private[akka] def registerSupervisorForActor(actorRef: ActorRef) = - if (!actorRef.supervisor.isDefined) throw new IllegalActorStateException("Can't register supervisor for " + actorRef + " since it is not under supervision") + if (!actorRef.supervisor.isDefined) throw new IllegalActorStateException( + "Can't register supervisor for " + actorRef + " since it is not under supervision") else supervisors.putIfAbsent(actorRef.supervisor.get.uuid, actorRef) private[akka] def deregisterSupervisorForActor(actorRef: ActorRef) = - if (!actorRef.supervisor.isDefined) throw new IllegalActorStateException("Can't unregister supervisor for " + actorRef + " since it is not under supervision") + if (!actorRef.supervisor.isDefined) throw new IllegalActorStateException( + "Can't unregister supervisor for " + actorRef + " since it is not under supervision") else supervisors.remove(actorRef.supervisor.get.uuid) } /** * @author Jonas Bonér */ -class RemoteClientPipelineFactory(name: String, - futures: ConcurrentMap[Long, CompletableFuture[_]], - supervisors: ConcurrentMap[String, ActorRef], - bootstrap: ClientBootstrap, - remoteAddress: SocketAddress, - timer: HashedWheelTimer, - client: RemoteClient) extends ChannelPipelineFactory { +class RemoteClientPipelineFactory( + name: String, + futures: ConcurrentMap[Long, CompletableFuture[_]], + supervisors: ConcurrentMap[String, ActorRef], + bootstrap: ClientBootstrap, + remoteAddress: SocketAddress, + timer: HashedWheelTimer, + client: RemoteClient) extends ChannelPipelineFactory { + def getPipeline: ChannelPipeline = { - val timeout = new ReadTimeoutHandler(timer, RemoteClient.READ_TIMEOUT) - val lenDec = new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4) - val lenPrep = new LengthFieldPrepender(4) + 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 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 zipCodec = RemoteServer.COMPRESSION_SCHEME match { - case "zlib" => Some(Codec(new ZlibEncoder(RemoteServer.ZLIB_COMPRESSION_LEVEL), new ZlibDecoder)) - //case "lzf" => Some(Codec(new LzfEncoder, new LzfDecoder)) - case _ => None + 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) - val stages: Array[ChannelHandler] = - zipCodec.map(codec => Array(timeout, codec.decoder, lenDec, protobufDec, codec.encoder, lenPrep, protobufEnc, remoteClient)) - .getOrElse(Array(timeout, lenDec, protobufDec, lenPrep, protobufEnc, remoteClient)) + val remoteClient = new RemoteClientHandler(name, futures, supervisors, bootstrap, remoteAddress, timer, client) + val stages = ssl ++ join(timeout) ++ dec ++ join(lenDec, protobufDec) ++ enc ++ join(lenPrep, protobufEnc, remoteClient) new StaticChannelPipeline(stages: _*) } } @@ -272,13 +299,14 @@ class RemoteClientPipelineFactory(name: String, * @author Jonas Bonér */ @ChannelHandler.Sharable -class RemoteClientHandler(val name: String, - val futures: ConcurrentMap[Long, CompletableFuture[_]], - val supervisors: ConcurrentMap[String, ActorRef], - val bootstrap: ClientBootstrap, - val remoteAddress: SocketAddress, - val timer: HashedWheelTimer, - val client: RemoteClient) +class RemoteClientHandler( + val name: String, + val futures: ConcurrentMap[Long, CompletableFuture[_]], + val supervisors: ConcurrentMap[String, ActorRef], + val bootstrap: ClientBootstrap, + val remoteAddress: SocketAddress, + val timer: HashedWheelTimer, + val client: RemoteClient) extends SimpleChannelUpstreamHandler with Logging { override def handleUpstream(ctx: ChannelHandlerContext, event: ChannelEvent) = { @@ -307,19 +335,19 @@ class RemoteClientHandler(val name: String, val supervisedActor = supervisors.get(supervisorUuid) if (!supervisedActor.supervisor.isDefined) throw new IllegalActorStateException( "Can't handle restart for remote actor " + supervisedActor + " since its supervisor has been removed") - else supervisedActor.supervisor.get ! Exit(supervisedActor, parseException(reply)) + else supervisedActor.supervisor.get ! Exit(supervisedActor, parseException(reply, client.loader)) } - future.completeWithException(null, parseException(reply)) + future.completeWithException(parseException(reply, client.loader)) } futures.remove(reply.getId) } else { - val exception = new IllegalArgumentException("Unknown message received in remote client handler: " + result) - client.listeners.toArray.foreach(l => l.asInstanceOf[ActorRef] ! RemoteClientError(exception, client.hostname, client.port)) + val exception = new RemoteClientException("Unknown message received in remote client handler: " + result) + client.foreachListener(l => l ! RemoteClientError(exception, client.hostname, client.port)) throw exception } } catch { case e: Exception => - client.listeners.toArray.foreach(l => l.asInstanceOf[ActorRef] ! RemoteClientError(e, client.hostname, client.port)) + client.foreachListener(l => l ! RemoteClientError(e, client.hostname, client.port)) log.error("Unexpected exception in remote client handler: %s", e) throw e } @@ -333,35 +361,47 @@ class RemoteClientHandler(val name: String, client.connection = bootstrap.connect(remoteAddress) client.connection.awaitUninterruptibly // Wait until the connection attempt succeeds or fails. if (!client.connection.isSuccess) { - client.listeners.toArray.foreach(l => + client.foreachListener(l => l.asInstanceOf[ActorRef] ! RemoteClientError(client.connection.getCause, client.hostname, client.port)) log.error(client.connection.getCause, "Reconnection to [%s] has failed", remoteAddress) } } - }, RemoteClient.RECONNECT_DELAY, TimeUnit.MILLISECONDS) + }, RemoteClient.RECONNECT_DELAY.toMillis, TimeUnit.MILLISECONDS) } override def channelConnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) = { - client.listeners.toArray.foreach(l => - l.asInstanceOf[ActorRef] ! RemoteClientConnected(client.hostname, client.port)) - log.debug("Remote client connected to [%s]", ctx.getChannel.getRemoteAddress) + def connect = { + client.foreachListener(l => l ! RemoteClientConnected(client.hostname, client.port)) + log.debug("Remote client connected to [%s]", ctx.getChannel.getRemoteAddress) + } + + if (RemoteServer.SECURE) { + val sslHandler: SslHandler = ctx.getPipeline.get(classOf[SslHandler]) + sslHandler.handshake.addListener(new ChannelFutureListener { + def operationComplete(future: ChannelFuture): Unit = { + if (future.isSuccess) connect + else throw new RemoteClientException("Could not establish SSL handshake") + } + }) + } else connect } override def channelDisconnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) = { - client.listeners.toArray.foreach(l => - l.asInstanceOf[ActorRef] ! RemoteClientDisconnected(client.hostname, client.port)) + client.foreachListener(l => l ! RemoteClientDisconnected(client.hostname, client.port)) log.debug("Remote client disconnected from [%s]", ctx.getChannel.getRemoteAddress) } override def exceptionCaught(ctx: ChannelHandlerContext, event: ExceptionEvent) = { - client.listeners.toArray.foreach(l => l.asInstanceOf[ActorRef] ! RemoteClientError(event.getCause, client.hostname, client.port)) + client.foreachListener(l => l ! RemoteClientError(event.getCause, client.hostname, client.port)) log.error(event.getCause, "Unexpected exception from downstream in remote client") event.getChannel.close } - private def parseException(reply: RemoteReplyProtocol): Throwable = { + private def parseException(reply: RemoteReplyProtocol, loader: Option[ClassLoader]): Throwable = { val exception = reply.getException - val exceptionClass = Class.forName(exception.getClassname) + val classname = exception.getClassname + val exceptionClass = if (loader.isDefined) loader.get.loadClass(classname) + else Class.forName(classname) exceptionClass .getConstructor(Array[Class[_]](classOf[String]): _*) .newInstance(exception.getMessage).asInstanceOf[Throwable] diff --git a/akka-core/src/main/scala/remote/RemoteServer.scala b/akka-core/src/main/scala/remote/RemoteServer.scala index 4283945de9..1bc443eab9 100644 --- a/akka-core/src/main/scala/remote/RemoteServer.scala +++ b/akka-core/src/main/scala/remote/RemoteServer.scala @@ -13,7 +13,7 @@ import se.scalablesolutions.akka.actor._ import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.util._ import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._ -import se.scalablesolutions.akka.config.Config.config +import se.scalablesolutions.akka.config.Config._ import org.jboss.netty.bootstrap.ServerBootstrap import org.jboss.netty.channel._ @@ -22,8 +22,11 @@ import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory import org.jboss.netty.handler.codec.frame.{LengthFieldBasedFrameDecoder, LengthFieldPrepender} import org.jboss.netty.handler.codec.protobuf.{ProtobufDecoder, ProtobufEncoder} import org.jboss.netty.handler.codec.compression.{ZlibEncoder, ZlibDecoder} +import org.jboss.netty.handler.ssl.SslHandler + import scala.collection.mutable.Map +import reflect.BeanProperty /** * Use this object if you need a single remote server on a specific node. @@ -55,7 +58,7 @@ object RemoteNode extends RemoteServer /** * For internal use only. - * Holds configuration variables, remote actors, remote active objects and remote servers. + * Holds configuration variables, remote actors, remote typed actors and remote servers. * * @author Jonas Bonér */ @@ -63,7 +66,7 @@ object RemoteServer { val HOSTNAME = config.getString("akka.remote.server.hostname", "localhost") val PORT = config.getInt("akka.remote.server.port", 9999) - val CONNECTION_TIMEOUT_MILLIS = config.getInt("akka.remote.server.connection-timeout", 1000) + val CONNECTION_TIMEOUT_MILLIS = Duration(config.getInt("akka.remote.server.connection-timeout", 1), TIME_UNIT) val COMPRESSION_SCHEME = config.getString("akka.remote.compression-scheme", "zlib") val ZLIB_COMPRESSION_LEVEL = { @@ -73,9 +76,32 @@ object RemoteServer { level } + val SECURE = { + /*if (config.getBool("akka.remote.ssl.service",false)) { + val properties = List( + ("key-store-type" , "keyStoreType"), + ("key-store" , "keyStore"), + ("key-store-pass" , "keyStorePassword"), + ("trust-store-type", "trustStoreType"), + ("trust-store" , "trustStore"), + ("trust-store-pass", "trustStorePassword") + ).map(x => ("akka.remote.ssl." + x._1, "javax.net.ssl." + x._2)) + + // If property is not set, and we have a value from our akka.conf, use that value + for { + p <- properties if System.getProperty(p._2) eq null + c <- config.getString(p._1) + } System.setProperty(p._2, c) + + if (config.getBool("akka.remote.ssl.debug", false)) System.setProperty("javax.net.debug","ssl") + true + } else */false + } + object Address { def apply(hostname: String, port: Int) = new Address(hostname, port) } + class Address(val hostname: String, val port: Int) { override def hashCode: Int = { var result = HashCode.SEED @@ -92,8 +118,8 @@ object RemoteServer { } private class RemoteActorSet { - private[RemoteServer] val actors = new ConcurrentHashMap[String, ActorRef] - private[RemoteServer] val activeObjects = new ConcurrentHashMap[String, AnyRef] + private[RemoteServer] val actors = new ConcurrentHashMap[String, ActorRef] + private[RemoteServer] val typedActors = new ConcurrentHashMap[String, AnyRef] } private val guard = new ReadWriteGuard @@ -104,8 +130,8 @@ object RemoteServer { actorsFor(RemoteServer.Address(address.getHostName, address.getPort)).actors.put(uuid, actor) } - private[akka] def registerActiveObject(address: InetSocketAddress, name: String, activeObject: AnyRef) = guard.withWriteGuard { - actorsFor(RemoteServer.Address(address.getHostName, address.getPort)).activeObjects.put(name, activeObject) + private[akka] def registerTypedActor(address: InetSocketAddress, name: String, typedActor: AnyRef) = guard.withWriteGuard { + actorsFor(RemoteServer.Address(address.getHostName, address.getPort)).typedActors.put(name, typedActor) } private[akka] def getOrCreateServer(address: InetSocketAddress): RemoteServer = guard.withWriteGuard { @@ -135,6 +161,28 @@ object RemoteServer { } } +/** + * Life-cycle events for RemoteServer. + */ +sealed trait RemoteServerLifeCycleEvent +case class RemoteServerError( + @BeanProperty val cause: Throwable, + @BeanProperty val host: String, + @BeanProperty val port: Int) extends RemoteServerLifeCycleEvent +case class RemoteServerShutdown( + @BeanProperty val host: String, + @BeanProperty val port: Int) extends RemoteServerLifeCycleEvent +case class RemoteServerStarted( + @BeanProperty val host: String, + @BeanProperty val port: Int) extends RemoteServerLifeCycleEvent +/*FIXME NOT SUPPORTED YET + case class RemoteServerClientConnected( + @BeanProperty val host: String, + @BeanProperty val port: Int) extends RemoteServerLifeCycleEvent +case class RemoteServerClientDisconnected( + @BeanProperty val host: String, + @BeanProperty val port: Int) extends RemoteServerLifeCycleEvent*/ + /** * Use this class if you need a more than one remote server on a specific node. * @@ -151,11 +199,11 @@ object RemoteServer { * * @author Jonas Bonér */ -class RemoteServer extends Logging { +class RemoteServer extends Logging with ListenerManagement { val name = "RemoteServer@" + hostname + ":" + port - private var hostname = RemoteServer.HOSTNAME - private var port = RemoteServer.PORT + private[akka] var hostname = RemoteServer.HOSTNAME + private[akka] var port = RemoteServer.PORT @volatile private var _isRunning = false @@ -197,18 +245,21 @@ class RemoteServer extends Logging { RemoteServer.register(hostname, port, this) val remoteActorSet = RemoteServer.actorsFor(RemoteServer.Address(hostname, port)) val pipelineFactory = new RemoteServerPipelineFactory( - name, openChannels, loader, remoteActorSet.actors, remoteActorSet.activeObjects) + name, openChannels, loader, remoteActorSet.actors, remoteActorSet.typedActors,this) bootstrap.setPipelineFactory(pipelineFactory) bootstrap.setOption("child.tcpNoDelay", true) bootstrap.setOption("child.keepAlive", true) bootstrap.setOption("child.reuseAddress", true) - bootstrap.setOption("child.connectTimeoutMillis", RemoteServer.CONNECTION_TIMEOUT_MILLIS) + bootstrap.setOption("child.connectTimeoutMillis", RemoteServer.CONNECTION_TIMEOUT_MILLIS.toMillis) openChannels.add(bootstrap.bind(new InetSocketAddress(hostname, port))) _isRunning = true Cluster.registerLocalNode(hostname, port) + foreachListener(_ ! RemoteServerStarted(hostname,port)) } } catch { - case e => log.error(e, "Could not start up remote server") + case e => + log.error(e, "Could not start up remote server") + foreachListener(_ ! RemoteServerError(e,hostname,port)) } this } @@ -221,47 +272,46 @@ class RemoteServer extends Logging { openChannels.close.awaitUninterruptibly bootstrap.releaseExternalResources Cluster.deregisterLocalNode(hostname, port) + foreachListener(_ ! RemoteServerShutdown(hostname,port)) } catch { + case e: java.nio.channels.ClosedChannelException => {} case e => log.warning("Could not close remote server channel in a graceful way") } } } - // TODO: register active object in RemoteServer as well + // TODO: register typed actor in RemoteServer as well /** * Register Remote Actor by the Actor's 'id' field. It starts the Actor if it is not started already. */ - def register(actorRef: ActorRef) = synchronized { - if (_isRunning) { - if (!actorRef.isRunning) actorRef.start - log.info("Registering server side remote actor [%s] with id [%s]", actorRef.actorClass.getName, actorRef.id) - RemoteServer.actorsFor(RemoteServer.Address(hostname, port)).actors.put(actorRef.id, actorRef) - } - } + def register(actorRef: ActorRef): Unit = register(actorRef.id,actorRef) /** * Register Remote Actor by a specific 'id' passed as argument. *

* NOTE: If you use this method to register your remote actor then you must unregister the actor by this ID yourself. */ - def register(id: String, actorRef: ActorRef) = synchronized { + def register(id: String, actorRef: ActorRef): Unit = synchronized { if (_isRunning) { - if (!actorRef.isRunning) actorRef.start - log.info("Registering server side remote actor [%s] with id [%s]", actorRef.actorClass.getName, id) - RemoteServer.actorsFor(RemoteServer.Address(hostname, port)).actors.put(id, actorRef) + val actors = RemoteServer.actorsFor(RemoteServer.Address(hostname, port)).actors + if (!actors.contains(id)) { + if (!actorRef.isRunning) actorRef.start + log.debug("Registering server side remote actor [%s] with id [%s]", actorRef.actorClass.getName, id) + actors.put(id, actorRef) + } } } /** * Unregister Remote Actor that is registered using its 'id' field (not custom ID). */ - def unregister(actorRef: ActorRef) = synchronized { + def unregister(actorRef: ActorRef):Unit = synchronized { if (_isRunning) { - log.info("Unregistering server side remote actor [%s] with id [%s]", actorRef.actorClass.getName, actorRef.id) - val server = RemoteServer.actorsFor(RemoteServer.Address(hostname, port)) - server.actors.remove(actorRef.id) - if (actorRef.registeredInRemoteNodeDuringSerialization) server.actors.remove(actorRef.uuid) + log.debug("Unregistering server side remote actor [%s] with id [%s]", actorRef.actorClass.getName, actorRef.id) + val actors = RemoteServer.actorsFor(RemoteServer.Address(hostname, port)).actors + actors.remove(actorRef.id) + if (actorRef.registeredInRemoteNodeDuringSerialization) actors.remove(actorRef.uuid) } } @@ -270,18 +320,33 @@ class RemoteServer extends Logging { *

* NOTE: You need to call this method if you have registered an actor by a custom ID. */ - def unregister(id: String) = synchronized { + def unregister(id: String):Unit = synchronized { if (_isRunning) { log.info("Unregistering server side remote actor with id [%s]", id) - val server = RemoteServer.actorsFor(RemoteServer.Address(hostname, port)) - val actorRef = server.actors.get(id) - server.actors.remove(id) - if (actorRef.registeredInRemoteNodeDuringSerialization) server.actors.remove(actorRef.uuid) + val actors = RemoteServer.actorsFor(RemoteServer.Address(hostname, port)).actors + val actorRef = actors.get(id) + actors.remove(id) + if (actorRef.registeredInRemoteNodeDuringSerialization) actors.remove(actorRef.uuid) } } + + protected[akka] override def foreachListener(f: (ActorRef) => Unit): Unit = super.foreachListener(f) } -case class Codec(encoder: ChannelHandler, decoder: ChannelHandler) +object RemoteServerSslContext { + import javax.net.ssl.SSLContext + + val (client, server) = { + val protocol = "TLS" + //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) + val c = SSLContext.getInstance(protocol) + c.init(null, null, null) + (c, s) + } +} /** * @author Jonas Bonér @@ -291,24 +356,29 @@ class RemoteServerPipelineFactory( val openChannels: ChannelGroup, val loader: Option[ClassLoader], val actors: JMap[String, ActorRef], - val activeObjects: JMap[String, AnyRef]) extends ChannelPipelineFactory { + val typedActors: JMap[String, AnyRef], + val server: RemoteServer) extends ChannelPipelineFactory { import RemoteServer._ def getPipeline: ChannelPipeline = { - val lenDec = new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4) - val lenPrep = new LengthFieldPrepender(4) - val protobufDec = new ProtobufDecoder(RemoteRequestProtocol.getDefaultInstance) - val protobufEnc = new ProtobufEncoder - val zipCodec = RemoteServer.COMPRESSION_SCHEME match { - case "zlib" => Some(Codec(new ZlibEncoder(RemoteServer.ZLIB_COMPRESSION_LEVEL), new ZlibDecoder)) - //case "lzf" => Some(Codec(new LzfEncoder, new LzfDecoder)) - case _ => None - } - val remoteServer = new RemoteServerHandler(name, openChannels, loader, actors, activeObjects) + def join(ch: ChannelHandler*) = Array[ChannelHandler](ch:_*) - val stages: Array[ChannelHandler] = - zipCodec.map(codec => Array(codec.decoder, lenDec, protobufDec, codec.encoder, lenPrep, protobufEnc, remoteServer)) - .getOrElse(Array(lenDec, protobufDec, lenPrep, protobufEnc, remoteServer)) + val engine = RemoteServerSslContext.server.createSSLEngine() + engine.setEnabledCipherSuites(engine.getSupportedCipherSuites) //TODO is this sensible? + engine.setUseClientMode(false) + + val ssl = if(RemoteServer.SECURE) join(new SslHandler(engine)) else join() + val lenDec = new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4) + val lenPrep = new LengthFieldPrepender(4) + val protobufDec = new ProtobufDecoder(RemoteRequestProtocol.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 remoteServer = new RemoteServerHandler(name, openChannels, loader, actors, typedActors,server) + val stages = ssl ++ dec ++ join(lenDec, protobufDec) ++ enc ++ join(lenPrep, protobufEnc, remoteServer) new StaticChannelPipeline(stages: _*) } } @@ -322,7 +392,8 @@ class RemoteServerHandler( val openChannels: ChannelGroup, val applicationLoader: Option[ClassLoader], val actors: JMap[String, ActorRef], - val activeObjects: JMap[String, AnyRef]) extends SimpleChannelUpstreamHandler with Logging { + val typedActors: JMap[String, AnyRef], + val server: RemoteServer) extends SimpleChannelUpstreamHandler with Logging { val AW_PROXY_PREFIX = "$$ProxiedByAW".intern applicationLoader.foreach(MessageSerializer.setClassLoader(_)) @@ -335,6 +406,20 @@ class RemoteServerHandler( openChannels.add(ctx.getChannel) } + override def channelConnected(ctx: ChannelHandlerContext, e: ChannelStateEvent) { + if (RemoteServer.SECURE) { + val sslHandler : SslHandler = ctx.getPipeline.get(classOf[SslHandler]) + + // Begin handshake. + sslHandler.handshake().addListener( new ChannelFutureListener { + def operationComplete(future: ChannelFuture): Unit = { + if (future.isSuccess) openChannels.add(future.getChannel) + else future.getChannel.close + } + }) + } + } + override def handleUpstream(ctx: ChannelHandlerContext, event: ChannelEvent) = { if (event.isInstanceOf[ChannelStateEvent] && event.asInstanceOf[ChannelStateEvent].getState != ChannelState.INTEREST_OPS) { @@ -352,147 +437,106 @@ class RemoteServerHandler( } override def exceptionCaught(ctx: ChannelHandlerContext, event: ExceptionEvent) = { - event.getCause.printStackTrace log.error(event.getCause, "Unexpected exception from remote downstream") event.getChannel.close + server.foreachListener(_ ! RemoteServerError(event.getCause,server.hostname,server.port)) } private def handleRemoteRequestProtocol(request: RemoteRequestProtocol, channel: Channel) = { log.debug("Received RemoteRequestProtocol[\n%s]", request.toString) - if (request.getIsActor) dispatchToActor(request, channel) - else dispatchToActiveObject(request, channel) + val actorType = request.getActorInfo.getActorType + if (actorType == ActorType.SCALA_ACTOR) dispatchToActor(request, channel) + else if (actorType == ActorType.JAVA_ACTOR) throw new IllegalActorStateException("ActorType JAVA_ACTOR is currently not supported") + else if (actorType == ActorType.TYPED_ACTOR) dispatchToTypedActor(request, channel) + else throw new IllegalActorStateException("Unknown ActorType [" + actorType + "]") } private def dispatchToActor(request: RemoteRequestProtocol, channel: Channel) = { - log.debug("Dispatching to remote actor [%s:%s]", request.getTarget, request.getUuid) - val actorRef = createActor(request.getTarget, request.getUuid, request.getTimeout) - actorRef.start + val actorInfo = request.getActorInfo + log.debug("Dispatching to remote actor [%s:%s]", actorInfo.getTarget, actorInfo.getUuid) + + val actorRef = createActor(actorInfo).start + val message = MessageSerializer.deserialize(request.getMessage) val sender = 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 - 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) - val replyMessage = replyBuilder.build - channel.write(replyMessage) - } catch { - case e: Throwable => - log.error(e, "Could not invoke remote actor [%s]", request.getTarget) - val replyBuilder = RemoteReplyProtocol.newBuilder - .setId(request.getId) - .setException(ExceptionProtocol.newBuilder.setClassname(e.getClass.getName).setMessage(e.getMessage).build) - .setIsSuccessful(false) - .setIsActor(true) - if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid) - val replyMessage = replyBuilder.build - channel.write(replyMessage) - } + + 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) + + if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid) + channel.write(replyBuilder.build) + + } catch { + case e: Throwable => + channel.write(createErrorReplyMessage(e, request, true)) + server.foreachListener(_ ! RemoteServerError(e,server.hostname,server.port)) + } + } } } - private def dispatchToActiveObject(request: RemoteRequestProtocol, channel: Channel) = { - log.debug("Dispatching to remote active object [%s :: %s]", request.getMethod, request.getTarget) - val activeObject = createActiveObject(request.getTarget, request.getTimeout) + private def dispatchToTypedActor(request: RemoteRequestProtocol, channel: Channel) = { + val actorInfo = request.getActorInfo + val typedActorInfo = actorInfo.getTypedActorInfo + log.debug("Dispatching to remote typed actor [%s :: %s]", typedActorInfo.getMethod, typedActorInfo.getInterface) + val typedActor = createTypedActor(actorInfo) val args = MessageSerializer.deserialize(request.getMessage).asInstanceOf[Array[AnyRef]].toList val argClasses = args.map(_.getClass) - val (unescapedArgs, unescapedArgClasses) = unescapeArgs(args, argClasses, request.getTimeout) try { - val messageReceiver = activeObject.getClass.getDeclaredMethod( - request.getMethod, unescapedArgClasses: _*) - if (request.getIsOneWay) messageReceiver.invoke(activeObject, unescapedArgs: _*) + val messageReceiver = typedActor.getClass.getDeclaredMethod(typedActorInfo.getMethod, argClasses: _*) + if (request.getIsOneWay) messageReceiver.invoke(typedActor, args: _*) else { - val result = messageReceiver.invoke(activeObject, unescapedArgs: _*) - log.debug("Returning result from remote active object invocation [%s]", result) + val result = messageReceiver.invoke(typedActor, args: _*) + log.debug("Returning result from remote typed actor invocation [%s]", result) val replyBuilder = RemoteReplyProtocol.newBuilder .setId(request.getId) .setMessage(MessageSerializer.serialize(result)) .setIsSuccessful(true) .setIsActor(false) if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid) - val replyMessage = replyBuilder.build - channel.write(replyMessage) + channel.write(replyBuilder.build) } } catch { case e: InvocationTargetException => - log.error(e.getCause, "Could not invoke remote active object [%s :: %s]", request.getMethod, request.getTarget) - val replyBuilder = RemoteReplyProtocol.newBuilder - .setId(request.getId) - .setException(ExceptionProtocol.newBuilder.setClassname(e.getCause.getClass.getName).setMessage(e.getCause.getMessage).build) - .setIsSuccessful(false) - .setIsActor(false) - if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid) - val replyMessage = replyBuilder.build - channel.write(replyMessage) - case e: Throwable => - log.error(e, "Could not invoke remote active object [%s :: %s]", request.getMethod, request.getTarget) - val replyBuilder = RemoteReplyProtocol.newBuilder - .setId(request.getId) - .setException(ExceptionProtocol.newBuilder.setClassname(e.getClass.getName).setMessage(e.getMessage).build) - .setIsSuccessful(false) - .setIsActor(false) - if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid) - val replyMessage = replyBuilder.build - channel.write(replyMessage) + channel.write(createErrorReplyMessage(e.getCause, request, false)) + server.foreachListener(_ ! RemoteServerError(e,server.hostname,server.port)) + case e: Throwable => + channel.write(createErrorReplyMessage(e, request, false)) + server.foreachListener(_ ! RemoteServerError(e,server.hostname,server.port)) } } - private def unescapeArgs(args: scala.List[AnyRef], argClasses: scala.List[Class[_]], timeout: Long) = { - val unescapedArgs = new Array[AnyRef](args.size) - val unescapedArgClasses = new Array[Class[_]](args.size) - - val escapedArgs = for (i <- 0 until args.size) { - val arg = args(i) - if (arg.isInstanceOf[String] && arg.asInstanceOf[String].startsWith(AW_PROXY_PREFIX)) { - val argString = arg.asInstanceOf[String] - val proxyName = argString.replace(AW_PROXY_PREFIX, "") - val activeObject = createActiveObject(proxyName, timeout) - unescapedArgs(i) = activeObject - unescapedArgClasses(i) = Class.forName(proxyName) - } else { - unescapedArgs(i) = args(i) - unescapedArgClasses(i) = argClasses(i) - } - } - (unescapedArgs, unescapedArgClasses) - } - - private def createActiveObject(name: String, timeout: Long): AnyRef = { - val activeObjectOrNull = activeObjects.get(name) - if (activeObjectOrNull eq null) { - try { - log.info("Creating a new remote active object [%s]", name) - val clazz = if (applicationLoader.isDefined) applicationLoader.get.loadClass(name) - else Class.forName(name) - val newInstance = ActiveObject.newInstance(clazz, timeout).asInstanceOf[AnyRef] - activeObjects.put(name, newInstance) - newInstance - } catch { - case e => - log.error(e, "Could not create remote active object instance") - throw e - } - } else activeObjectOrNull - } - /** * Creates a new instance of the actor with name, uuid and timeout specified as arguments. + * * If actor already created then just return it from the registry. + * * Does not start the actor. */ - private def createActor(name: String, uuid: String, timeout: Long): ActorRef = { - val actorRefOrNull = actors.get(uuid) + private def createActor(actorInfo: ActorInfoProtocol): ActorRef = { + val uuid = actorInfo.getUuid + val name = actorInfo.getTarget + val timeout = actorInfo.getTimeout + + val actorRefOrNull = actors get uuid + if (actorRefOrNull eq null) { try { log.info("Creating a new remote actor [%s:%s]", name, uuid) @@ -507,8 +551,51 @@ class RemoteServerHandler( } catch { case e => log.error(e, "Could not create remote actor instance") + server.foreachListener(_ ! RemoteServerError(e,server.hostname,server.port)) throw e } } else actorRefOrNull } + + private def createTypedActor(actorInfo: ActorInfoProtocol): AnyRef = { + val uuid = actorInfo.getUuid + val typedActorOrNull = typedActors get uuid + + if (typedActorOrNull eq null) { + val typedActorInfo = actorInfo.getTypedActorInfo + val interfaceClassname = typedActorInfo.getInterface + val targetClassname = actorInfo.getTarget + + try { + log.info("Creating a new remote typed actor:\n\t[%s :: %s]", interfaceClassname, targetClassname) + + val (interfaceClass, targetClass) = + if (applicationLoader.isDefined) (applicationLoader.get.loadClass(interfaceClassname), + applicationLoader.get.loadClass(targetClassname)) + else (Class.forName(interfaceClassname), Class.forName(targetClassname)) + + val newInstance = TypedActor.newInstance( + interfaceClass, targetClass.asInstanceOf[Class[_ <: TypedActor]], actorInfo.getTimeout).asInstanceOf[AnyRef] + typedActors.put(uuid, newInstance) + newInstance + } catch { + case e => + log.error(e, "Could not create remote typed actor instance") + server.foreachListener(_ ! RemoteServerError(e,server.hostname,server.port)) + throw e + } + } else typedActorOrNull + } + + private def createErrorReplyMessage(e: Throwable, request: RemoteRequestProtocol, isActor: Boolean): RemoteReplyProtocol = { + val actorInfo = request.getActorInfo + log.error(e, "Could not invoke remote typed actor [%s :: %s]", actorInfo.getTypedActorInfo.getMethod, actorInfo.getTarget) + val replyBuilder = RemoteReplyProtocol.newBuilder + .setId(request.getId) + .setException(ExceptionProtocol.newBuilder.setClassname(e.getClass.getName).setMessage(e.getMessage).build) + .setIsSuccessful(false) + .setIsActor(isActor) + if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid) + replyBuilder.build + } } diff --git a/akka-core/src/main/scala/serialization/Serializer.scala b/akka-core/src/main/scala/serialization/Serializer.scala index 79be0bec8b..1365a7d4c1 100644 --- a/akka-core/src/main/scala/serialization/Serializer.scala +++ b/akka-core/src/main/scala/serialization/Serializer.scala @@ -39,8 +39,7 @@ class SerializerFactory { * @author Jonas Bonér */ object Serializer { - val EMPTY_CLASS_ARRAY = Array[Class[_]]() - val EMPTY_ANY_REF_ARRAY = Array[AnyRef]() + val ARRAY_OF_BYTE_ARRAY = Array[Class[_]](classOf[Array[Byte]]) object NOOP extends NOOP class NOOP extends Serializer { @@ -85,10 +84,7 @@ object Serializer { def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef = { if (!clazz.isDefined) throw new IllegalArgumentException( "Need a protobuf message class to be able to serialize bytes using protobuf") - // TODO: should we cache this method lookup? - val message = clazz.get.getDeclaredMethod( - "getDefaultInstance", EMPTY_CLASS_ARRAY: _*).invoke(null, EMPTY_ANY_REF_ARRAY: _*).asInstanceOf[Message] - message.toBuilder().mergeFrom(bytes).build + clazz.get.getDeclaredMethod("parseFrom", ARRAY_OF_BYTE_ARRAY: _*).invoke(null, bytes).asInstanceOf[Message] } def fromBinary(bytes: Array[Byte], clazz: Class[_]): AnyRef = { diff --git a/akka-core/src/main/scala/stm/JTA.scala b/akka-core/src/main/scala/stm/JTA.scala index 24b5a49086..80a0cda4ec 100644 --- a/akka-core/src/main/scala/stm/JTA.scala +++ b/akka-core/src/main/scala/stm/JTA.scala @@ -4,13 +4,14 @@ package se.scalablesolutions.akka.stm -import javax.transaction.{TransactionManager, UserTransaction, - Transaction => JtaTransaction, SystemException, +import javax.transaction.{TransactionManager, UserTransaction, + Transaction => JtaTransaction, SystemException, Status, Synchronization, TransactionSynchronizationRegistry} import javax.naming.{InitialContext, Context, NamingException} import se.scalablesolutions.akka.config.Config._ import se.scalablesolutions.akka.util.Logging +import se.scalablesolutions.akka.AkkaException /** * Detects if there is a UserTransaction or TransactionManager available in the JNDI. @@ -122,7 +123,7 @@ class TransactionContainer private (val tm: Either[Option[UserTransaction], Opti } def begin = { - TransactionContainer.log.ifTrace("Starting JTA transaction") + TransactionContainer.log.trace("Starting JTA transaction") tm match { case Left(Some(userTx)) => userTx.begin case Right(Some(txMan)) => txMan.begin @@ -131,7 +132,7 @@ class TransactionContainer private (val tm: Either[Option[UserTransaction], Opti } def commit = { - TransactionContainer.log.ifTrace("Committing JTA transaction") + TransactionContainer.log.trace("Committing JTA transaction") tm match { case Left(Some(userTx)) => userTx.commit case Right(Some(txMan)) => txMan.commit @@ -140,7 +141,7 @@ class TransactionContainer private (val tm: Either[Option[UserTransaction], Opti } def rollback = { - TransactionContainer.log.ifTrace("Aborting JTA transaction") + TransactionContainer.log.trace("Aborting JTA transaction") tm match { case Left(Some(userTx)) => userTx.rollback case Right(Some(txMan)) => txMan.rollback diff --git a/akka-core/src/main/scala/stm/Ref.scala b/akka-core/src/main/scala/stm/Ref.scala index 3b13d32971..b0ae15c957 100644 --- a/akka-core/src/main/scala/stm/Ref.scala +++ b/akka-core/src/main/scala/stm/Ref.scala @@ -6,25 +6,17 @@ package se.scalablesolutions.akka.stm import se.scalablesolutions.akka.util.UUID -import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance - -object RefFactory { - private val factory = getGlobalStmInstance.getProgrammaticRefFactoryBuilder.build - - def createRef[T] = factory.atomicCreateRef[T]() - - def createRef[T](value: T) = factory.atomicCreateRef(value) -} +import org.multiverse.transactional.refs.BasicRef /** - * Ref. + * Ref * * @author Jonas Bonér */ object Ref { - def apply[T]() = new Ref[T] + def apply[T]() = new Ref[T]() - def apply[T](initialValue: T) = new Ref[T](Some(initialValue)) + def apply[T](initialValue: T) = new Ref[T](initialValue) /** * An implicit conversion that converts a Ref to an Iterable value. @@ -33,77 +25,44 @@ object Ref { } /** - * Implements a transactional managed reference. + * Transactional managed reference. * * @author Jonas Bonér */ -class Ref[T](initialOpt: Option[T] = None) extends Transactional { +class Ref[T](initialValue: T) extends BasicRef[T](initialValue) with Transactional { self => - def this() = this(None) // Java compatibility - - import org.multiverse.api.ThreadLocalTransaction._ + def this() = this(null.asInstanceOf[T]) val uuid = UUID.newUuid.toString - private[this] val ref = { - if (initialOpt.isDefined) RefFactory.createRef(initialOpt.get) - else RefFactory.createRef[T] - } - - def swap(elem: T) = { - ensureIsInTransaction - ref.set(elem) - } + def swap(elem: T) = set(elem) def alter(f: T => T): T = { - ensureIsInTransaction - ensureNotNull - ref.set(f(ref.get)) - ref.get + val value = f(this.get) + set(value) + value } - def get: Option[T] = { - ensureIsInTransaction - if (ref.isNull) None - else Some(ref.get) - } + def opt: Option[T] = Option(this.get) - def getOrWait: T = { - ensureIsInTransaction - ref.getOrAwait - } + def getOrWait: T = getOrAwait - def getOrElse(default: => T): T = { - ensureIsInTransaction - if (ref.isNull) default - else ref.get - } + def getOrElse(default: => T): T = + if (isNull) default else this.get - def isDefined: Boolean = { - ensureIsInTransaction - !ref.isNull - } + def isDefined: Boolean = !isNull - def isEmpty: Boolean = { - ensureIsInTransaction - ref.isNull - } + def isEmpty: Boolean = isNull - def map[B](f: T => B): Ref[B] = { - ensureIsInTransaction - if (isEmpty) Ref[B] else Ref(f(ref.get)) - } + def map[B](f: T => B): Ref[B] = + if (isEmpty) Ref[B] else Ref(f(this.get)) - def flatMap[B](f: T => Ref[B]): Ref[B] = { - ensureIsInTransaction - if (isEmpty) Ref[B] else f(ref.get) - } + def flatMap[B](f: T => Ref[B]): Ref[B] = + if (isEmpty) Ref[B] else f(this.get) - def filter(p: T => Boolean): Ref[T] = { - ensureIsInTransaction - if (isDefined && p(ref.get)) Ref(ref.get) else Ref[T] - } + def filter(p: T => Boolean): Ref[T] = + if (isDefined && p(this.get)) Ref(this.get) else Ref[T] /** * Necessary to keep from being implicitly converted to Iterable in for comprehensions. @@ -117,34 +76,18 @@ class Ref[T](initialOpt: Option[T] = None) extends Transactional { def withFilter(q: T => Boolean): WithFilter = new WithFilter(x => p(x) && q(x)) } - def foreach[U](f: T => U): Unit = { - ensureIsInTransaction - if (isDefined) f(ref.get) - } + def foreach[U](f: T => U): Unit = + if (isDefined) f(this.get) - def elements: Iterator[T] = { - ensureIsInTransaction - if (isEmpty) Iterator.empty else Iterator(ref.get) - } + def elements: Iterator[T] = + if (isEmpty) Iterator.empty else Iterator(this.get) - def toList: List[T] = { - ensureIsInTransaction - if (isEmpty) List() else List(ref.get) - } + def toList: List[T] = + if (isEmpty) List() else List(this.get) - def toRight[X](left: => X) = { - ensureIsInTransaction - if (isEmpty) Left(left) else Right(ref.get) - } + def toRight[X](left: => X) = + if (isEmpty) Left(left) else Right(this.get) - def toLeft[X](right: => X) = { - ensureIsInTransaction - if (isEmpty) Right(right) else Left(ref.get) - } - - private def ensureIsInTransaction = - if (getThreadLocalTransaction eq null) throw new NoTransactionInScopeException - - private def ensureNotNull = - if (ref.isNull) throw new RuntimeException("Cannot alter Ref's value when it is null") + def toLeft[X](right: => X) = + if (isEmpty) Right(right) else Left(this.get) } diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala index 0951cbc5c5..dc7baa3010 100644 --- a/akka-core/src/main/scala/stm/Transaction.scala +++ b/akka-core/src/main/scala/stm/Transaction.scala @@ -13,15 +13,16 @@ import scala.collection.mutable.HashMap import se.scalablesolutions.akka.util.Logging import se.scalablesolutions.akka.config.Config._ +import se.scalablesolutions.akka.AkkaException import org.multiverse.api.{Transaction => MultiverseTransaction} import org.multiverse.api.lifecycle.{TransactionLifecycleListener, TransactionLifecycleEvent} import org.multiverse.api.ThreadLocalTransaction._ import org.multiverse.api.{TraceLevel => MultiverseTraceLevel} -class NoTransactionInScopeException extends RuntimeException -class TransactionRetryException(message: String) extends RuntimeException(message) -class StmConfigurationException(message: String) extends RuntimeException(message) +class NoTransactionInScopeException extends AkkaException("No transaction in scope") +class TransactionRetryException(message: String) extends AkkaException(message) +class StmConfigurationException(message: String) extends AkkaException(message) object Transaction { val idFactory = new AtomicLong(-1L) @@ -83,12 +84,12 @@ object Transaction { if (JTA_AWARE) Some(TransactionContainer()) else None - log.ifTrace("Creating transaction " + toString) + log.trace("Creating transaction " + toString) // --- public methods --------- def begin = synchronized { - log.ifTrace("Starting transaction " + toString) + log.trace("Starting transaction " + toString) jta.foreach { txContainer => txContainer.begin txContainer.registerSynchronization(new StmSynchronization(txContainer, this)) @@ -96,14 +97,14 @@ object Transaction { } def commit = synchronized { - log.ifTrace("Committing transaction " + toString) + log.trace("Committing transaction " + toString) persistentStateMap.valuesIterator.foreach(_.commit) status = TransactionStatus.Completed jta.foreach(_.commit) } def abort = synchronized { - log.ifTrace("Aborting transaction " + toString) + log.trace("Aborting transaction " + toString) jta.foreach(_.rollback) persistentStateMap.valuesIterator.foreach(_.abort) persistentStateMap.clear diff --git a/akka-core/src/main/scala/stm/TransactionFactory.scala b/akka-core/src/main/scala/stm/TransactionFactory.scala index 24c4712509..691fec675b 100644 --- a/akka-core/src/main/scala/stm/TransactionFactory.scala +++ b/akka-core/src/main/scala/stm/TransactionFactory.scala @@ -23,7 +23,6 @@ object TransactionConfig { val READONLY = null.asInstanceOf[JBoolean] val MAX_RETRIES = config.getInt("akka.stm.max-retries", 1000) val TIMEOUT = config.getLong("akka.stm.timeout", 10) - val TIME_UNIT = config.getString("akka.stm.time-unit", "seconds") val TRACK_READS = null.asInstanceOf[JBoolean] val WRITE_SKEW = config.getBool("akka.stm.write-skew", true) val EXPLICIT_RETRIES = config.getBool("akka.stm.explicit-retries", false) @@ -154,7 +153,7 @@ object TransactionFactory { * @see TransactionConfig for configuration options. */ class TransactionFactory( - val config: TransactionConfig = DefaultTransactionConfig, + val config: TransactionConfig = DefaultTransactionConfig, defaultName: String = TransactionConfig.FAMILY_NAME) { self => // use the config family name if it's been set, otherwise defaultName - used by actors to set class name as default diff --git a/akka-core/src/main/scala/stm/TransactionFactoryBuilder.scala b/akka-core/src/main/scala/stm/TransactionFactoryBuilder.scala new file mode 100644 index 0000000000..a5045ca090 --- /dev/null +++ b/akka-core/src/main/scala/stm/TransactionFactoryBuilder.scala @@ -0,0 +1,84 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +import java.lang.{Boolean => JBoolean} + +import se.scalablesolutions.akka.util.Duration + +import org.multiverse.api.TraceLevel + +/** + * For more easily creating TransactionConfig from Java. + */ +class TransactionConfigBuilder { + var familyName: String = TransactionConfig.FAMILY_NAME + var readonly: JBoolean = TransactionConfig.READONLY + var maxRetries: Int = TransactionConfig.MAX_RETRIES + var timeout: Duration = TransactionConfig.DefaultTimeout + var trackReads: JBoolean = TransactionConfig.TRACK_READS + var writeSkew: Boolean = TransactionConfig.WRITE_SKEW + var explicitRetries: Boolean = TransactionConfig.EXPLICIT_RETRIES + var interruptible: Boolean = TransactionConfig.INTERRUPTIBLE + var speculative: Boolean = TransactionConfig.SPECULATIVE + var quickRelease: Boolean = TransactionConfig.QUICK_RELEASE + var traceLevel: TraceLevel = TransactionConfig.TRACE_LEVEL + var hooks: Boolean = TransactionConfig.HOOKS + + def setFamilyName(familyName: String) = { this.familyName = familyName; this } + def setReadonly(readonly: JBoolean) = { this.readonly = readonly; this } + def setMaxRetries(maxRetries: Int) = { this.maxRetries = maxRetries; this } + def setTimeout(timeout: Duration) = { this.timeout = timeout; this } + def setTrackReads(trackReads: JBoolean) = { this.trackReads = trackReads; this } + def setWriteSkew(writeSkew: Boolean) = { this.writeSkew = writeSkew; this } + def setExplicitRetries(explicitRetries: Boolean) = { this.explicitRetries = explicitRetries; this } + def setInterruptible(interruptible: Boolean) = { this.interruptible = interruptible; this } + def setSpeculative(speculative: Boolean) = { this.speculative = speculative; this } + def setQuickRelease(quickRelease: Boolean) = { this.quickRelease = quickRelease; this } + def setTraceLevel(traceLevel: TraceLevel) = { this.traceLevel = traceLevel; this } + def setHooks(hooks: Boolean) = { this.hooks = hooks; this } + + def build = new TransactionConfig( + familyName, readonly, maxRetries, timeout, trackReads, writeSkew, + explicitRetries, interruptible, speculative, quickRelease, traceLevel, hooks) +} + +/** + * For more easily creating TransactionFactory from Java. + */ +class TransactionFactoryBuilder { + var familyName: String = TransactionConfig.FAMILY_NAME + var readonly: JBoolean = TransactionConfig.READONLY + var maxRetries: Int = TransactionConfig.MAX_RETRIES + var timeout: Duration = TransactionConfig.DefaultTimeout + var trackReads: JBoolean = TransactionConfig.TRACK_READS + var writeSkew: Boolean = TransactionConfig.WRITE_SKEW + var explicitRetries: Boolean = TransactionConfig.EXPLICIT_RETRIES + var interruptible: Boolean = TransactionConfig.INTERRUPTIBLE + var speculative: Boolean = TransactionConfig.SPECULATIVE + var quickRelease: Boolean = TransactionConfig.QUICK_RELEASE + var traceLevel: TraceLevel = TransactionConfig.TRACE_LEVEL + var hooks: Boolean = TransactionConfig.HOOKS + + def setFamilyName(familyName: String) = { this.familyName = familyName; this } + def setReadonly(readonly: JBoolean) = { this.readonly = readonly; this } + def setMaxRetries(maxRetries: Int) = { this.maxRetries = maxRetries; this } + def setTimeout(timeout: Duration) = { this.timeout = timeout; this } + def setTrackReads(trackReads: JBoolean) = { this.trackReads = trackReads; this } + def setWriteSkew(writeSkew: Boolean) = { this.writeSkew = writeSkew; this } + def setExplicitRetries(explicitRetries: Boolean) = { this.explicitRetries = explicitRetries; this } + def setInterruptible(interruptible: Boolean) = { this.interruptible = interruptible; this } + def setSpeculative(speculative: Boolean) = { this.speculative = speculative; this } + def setQuickRelease(quickRelease: Boolean) = { this.quickRelease = quickRelease; this } + def setTraceLevel(traceLevel: TraceLevel) = { this.traceLevel = traceLevel; this } + def setHooks(hooks: Boolean) = { this.hooks = hooks; this } + + def build = { + val config = new TransactionConfig( + familyName, readonly, maxRetries, timeout, trackReads, writeSkew, + explicitRetries, interruptible, speculative, quickRelease, traceLevel, hooks) + new TransactionFactory(config) + } +} diff --git a/akka-core/src/main/scala/stm/TransactionManagement.scala b/akka-core/src/main/scala/stm/TransactionManagement.scala index 25e58f3fb6..2da5ffad98 100644 --- a/akka-core/src/main/scala/stm/TransactionManagement.scala +++ b/akka-core/src/main/scala/stm/TransactionManagement.scala @@ -4,23 +4,15 @@ package se.scalablesolutions.akka.stm -import se.scalablesolutions.akka.util.Logging - -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.TimeUnit +import se.scalablesolutions.akka.AkkaException import org.multiverse.api.{StmUtils => MultiverseStmUtils} import org.multiverse.api.ThreadLocalTransaction._ import org.multiverse.api.{Transaction => MultiverseTransaction} import org.multiverse.commitbarriers.CountDownCommitBarrier -import org.multiverse.templates.{TransactionalCallable, OrElseTemplate} +import org.multiverse.templates.OrElseTemplate -class TransactionSetAbortedException(msg: String) extends RuntimeException(msg) - -// TODO Should we remove TransactionAwareWrapperException? Not used anywhere yet. -class TransactionAwareWrapperException(val cause: Throwable, val tx: Option[Transaction]) extends RuntimeException(cause) { - override def toString = "TransactionAwareWrapperException[" + cause + ", " + tx + "]" -} +class TransactionSetAbortedException(msg: String) extends AkkaException(msg) /** * Internal helper methods and properties for transaction management. @@ -93,96 +85,19 @@ trait TransactionManagement { } } -/** - * Local transaction management, local in the context of threads. - * Use this if you do not need to have one transaction span - * multiple threads (or Actors). - *

- * Example of atomic transaction management using the atomic block. - *

- *

- * import se.scalablesolutions.akka.stm.local._
- *
- * atomic  {
- *   // do something within a transaction
- * }
- * 
- */ -class LocalStm extends TransactionManagement with Logging { - - val DefaultLocalTransactionConfig = TransactionConfig() - val DefaultLocalTransactionFactory = TransactionFactory(DefaultLocalTransactionConfig, "DefaultLocalTransaction") - - def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultLocalTransactionFactory): T = atomic(factory)(body) - - def atomic[T](factory: TransactionFactory)(body: => T): T = { - factory.boilerplate.execute(new TransactionalCallable[T]() { - def call(mtx: MultiverseTransaction): T = { - factory.addHooks - val result = body - log.ifTrace("Committing local transaction [" + mtx + "]") - result - } - }) - } -} - -/** - * Global transaction management, global in the context of multiple threads. - * Use this if you need to have one transaction span multiple threads (or Actors). - *

- * Example of atomic transaction management using the atomic block: - *

- *

- * import se.scalablesolutions.akka.stm.global._
- *
- * atomic  {
- *   // do something within a transaction
- * }
- * 
- */ -class GlobalStm extends TransactionManagement with Logging { - - val DefaultGlobalTransactionConfig = TransactionConfig() - val DefaultGlobalTransactionFactory = TransactionFactory(DefaultGlobalTransactionConfig, "DefaultGlobalTransaction") - - def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultGlobalTransactionFactory): T = atomic(factory)(body) - - def atomic[T](factory: TransactionFactory)(body: => T): T = { - factory.boilerplate.execute(new TransactionalCallable[T]() { - def call(mtx: MultiverseTransaction): T = { - if (!isTransactionSetInScope) createNewTransactionSet - factory.addHooks - val result = body - val txSet = getTransactionSetInScope - log.ifTrace("Committing global transaction [" + mtx + "]\n\tand joining transaction set [" + txSet + "]") - try { - txSet.tryJoinCommit( - mtx, - TransactionConfig.DefaultTimeout.length, - TransactionConfig.DefaultTimeout.unit) - // Need to catch IllegalStateException until we have fix in Multiverse, since it throws it by mistake - } catch { case e: IllegalStateException => {} } - result - } - }) - } -} - trait StmUtil { - /** * Schedule a deferred task on the thread local transaction (use within an atomic). * This is executed when the transaction commits. */ - def deferred[T](body: => T): Unit = + def deferred[T](body: => T): Unit = MultiverseStmUtils.scheduleDeferredTask(new Runnable { def run = body }) /** * Schedule a compensating task on the thread local transaction (use within an atomic). * This is executed when the transaction aborts. */ - def compensating[T](body: => T): Unit = + def compensating[T](body: => T): Unit = MultiverseStmUtils.scheduleCompensatingTask(new Runnable { def run = body }) /** @@ -193,7 +108,7 @@ trait StmUtil { /** * Use either-orElse to combine two blocking transactions. - * Usage: + * Usage: *
    * either {
    *   ...
@@ -209,3 +124,14 @@ trait StmUtil {
     }.execute()
   }
 }
+
+trait StmCommon {
+  type TransactionConfig = se.scalablesolutions.akka.stm.TransactionConfig
+  val TransactionConfig = se.scalablesolutions.akka.stm.TransactionConfig
+
+  type TransactionFactory = se.scalablesolutions.akka.stm.TransactionFactory
+  val TransactionFactory = se.scalablesolutions.akka.stm.TransactionFactory
+
+  type Ref[T] = se.scalablesolutions.akka.stm.Ref[T]
+  val Ref = se.scalablesolutions.akka.stm.Ref
+}
diff --git a/akka-core/src/main/scala/stm/TransactionalMap.scala b/akka-core/src/main/scala/stm/TransactionalMap.scala
index be7b9c5189..d45396ad25 100644
--- a/akka-core/src/main/scala/stm/TransactionalMap.scala
+++ b/akka-core/src/main/scala/stm/TransactionalMap.scala
@@ -11,9 +11,9 @@ import se.scalablesolutions.akka.util.UUID
 import org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction
 
 object TransactionalMap {
-  def apply[K, V]() = new TransactionalMap[K, V]
+  def apply[K, V]() = new TransactionalMap[K, V]()
 
-  def apply[K, V](pairs: (K, V)*) = new TransactionalMap(Some(HashMap(pairs: _*)))
+  def apply[K, V](pairs: (K, V)*) = new TransactionalMap(HashMap(pairs: _*))
 }
 
 /**
@@ -21,12 +21,12 @@ object TransactionalMap {
  *
  * @author Jonas Bonér
  */
-class TransactionalMap[K, V](initialOpt: Option[HashMap[K, V]] = None) extends Transactional with scala.collection.mutable.Map[K, V] {
-  def this() = this(None) // Java compatibility
+class TransactionalMap[K, V](initialValue: HashMap[K, V]) extends Transactional with scala.collection.mutable.Map[K, V] {
+  def this() = this(HashMap[K, V]())
 
   val uuid = UUID.newUuid.toString
 
-  protected[this] val ref = new Ref(initialOpt.orElse(Some(HashMap[K, V]())))
+  private[this] val ref = Ref(initialValue)
 
   def -=(key: K) = {
     remove(key)
@@ -41,36 +41,36 @@ class TransactionalMap[K, V](initialOpt: Option[HashMap[K, V]] = None) extends T
   }
 
   override def remove(key: K) = {
-    val map = ref.get.get
+    val map = ref.get
     val oldValue = map.get(key)
-    ref.swap(ref.get.get - key)
+    ref.swap(ref.get - key)
     oldValue
   }
 
-  def get(key: K): Option[V] = ref.get.get.get(key)
+  def get(key: K): Option[V] = ref.get.get(key)
 
   override def put(key: K, value: V): Option[V] = {
-    val map = ref.get.get
+    val map = ref.get
     val oldValue = map.get(key)
     ref.swap(map.updated(key, value))
     oldValue
   }
 
   override def update(key: K, value: V) = {
-    val map = ref.get.get
+    val map = ref.get
     val oldValue = map.get(key)
     ref.swap(map.updated(key, value))
   }
 
-  def iterator = ref.get.get.iterator
+  def iterator = ref.get.iterator
 
-  override def elements: Iterator[(K, V)] = ref.get.get.iterator
+  override def elements: Iterator[(K, V)] = ref.get.iterator
 
-  override def contains(key: K): Boolean = ref.get.get.contains(key)
+  override def contains(key: K): Boolean = ref.get.contains(key)
 
   override def clear = ref.swap(HashMap[K, V]())
 
-  override def size: Int = ref.get.get.size
+  override def size: Int = ref.get.size
 
   override def hashCode: Int = System.identityHashCode(this);
 
diff --git a/akka-core/src/main/scala/stm/TransactionalVector.scala b/akka-core/src/main/scala/stm/TransactionalVector.scala
index e2ad6a2aeb..2beeeecef0 100644
--- a/akka-core/src/main/scala/stm/TransactionalVector.scala
+++ b/akka-core/src/main/scala/stm/TransactionalVector.scala
@@ -11,9 +11,9 @@ import se.scalablesolutions.akka.util.UUID
 import org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction
 
 object TransactionalVector {
-  def apply[T]() = new TransactionalVector[T]
+  def apply[T]() = new TransactionalVector[T]()
 
-  def apply[T](elems: T*) = new TransactionalVector(Some(Vector(elems: _*)))
+  def apply[T](elems: T*) = new TransactionalVector(Vector(elems: _*))
 }
 
 /**
@@ -21,31 +21,31 @@ object TransactionalVector {
  *
  * @author Jonas Bonér
  */
-class TransactionalVector[T](initialOpt: Option[Vector[T]] = None) extends Transactional with IndexedSeq[T] {
-  def this() = this(None) // Java compatibility
+class TransactionalVector[T](initialValue: Vector[T]) extends Transactional with IndexedSeq[T] {
+  def this() = this(Vector[T]())
 
   val uuid = UUID.newUuid.toString
 
-  private[this] val ref = new Ref(initialOpt.orElse(Some(Vector[T]())))
+  private[this] val ref = Ref(initialValue)
 
   def clear = ref.swap(Vector[T]())
 
   def +(elem: T) = add(elem)
 
-  def add(elem: T) = ref.swap(ref.get.get :+ elem)
+  def add(elem: T) = ref.swap(ref.get :+ elem)
 
-  def get(index: Int): T = ref.get.get.apply(index)
+  def get(index: Int): T = ref.get.apply(index)
 
   /**
    * Removes the tail element of this vector.
    */
-  def pop = ref.swap(ref.get.get.dropRight(1))
+  def pop = ref.swap(ref.get.dropRight(1))
 
-  def update(index: Int, elem: T) = ref.swap(ref.get.get.updated(index, elem))
+  def update(index: Int, elem: T) = ref.swap(ref.get.updated(index, elem))
 
-  def length: Int = ref.get.get.length
+  def length: Int = ref.get.length
 
-  def apply(index: Int): T = ref.get.get.apply(index)
+  def apply(index: Int): T = ref.get.apply(index)
 
   override def hashCode: Int = System.identityHashCode(this);
 
diff --git a/akka-core/src/main/scala/stm/global/Atomic.scala b/akka-core/src/main/scala/stm/global/Atomic.scala
new file mode 100644
index 0000000000..d5a92fe047
--- /dev/null
+++ b/akka-core/src/main/scala/stm/global/Atomic.scala
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB 
+ */
+
+package se.scalablesolutions.akka.stm.global
+
+/**
+ * Java-friendly atomic blocks.
+ * 

+ * Example usage (in Java): + *

+ *

+ * import se.scalablesolutions.akka.stm.*;
+ * import se.scalablesolutions.akka.stm.global.Atomic;
+ *
+ * final Ref ref = new Ref(0);
+ *
+ * new Atomic() {
+ *     public Object atomically() {
+ *         return ref.set(1);
+ *     }
+ * }.execute();
+ *
+ * // To configure transactions pass a TransactionFactory
+ *
+ * TransactionFactory txFactory = new TransactionFactoryBuilder()
+ *     .setReadonly(true)
+ *     .build();
+ *
+ * Integer value = new Atomic(txFactory) {
+ *     public Integer atomically() {
+ *         return ref.get();
+ *     }
+ * }.execute();
+ * 
+ */ +abstract class Atomic[T](factory: TransactionFactory) { + def this() = this(DefaultGlobalTransactionFactory) + def atomically: T + def execute: T = atomic(factory)(atomically) +} diff --git a/akka-core/src/main/scala/stm/global/GlobalStm.scala b/akka-core/src/main/scala/stm/global/GlobalStm.scala new file mode 100644 index 0000000000..1fd53ffe51 --- /dev/null +++ b/akka-core/src/main/scala/stm/global/GlobalStm.scala @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +import se.scalablesolutions.akka.util.Logging + +import org.multiverse.api.{Transaction => MultiverseTransaction} +import org.multiverse.templates.TransactionalCallable + +/** + * Global transaction management, global in the context of multiple threads. + * Use this if you need to have one transaction span multiple threads (or Actors). + *

+ * Example of atomic transaction management using the atomic block: + *

+ *

+ * import se.scalablesolutions.akka.stm.global._
+ *
+ * atomic  {
+ *   // do something within a transaction
+ * }
+ * 
+ */ +class GlobalStm extends TransactionManagement with Logging { + + val DefaultGlobalTransactionConfig = TransactionConfig() + val DefaultGlobalTransactionFactory = TransactionFactory(DefaultGlobalTransactionConfig, "DefaultGlobalTransaction") + + def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultGlobalTransactionFactory): T = atomic(factory)(body) + + def atomic[T](factory: TransactionFactory)(body: => T): T = { + factory.boilerplate.execute(new TransactionalCallable[T]() { + def call(mtx: MultiverseTransaction): T = { + if (!isTransactionSetInScope) createNewTransactionSet + factory.addHooks + val result = body + val txSet = getTransactionSetInScope + log.trace("Committing global transaction [" + mtx + "]\n\tand joining transaction set [" + txSet + "]") + try { + txSet.tryJoinCommit( + mtx, + TransactionConfig.DefaultTimeout.length, + TransactionConfig.DefaultTimeout.unit) + // Need to catch IllegalStateException until we have fix in Multiverse, since it throws it by mistake + } catch { case e: IllegalStateException => {} } + result + } + }) + } +} + diff --git a/akka-core/src/main/scala/stm/global/package.scala b/akka-core/src/main/scala/stm/global/package.scala new file mode 100644 index 0000000000..9b8a1b289e --- /dev/null +++ b/akka-core/src/main/scala/stm/global/package.scala @@ -0,0 +1,10 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +/** + * For easily importing global STM. + */ +package object global extends GlobalStm with StmUtil with StmCommon diff --git a/akka-core/src/main/scala/stm/local/Atomic.scala b/akka-core/src/main/scala/stm/local/Atomic.scala new file mode 100644 index 0000000000..c06f99ba99 --- /dev/null +++ b/akka-core/src/main/scala/stm/local/Atomic.scala @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm.local + +/** + * Java-friendly atomic blocks. + *

+ * Example usage (in Java): + *

+ *

+ * import se.scalablesolutions.akka.stm.*;
+ * import se.scalablesolutions.akka.stm.local.Atomic;
+ *
+ * final Ref ref = new Ref(0);
+ *
+ * new Atomic() {
+ *     public Object atomically() {
+ *         return ref.set(1);
+ *     }
+ * }.execute();
+ *
+ * // To configure transactions pass a TransactionFactory
+ *
+ * TransactionFactory txFactory = new TransactionFactoryBuilder()
+ *     .setReadonly(true)
+ *     .build();
+ *
+ * Integer value = new Atomic(txFactory) {
+ *     public Integer atomically() {
+ *         return ref.get();
+ *     }
+ * }.execute();
+ * 
+ */ +abstract class Atomic[T](factory: TransactionFactory) { + def this() = this(DefaultLocalTransactionFactory) + def atomically: T + def execute: T = atomic(factory)(atomically) +} diff --git a/akka-core/src/main/scala/stm/local/LocalStm.scala b/akka-core/src/main/scala/stm/local/LocalStm.scala new file mode 100644 index 0000000000..477027aab2 --- /dev/null +++ b/akka-core/src/main/scala/stm/local/LocalStm.scala @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +import se.scalablesolutions.akka.util.Logging + +import org.multiverse.api.{Transaction => MultiverseTransaction} +import org.multiverse.templates.TransactionalCallable + +/** + * Local transaction management, local in the context of threads. + * Use this if you do not need to have one transaction span + * multiple threads (or Actors). + *

+ * Example of atomic transaction management using the atomic block. + *

+ *

+ * import se.scalablesolutions.akka.stm.local._
+ *
+ * atomic  {
+ *   // do something within a transaction
+ * }
+ * 
+ */ +class LocalStm extends TransactionManagement with Logging { + + val DefaultLocalTransactionConfig = TransactionConfig() + val DefaultLocalTransactionFactory = TransactionFactory(DefaultLocalTransactionConfig, "DefaultLocalTransaction") + + def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultLocalTransactionFactory): T = atomic(factory)(body) + + def atomic[T](factory: TransactionFactory)(body: => T): T = { + factory.boilerplate.execute(new TransactionalCallable[T]() { + def call(mtx: MultiverseTransaction): T = { + factory.addHooks + val result = body + log.trace("Committing local transaction [" + mtx + "]") + result + } + }) + } +} diff --git a/akka-core/src/main/scala/stm/local/package.scala b/akka-core/src/main/scala/stm/local/package.scala new file mode 100644 index 0000000000..406d4880f6 --- /dev/null +++ b/akka-core/src/main/scala/stm/local/package.scala @@ -0,0 +1,10 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +/** + * For easily importing local STM. + */ +package object local extends LocalStm with StmUtil with StmCommon diff --git a/akka-core/src/main/scala/stm/packages.scala b/akka-core/src/main/scala/stm/transactional.scala similarity index 68% rename from akka-core/src/main/scala/stm/packages.scala rename to akka-core/src/main/scala/stm/transactional.scala index cbb3ad4804..e00c7ef8e9 100644 --- a/akka-core/src/main/scala/stm/packages.scala +++ b/akka-core/src/main/scala/stm/transactional.scala @@ -5,28 +5,7 @@ package se.scalablesolutions.akka.stm /** - * For importing 'local' STM. - */ -package object local extends LocalStm with StmUtil with StmCommon - -/** - * For importing 'global' STM. - */ -package object global extends GlobalStm with StmUtil with StmCommon - -trait StmCommon { - type TransactionConfig = se.scalablesolutions.akka.stm.TransactionConfig - val TransactionConfig = se.scalablesolutions.akka.stm.TransactionConfig - - type TransactionFactory = se.scalablesolutions.akka.stm.TransactionFactory - val TransactionFactory = se.scalablesolutions.akka.stm.TransactionFactory - - type Ref[T] = se.scalablesolutions.akka.stm.Ref[T] - val Ref = se.scalablesolutions.akka.stm.Ref -} - -/** - * For importing the transactional data structures, including the primitive refs + * For importing the transactional datastructures, including the primitive refs * and transactional data structures from Multiverse. */ package object transactional { diff --git a/akka-core/src/main/scala/util/AkkaException.scala b/akka-core/src/main/scala/util/AkkaException.scala new file mode 100644 index 0000000000..ca8284ced5 --- /dev/null +++ b/akka-core/src/main/scala/util/AkkaException.scala @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka + +import se.scalablesolutions.akka.util.{UUID, Logging} + +import java.io.{StringWriter, PrintWriter} +import java.net.{InetAddress, UnknownHostException} + +/** + * Akka base Exception. Each Exception gets: + *
    + *
  • a UUID for tracking purposes
  • + *
  • a message including exception name, uuid, original message and the stacktrace
  • + *
  • a method 'log' that will log the exception once and only once
  • + *
+ * + * @author Jonas Bonér + */ +@serializable abstract class AkkaException(message: String) extends RuntimeException(message) { + @volatile private var isLogged = false + val exceptionName = getClass.getName + + val uuid = String.format("%s_%s", AkkaException.hostname, UUID.newUuid.toString) + + override val toString = + String.format("%s\n\t[%s]\n\t%s\n\t%s", exceptionName, uuid, message, stackTrace) + + val stackTrace = { + val sw = new StringWriter + val pw = new PrintWriter(sw) + printStackTrace(pw) + sw.toString + } + + def log = if (!isLogged) { + isLogged = true + AkkaException.log.error(toString) + } +} + +object AkkaException extends Logging { + val hostname = try { + InetAddress.getLocalHost.getHostName + } catch { + case e: UnknownHostException => "unknown" + } +} diff --git a/akka-core/src/main/scala/util/Duration.scala b/akka-core/src/main/scala/util/Duration.scala index f49e1ae04b..0dee2fc139 100644 --- a/akka-core/src/main/scala/util/Duration.scala +++ b/akka-core/src/main/scala/util/Duration.scala @@ -20,8 +20,21 @@ object Duration { /** * Utility for working with java.util.concurrent.TimeUnit durations. + * *

- * Example: + * Examples of usage from Java: + *

+ * import se.scalablesolutions.akka.util.Duration;
+ * import java.util.concurrent.TimeUnit;
+ *
+ * Duration duration = new Duration(100, TimeUnit.MILLISECONDS);
+ * Duration duration = new Duration(5, "seconds");
+ *
+ * duration.toNanos();
+ * 
+ * + *

+ * Examples of usage from Scala: *

  * import se.scalablesolutions.akka.util.Duration
  * import java.util.concurrent.TimeUnit
@@ -31,6 +44,7 @@ object Duration {
  *
  * duration.toNanos
  * 
+ * *

* Implicits are also provided for Int and Long. Example usage: *

@@ -40,6 +54,7 @@ object Duration {
  * 
*/ class Duration(val length: Long, val unit: TimeUnit) { + def this(length: Long, unit: String) = this(length, Duration.timeUnit(unit)) def toNanos = unit.toNanos(length) def toMicros = unit.toMicros(length) def toMillis = unit.toMillis(length) diff --git a/akka-core/src/main/scala/util/Helpers.scala b/akka-core/src/main/scala/util/Helpers.scala index ccbd896610..eab9e1981d 100644 --- a/akka-core/src/main/scala/util/Helpers.scala +++ b/akka-core/src/main/scala/util/Helpers.scala @@ -6,8 +6,6 @@ package se.scalablesolutions.akka.util import java.security.MessageDigest -class SystemFailure(cause: Throwable) extends RuntimeException(cause) - /** * @author Jonas Bonér */ diff --git a/akka-core/src/main/scala/util/ListenerManagement.scala b/akka-core/src/main/scala/util/ListenerManagement.scala index 7316beba64..cfcb5ac2b6 100644 --- a/akka-core/src/main/scala/util/ListenerManagement.scala +++ b/akka-core/src/main/scala/util/ListenerManagement.scala @@ -4,7 +4,7 @@ package se.scalablesolutions.akka.util -import java.util.concurrent.CopyOnWriteArrayList +import java.util.concurrent.ConcurrentSkipListSet import se.scalablesolutions.akka.actor.ActorRef @@ -15,7 +15,7 @@ import se.scalablesolutions.akka.actor.ActorRef */ trait ListenerManagement extends Logging { - private val listeners = new CopyOnWriteArrayList[ActorRef] + private val listeners = new ConcurrentSkipListSet[ActorRef] /** * Adds the listener this this registry's listener list. @@ -23,7 +23,7 @@ trait ListenerManagement extends Logging { */ def addListener(listener: ActorRef) = { listener.start - listeners.add(listener) + listeners add listener } /** @@ -31,8 +31,8 @@ trait ListenerManagement extends Logging { * The listener is stopped by this method. */ def removeListener(listener: ActorRef) = { + listeners remove listener listener.stop - listeners.remove(listener) } /** diff --git a/akka-core/src/main/scala/util/Logging.scala b/akka-core/src/main/scala/util/Logging.scala index 8c6ea0fb99..1fed0d017a 100644 --- a/akka-core/src/main/scala/util/Logging.scala +++ b/akka-core/src/main/scala/util/Logging.scala @@ -4,7 +4,7 @@ package se.scalablesolutions.akka.util -import net.lag.logging.Logger +import org.slf4j.{Logger => SLFLogger,LoggerFactory => SLFLoggerFactory} import java.io.StringWriter import java.io.PrintWriter @@ -17,55 +17,146 @@ import java.net.UnknownHostException * @author Jonas Bonér */ trait Logging { - @sjson.json.JSONProperty(ignore = true) @transient lazy val log = Logger.get(this.getClass.getName) + @sjson.json.JSONProperty(ignore = true) @transient lazy val log = Logger(this.getClass.getName) } /** - * LoggableException is a subclass of Exception and can be used as the base exception - * for application specific exceptions. - *

- * It keeps track of the exception is logged or not and also stores the unique id, - * so that it can be carried all along to the client tier and displayed to the end user. - * The end user can call up the customer support using this number. + * Scala SLF4J wrapper * - * @author Jonas Bonér + * Example: + *

+ * class Foo extends Logging {
+ *   log.info("My foo is %s","alive")
+ *   log.error(new Exception(),"My foo is %s","broken")
+ * }
+ * 
+ * + * The logger uses String.format: + * http://download-llnw.oracle.com/javase/6/docs/api/java/lang/String.html#format(java.lang.String,%20java.lang.Object...) */ - // FIXME make use of LoggableException -class LoggableException extends Exception with Logging { - private val uniqueId = getExceptionID - private var originalException: Option[Exception] = None - private var isLogged = false +class Logger(val logger: SLFLogger) { + def name = logger.getName - def this(baseException: Exception) = { - this() - originalException = Some(baseException) + def trace_? = logger.isTraceEnabled + def debug_? = logger.isDebugEnabled + def info_? = logger.isInfoEnabled + def warning_? = logger.isWarnEnabled + def error_? = logger.isErrorEnabled + + //Trace + def trace(t: Throwable, fmt: => String, arg: Any, argN: Any*) { + trace(t,message(fmt,arg,argN:_*)) } - def logException = synchronized { - if (!isLogged) { - originalException match { - case Some(e) => log.error("Logged Exception [%s] %s", uniqueId, getStackTraceAsString(e)) - case None => log.error("Logged Exception [%s] %s", uniqueId, getStackTraceAsString(this)) - } - isLogged = true - } + def trace(t: Throwable, msg: => String) { + if (trace_?) logger.trace(msg,t) } - private def getExceptionID: String = { - val hostname: String = try { - InetAddress.getLocalHost.getHostName - } catch { - case e: UnknownHostException => - log.error("Could not get hostname to generate loggable exception") - "N/A" - } - hostname + "_" + System.currentTimeMillis + def trace(fmt: => String, arg: Any, argN: Any*) { + trace(message(fmt,arg,argN:_*)) } - private def getStackTraceAsString(exception: Throwable): String = { - val sw = new StringWriter - val pw = new PrintWriter(sw) - exception.printStackTrace(pw) - sw.toString + def trace(msg: => String) { + if (trace_?) logger trace msg + } + + //Debug + def debug(t: Throwable, fmt: => String, arg: Any, argN: Any*) { + debug(t,message(fmt,arg,argN:_*)) + } + + def debug(t: Throwable, msg: => String) { + if (debug_?) logger.debug(msg,t) + } + + def debug(fmt: => String, arg: Any, argN: Any*) { + debug(message(fmt,arg,argN:_*)) + } + + def debug(msg: => String) { + if (debug_?) logger debug msg + } + + //Info + def info(t: Throwable, fmt: => String, arg: Any, argN: Any*) { + info(t,message(fmt,arg,argN:_*)) + } + + def info(t: Throwable, msg: => String) { + if (info_?) logger.info(msg,t) + } + + def info(fmt: => String, arg: Any, argN: Any*) { + info(message(fmt,arg,argN:_*)) + } + + def info(msg: => String) { + if (info_?) logger info msg + } + + //Warning + def warning(t: Throwable, fmt: => String, arg: Any, argN: Any*) { + warning(t,message(fmt,arg,argN:_*)) + } + + def warning(t: Throwable, msg: => String) { + if (warning_?) logger.warn(msg,t) + } + + def warning(fmt: => String, arg: Any, argN: Any*) { + warning(message(fmt,arg,argN:_*)) + } + + def warning(msg: => String) { + if (warning_?) logger warn msg + } + + //Error + def error(t: Throwable, fmt: => String, arg: Any, argN: Any*) { + error(t,message(fmt,arg,argN:_*)) + } + + def error(t: Throwable, msg: => String) { + if (error_?) logger.error(msg,t) + } + + def error(fmt: => String, arg: Any, argN: Any*) { + error(message(fmt,arg,argN:_*)) + } + + def error(msg: => String) { + if (error_?) logger error msg + } + + protected def message(fmt: String, arg: Any, argN: Any*) : String = { + if ((argN eq null) || argN.isEmpty) fmt.format(arg) + else fmt.format((arg +: argN):_*) } } + +/** + * Logger factory + * + * ex. + * + * val logger = Logger("my.cool.logger") + * val logger = Logger(classOf[Banana]) + * val rootLogger = Logger.root + * + */ +object Logger { + + /* Uncomment to be able to debug what logging configuration will be used + { + import org.slf4j.LoggerFactory + import ch.qos.logback.classic.LoggerContext + import ch.qos.logback.core.util.StatusPrinter + + // print logback's internal status + StatusPrinter.print(LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext]) + }*/ + + def apply(logger: String) : Logger = new Logger(SLFLoggerFactory getLogger logger) + def apply(clazz: Class[_]) : Logger = apply(clazz.getName) + def root : Logger = apply(SLFLogger.ROOT_LOGGER_NAME) +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/ActiveObjectFailer.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/ActiveObjectFailer.java deleted file mode 100644 index 6e30a1a971..0000000000 --- a/akka-core/src/test/java/se/scalablesolutions/akka/actor/ActiveObjectFailer.java +++ /dev/null @@ -1,7 +0,0 @@ -package se.scalablesolutions.akka.actor; - -public class ActiveObjectFailer implements java.io.Serializable { - public int fail() { - throw new RuntimeException("expected"); - } -} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/BarImpl.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/BarImpl.java index 09b50a7347..9cb41a85cf 100644 --- a/akka-core/src/test/java/se/scalablesolutions/akka/actor/BarImpl.java +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/BarImpl.java @@ -1,13 +1,16 @@ package se.scalablesolutions.akka.actor; import com.google.inject.Inject; +import se.scalablesolutions.akka.actor.*; -public class BarImpl implements Bar { +public class BarImpl extends TypedActor implements Bar { @Inject private Ext ext; + public Ext getExt() { return ext; } + public void bar(String msg) { } } diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/Foo.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/Foo.java index 87eb809aba..4cc5b977dc 100644 --- a/akka-core/src/test/java/se/scalablesolutions/akka/actor/Foo.java +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/Foo.java @@ -1,34 +1,14 @@ package se.scalablesolutions.akka.actor; -import com.google.inject.Inject; - -public class Foo extends se.scalablesolutions.akka.serialization.Serializable.JavaJSON { - @Inject - private Bar bar; - public Foo body() { return this; } - public Bar getBar() { - return bar; - } - public String foo(String msg) { - return msg + "return_foo "; - } - public void bar(String msg) { - bar.bar(msg); - } - public String longRunning() { - try { - Thread.sleep(1200); - } catch (InterruptedException e) { - } - return "test"; - } - public String throwsException() { - if (true) throw new RuntimeException("Expected exception; to test fault-tolerance"); - return "test"; - } +public interface Foo { + public Foo body(); + public Bar getBar(); - public int $tag() throws java.rmi.RemoteException - { - return 0; - } + public String foo(String msg); + public void bar(String msg); + + public String longRunning(); + public String throwsException(); + + public int $tag() throws java.rmi.RemoteException; } diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/FooImpl.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/FooImpl.java new file mode 100644 index 0000000000..dc6aba481c --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/FooImpl.java @@ -0,0 +1,40 @@ +package se.scalablesolutions.akka.actor; + +import com.google.inject.Inject; +import se.scalablesolutions.akka.actor.*; + +public class FooImpl extends TypedActor implements Foo { + @Inject + private Bar bar; + + public Foo body() { return this; } + + public Bar getBar() { + return bar; + } + + public String foo(String msg) { + return msg + "return_foo "; + } + + public void bar(String msg) { + bar.bar(msg); + } + + public String longRunning() { + try { + Thread.sleep(1200); + } catch (InterruptedException e) { + } + return "test"; + } + + public String throwsException() { + if (true) throw new RuntimeException("Expected exception; to test fault-tolerance"); + return "test"; + } + + public int $tag() throws java.rmi.RemoteException { + return 0; + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/NestedTransactionalTypedActor.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/NestedTransactionalTypedActor.java new file mode 100644 index 0000000000..ee7998f69a --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/NestedTransactionalTypedActor.java @@ -0,0 +1,12 @@ +package se.scalablesolutions.akka.actor; + +public interface NestedTransactionalTypedActor { + public String getMapState(String key); + public String getVectorState(); + public String getRefState(); + public void setMapState(String key, String msg); + public void setVectorState(String msg); + public void setRefState(String msg); + public void success(String key, String msg); + public String failure(String key, String msg, TypedActorFailer failer); +} \ No newline at end of file diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/NestedTransactionalActiveObject.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/NestedTransactionalTypedActorImpl.java similarity index 59% rename from akka-core/src/test/java/se/scalablesolutions/akka/actor/NestedTransactionalActiveObject.java rename to akka-core/src/test/java/se/scalablesolutions/akka/actor/NestedTransactionalTypedActorImpl.java index af6bb8245c..5b7eab615e 100644 --- a/akka-core/src/test/java/se/scalablesolutions/akka/actor/NestedTransactionalActiveObject.java +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/NestedTransactionalTypedActorImpl.java @@ -1,17 +1,15 @@ package se.scalablesolutions.akka.actor; -import se.scalablesolutions.akka.actor.annotation.transactionrequired; -import se.scalablesolutions.akka.actor.annotation.inittransactionalstate; +import se.scalablesolutions.akka.actor.*; import se.scalablesolutions.akka.stm.*; -@transactionrequired -public class NestedTransactionalActiveObject { +public class NestedTransactionalTypedActorImpl extends TypedTransactor implements NestedTransactionalTypedActor { private TransactionalMap mapState; private TransactionalVector vectorState; private Ref refState; private boolean isInitialized = false; - @inittransactionalstate + @Override public void init() { if (!isInitialized) { mapState = new TransactionalMap(); @@ -25,62 +23,37 @@ public class NestedTransactionalActiveObject { return (String) mapState.get(key).get(); } - public String getVectorState() { return (String) vectorState.last(); } - public String getRefState() { - return (String) refState.get().get(); + return (String) refState.get(); } - public void setMapState(String key, String msg) { mapState.put(key, msg); } - public void setVectorState(String msg) { vectorState.add(msg); } - public void setRefState(String msg) { refState.swap(msg); } - public void success(String key, String msg) { mapState.put(key, msg); vectorState.add(msg); refState.swap(msg); } - - public String failure(String key, String msg, ActiveObjectFailer failer) { + public String failure(String key, String msg, TypedActorFailer failer) { mapState.put(key, msg); vectorState.add(msg); refState.swap(msg); failer.fail(); return msg; } - - - public void thisMethodHangs(String key, String msg, ActiveObjectFailer failer) { - setMapState(key, msg); - } - - /* - public void clashOk(String key, String msg, InMemClasher clasher) { - mapState.put(key, msg); - clasher.clash(); - } - - public void clashNotOk(String key, String msg, InMemClasher clasher) { - mapState.put(key, msg); - clasher.clash(); - this.success("clash", "clash"); - } - */ } \ No newline at end of file diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorOne.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorOne.java new file mode 100644 index 0000000000..03df632582 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorOne.java @@ -0,0 +1,6 @@ +package se.scalablesolutions.akka.actor; + +public interface RemoteTypedActorOne { + public String requestReply(String s) throws Exception; + public void oneWay() throws Exception; +} \ No newline at end of file diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorOneImpl.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorOneImpl.java new file mode 100644 index 0000000000..0744652181 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorOneImpl.java @@ -0,0 +1,29 @@ +package se.scalablesolutions.akka.actor.remote; + +import se.scalablesolutions.akka.actor.*; + +import java.util.concurrent.CountDownLatch; + +public class RemoteTypedActorOneImpl extends TypedActor implements RemoteTypedActorOne { + + public static CountDownLatch latch = new CountDownLatch(1); + + public String requestReply(String s) throws Exception { + if (s.equals("ping")) { + RemoteTypedActorLog.messageLog().put("ping"); + return "pong"; + } else if (s.equals("die")) { + throw new RuntimeException("Expected exception; to test fault-tolerance"); + } else return null; + } + + public void oneWay() throws Exception { + RemoteTypedActorLog.oneWayLog().put("oneway"); + } + + @Override + public void preRestart(Throwable e) { + try { RemoteTypedActorLog.messageLog().put(e.getMessage()); } catch(Exception ex) {} + latch.countDown(); + } +} \ No newline at end of file diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorTwo.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorTwo.java new file mode 100644 index 0000000000..58f294c6cd --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorTwo.java @@ -0,0 +1,6 @@ +package se.scalablesolutions.akka.actor; + +public interface RemoteTypedActorTwo { + public String requestReply(String s) throws Exception; + public void oneWay() throws Exception; +} \ No newline at end of file diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorTwoImpl.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorTwoImpl.java new file mode 100644 index 0000000000..36bb055ef8 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/RemoteTypedActorTwoImpl.java @@ -0,0 +1,29 @@ +package se.scalablesolutions.akka.actor.remote; + +import se.scalablesolutions.akka.actor.*; + +import java.util.concurrent.CountDownLatch; + +public class RemoteTypedActorTwoImpl extends TypedActor implements RemoteTypedActorTwo { + + public static CountDownLatch latch = new CountDownLatch(1); + + public String requestReply(String s) throws Exception { + if (s.equals("ping")) { + RemoteTypedActorLog.messageLog().put("ping"); + return "pong"; + } else if (s.equals("die")) { + throw new RuntimeException("Expected exception; to test fault-tolerance"); + } else return null; + } + + public void oneWay() throws Exception { + RemoteTypedActorLog.oneWayLog().put("oneway"); + } + + @Override + public void preRestart(Throwable e) { + try { RemoteTypedActorLog.messageLog().put(e.getMessage()); } catch(Exception ex) {} + latch.countDown(); + } +} \ No newline at end of file diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/SamplePojo.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SamplePojo.java index 50f3e43221..ae47276ba6 100644 --- a/akka-core/src/test/java/se/scalablesolutions/akka/actor/SamplePojo.java +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SamplePojo.java @@ -2,36 +2,7 @@ package se.scalablesolutions.akka.actor; import java.util.concurrent.CountDownLatch; -public class SamplePojo { - - private CountDownLatch latch; - - public boolean _pre = false; - public boolean _post = false; - public boolean _down = false; - - public CountDownLatch newCountdownLatch(int count) { - latch = new CountDownLatch(count); - return latch; - } - - public String fail() { - throw new RuntimeException("expected"); - } - - public void pre() { - _pre = true; - latch.countDown(); - } - - public void post() { - _post = true; - latch.countDown(); - } - - public void down() { - _down = true; - latch.countDown(); - } - -} +public interface SamplePojo { + public String greet(String s); + public String fail(); +} \ No newline at end of file diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/SamplePojoAnnotated.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SamplePojoAnnotated.java deleted file mode 100644 index 8bf4ba36d3..0000000000 --- a/akka-core/src/test/java/se/scalablesolutions/akka/actor/SamplePojoAnnotated.java +++ /dev/null @@ -1,52 +0,0 @@ -package se.scalablesolutions.akka.actor; - -import se.scalablesolutions.akka.actor.annotation.postrestart; -import se.scalablesolutions.akka.actor.annotation.prerestart; -import se.scalablesolutions.akka.actor.annotation.shutdown; - -import java.util.concurrent.CountDownLatch; - -public class SamplePojoAnnotated { - - private CountDownLatch latch; - - public boolean _pre = false; - public boolean _post = false; - public boolean _down = false; - - public SamplePojoAnnotated() { - latch = new CountDownLatch(1); - } - - public CountDownLatch newCountdownLatch(int count) { - latch = new CountDownLatch(count); - return latch; - } - - public String greet(String s) { - return "hello " + s; - } - - public String fail() { - throw new RuntimeException("expected"); - } - - @prerestart - public void pre() { - _pre = true; - latch.countDown(); - } - - @postrestart - public void post() { - _post = true; - latch.countDown(); - } - - @shutdown - public void down() { - _down = true; - latch.countDown(); - } - -} \ No newline at end of file diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/SamplePojoImpl.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SamplePojoImpl.java new file mode 100644 index 0000000000..d57232b629 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SamplePojoImpl.java @@ -0,0 +1,45 @@ +package se.scalablesolutions.akka.actor; + +import se.scalablesolutions.akka.actor.*; + +import java.util.concurrent.CountDownLatch; + +public class SamplePojoImpl extends TypedActor implements SamplePojo { + + public static CountDownLatch latch = new CountDownLatch(1); + + public static boolean _pre = false; + public static boolean _post = false; + public static boolean _down = false; + public static void reset() { + _pre = false; + _post = false; + _down = false; + } + + public String greet(String s) { + return "hello " + s; + } + + public String fail() { + throw new RuntimeException("expected"); + } + + @Override + public void preRestart(Throwable e) { + _pre = true; + latch.countDown(); + } + + @Override + public void postRestart(Throwable e) { + _post = true; + latch.countDown(); + } + + @Override + public void shutdown() { + _down = true; + latch.countDown(); + } +} \ No newline at end of file diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojo.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojo.java index 31f22c217f..d3a18abbd9 100644 --- a/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojo.java +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojo.java @@ -1,48 +1,14 @@ package se.scalablesolutions.akka.actor; -import se.scalablesolutions.akka.actor.annotation.prerestart; -import se.scalablesolutions.akka.actor.annotation.postrestart; -import se.scalablesolutions.akka.actor.ActiveObjectContext; +import se.scalablesolutions.akka.dispatch.Future; import se.scalablesolutions.akka.dispatch.CompletableFuture; +import se.scalablesolutions.akka.dispatch.Future; -public class SimpleJavaPojo { - - ActiveObjectContext context; - - public boolean pre = false; - public boolean post = false; - - private String name; - - public Object getSender() { - return context.getSender(); - } - - public CompletableFuture getSenderFuture() { - return context.getSenderFuture(); - } - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - @prerestart - public void pre() { - System.out.println("** pre()"); - pre = true; - } - - @postrestart - public void post() { - System.out.println("** post()"); - post = true; - } - - public void throwException() { - throw new RuntimeException(); - } +public interface SimpleJavaPojo { + public Object getSender(); + public Object getSenderFuture(); + public Future square(int value); + public void setName(String name); + public String getName(); + public void throwException(); } diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoCaller.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoCaller.java index 0fb6aff9c5..e35702846f 100644 --- a/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoCaller.java +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoCaller.java @@ -2,19 +2,8 @@ package se.scalablesolutions.akka.actor; import se.scalablesolutions.akka.dispatch.CompletableFuture; -public class SimpleJavaPojoCaller { - - SimpleJavaPojo pojo; - - public void setPojo(SimpleJavaPojo pojo) { - this.pojo = pojo; - } - - public Object getSenderFromSimpleJavaPojo() { - return pojo.getSender(); - } - - public CompletableFuture getSenderFutureFromSimpleJavaPojo() { - return pojo.getSenderFuture(); - } +public interface SimpleJavaPojoCaller { + public void setPojo(SimpleJavaPojo pojo); + public Object getSenderFromSimpleJavaPojo(); + public Object getSenderFutureFromSimpleJavaPojo(); } diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoCallerImpl.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoCallerImpl.java new file mode 100644 index 0000000000..3fbc994f71 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoCallerImpl.java @@ -0,0 +1,26 @@ +package se.scalablesolutions.akka.actor; + +import se.scalablesolutions.akka.actor.*; +import se.scalablesolutions.akka.dispatch.Future; + +public class SimpleJavaPojoCallerImpl extends TypedActor implements SimpleJavaPojoCaller { + + SimpleJavaPojo pojo; + + public void setPojo(SimpleJavaPojo pojo) { + this.pojo = pojo; + } + + public Object getSenderFromSimpleJavaPojo() { + Object sender = pojo.getSender(); + return sender; + } + + public Object getSenderFutureFromSimpleJavaPojo() { + return pojo.getSenderFuture(); + } + + public Future square(int value) { + return future(value * value); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoImpl.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoImpl.java new file mode 100644 index 0000000000..c02d266ce8 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoImpl.java @@ -0,0 +1,53 @@ +package se.scalablesolutions.akka.actor; + +import se.scalablesolutions.akka.actor.*; +import se.scalablesolutions.akka.dispatch.Future; +import se.scalablesolutions.akka.dispatch.CompletableFuture; + +public class SimpleJavaPojoImpl extends TypedActor implements SimpleJavaPojo { + + public static boolean _pre = false; + public static boolean _post = false; + public static boolean _down = false; + public static void reset() { + _pre = false; + _post = false; + _down = false; + } + + private String name; + + public Future square(int value) { + return future(value * value); + } + + public Object getSender() { + return getContext().getSender(); + } + + public CompletableFuture getSenderFuture() { + return getContext().getSenderFuture().get(); + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public void preRestart(Throwable e) { + _pre = true; + } + + @Override + public void postRestart(Throwable e) { + _post = true; + } + + public void throwException() { + throw new RuntimeException(); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/TransactionalTypedActor.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/TransactionalTypedActor.java new file mode 100644 index 0000000000..6e7c43745b --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/TransactionalTypedActor.java @@ -0,0 +1,14 @@ +package se.scalablesolutions.akka.actor; + +public interface TransactionalTypedActor { + public String getMapState(String key); + public String getVectorState(); + public String getRefState(); + public void setMapState(String key, String msg); + public void setVectorState(String msg); + public void setRefState(String msg); + public void success(String key, String msg); + public void success(String key, String msg, NestedTransactionalTypedActor nested); + public String failure(String key, String msg, TypedActorFailer failer); + public String failure(String key, String msg, NestedTransactionalTypedActor nested, TypedActorFailer failer); +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/TransactionalActiveObject.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/TransactionalTypedActorImpl.java similarity index 67% rename from akka-core/src/test/java/se/scalablesolutions/akka/actor/TransactionalActiveObject.java rename to akka-core/src/test/java/se/scalablesolutions/akka/actor/TransactionalTypedActorImpl.java index 515f4fafee..f992028caf 100644 --- a/akka-core/src/test/java/se/scalablesolutions/akka/actor/TransactionalActiveObject.java +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/TransactionalTypedActorImpl.java @@ -1,20 +1,16 @@ package se.scalablesolutions.akka.actor; -import se.scalablesolutions.akka.actor.annotation.transactionrequired; -import se.scalablesolutions.akka.actor.annotation.prerestart; -import se.scalablesolutions.akka.actor.annotation.postrestart; -import se.scalablesolutions.akka.actor.annotation.inittransactionalstate; +import se.scalablesolutions.akka.actor.*; import se.scalablesolutions.akka.stm.*; -@transactionrequired -public class TransactionalActiveObject { +public class TransactionalTypedActorImpl extends TypedTransactor implements TransactionalTypedActor { 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(); @@ -32,7 +28,7 @@ public class TransactionalActiveObject { } public String getRefState() { - return (String)refState.get().get(); + return (String)refState.get(); } public void setMapState(String key, String msg) { @@ -53,14 +49,14 @@ public class TransactionalActiveObject { refState.swap(msg); } - public void success(String key, String msg, NestedTransactionalActiveObject nested) { + public void success(String key, String msg, NestedTransactionalTypedActor nested) { mapState.put(key, msg); vectorState.add(msg); refState.swap(msg); nested.success(key, msg); } - public String failure(String key, String msg, ActiveObjectFailer failer) { + public String failure(String key, String msg, TypedActorFailer failer) { mapState.put(key, msg); vectorState.add(msg); refState.swap(msg); @@ -68,7 +64,7 @@ public class TransactionalActiveObject { return msg; } - public String failure(String key, String msg, NestedTransactionalActiveObject nested, ActiveObjectFailer failer) { + public String failure(String key, String msg, NestedTransactionalTypedActor nested, TypedActorFailer failer) { mapState.put(key, msg); vectorState.add(msg); refState.swap(msg); @@ -76,17 +72,13 @@ public class TransactionalActiveObject { return msg; } - public void thisMethodHangs(String key, String msg, ActiveObjectFailer failer) { - setMapState(key, msg); - } - - @prerestart - public void preRestart() { + @Override + public void preRestart(Throwable e) { System.out.println("################ PRE RESTART"); } - @postrestart - public void postRestart() { + @Override + public void postRestart(Throwable e) { System.out.println("################ POST RESTART"); } } diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/TypedActorFailer.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/TypedActorFailer.java new file mode 100644 index 0000000000..b4a69e1cd1 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/TypedActorFailer.java @@ -0,0 +1,5 @@ +package se.scalablesolutions.akka.actor; + +public interface TypedActorFailer extends java.io.Serializable { + public int fail(); +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/actor/TypedActorFailerImpl.java b/akka-core/src/test/java/se/scalablesolutions/akka/actor/TypedActorFailerImpl.java new file mode 100644 index 0000000000..0d01fd801c --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/actor/TypedActorFailerImpl.java @@ -0,0 +1,9 @@ +package se.scalablesolutions.akka.actor; + +import se.scalablesolutions.akka.actor.*; + +public class TypedActorFailerImpl extends TypedActor implements TypedActorFailer { + public int fail() { + throw new RuntimeException("expected"); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/Address.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/Address.java new file mode 100644 index 0000000000..cb3057929f --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/Address.java @@ -0,0 +1,13 @@ +package se.scalablesolutions.akka.stm; + +public class Address { + private String location; + + public Address(String location) { + this.location = location; + } + + @Override public String toString() { + return "Address(" + location + ")"; + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/CounterExample.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/CounterExample.java new file mode 100644 index 0000000000..57a9a07daa --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/CounterExample.java @@ -0,0 +1,26 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.Ref; +import se.scalablesolutions.akka.stm.local.Atomic; + +public class CounterExample { + final static Ref ref = new Ref(0); + + public static int counter() { + return new Atomic() { + public Integer atomically() { + int inc = ref.get() + 1; + ref.set(inc); + return inc; + } + }.execute(); + } + + public static void main(String[] args) { + System.out.println(); + System.out.println("Counter example"); + System.out.println(); + System.out.println("counter 1: " + counter()); + System.out.println("counter 2: " + counter()); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/JavaStmTests.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/JavaStmTests.java new file mode 100644 index 0000000000..7204013808 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/JavaStmTests.java @@ -0,0 +1,91 @@ +package se.scalablesolutions.akka.stm; + +import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.Before; + +import se.scalablesolutions.akka.stm.*; +import se.scalablesolutions.akka.stm.local.Atomic; + +import org.multiverse.api.ThreadLocalTransaction; +import org.multiverse.api.TransactionConfiguration; +import org.multiverse.api.exceptions.ReadonlyException; + +public class JavaStmTests { + + private Ref ref; + + private int getRefValue() { + return new Atomic() { + public Integer atomically() { + return ref.get(); + } + }.execute(); + } + + public int increment() { + return new Atomic() { + public Integer atomically() { + int inc = ref.get() + 1; + ref.set(inc); + return inc; + } + }.execute(); + } + + @Before public void initialise() { + ref = new Ref(0); + } + + @Test public void incrementRef() { + assertEquals(0, getRefValue()); + increment(); + increment(); + increment(); + assertEquals(3, getRefValue()); + } + + @Test public void failSetRef() { + assertEquals(0, getRefValue()); + try { + new Atomic() { + public Object atomically() { + ref.set(3); + throw new RuntimeException(); + } + }.execute(); + } catch(RuntimeException e) {} + assertEquals(0, getRefValue()); + } + + @Test public void configureTransaction() { + TransactionFactory txFactory = new TransactionFactoryBuilder() + .setFamilyName("example") + .setReadonly(true) + .build(); + + // get transaction config from multiverse + TransactionConfiguration config = new Atomic(txFactory) { + public TransactionConfiguration atomically() { + ref.get(); + return ThreadLocalTransaction.getThreadLocalTransaction().getConfiguration(); + } + }.execute(); + + assertEquals("example", config.getFamilyName()); + assertEquals(true, config.isReadonly()); + } + + @Test(expected=ReadonlyException.class) public void failReadonlyTransaction() { + TransactionFactory txFactory = new TransactionFactoryBuilder() + .setFamilyName("example") + .setReadonly(true) + .build(); + + new Atomic(txFactory) { + public Object atomically() { + return ref.set(3); + } + }.execute(); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/RefExample.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/RefExample.java new file mode 100644 index 0000000000..f590524fd7 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/RefExample.java @@ -0,0 +1,36 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.Ref; +import se.scalablesolutions.akka.stm.local.Atomic; + +public class RefExample { + public static void main(String[] args) { + System.out.println(); + System.out.println("Ref example"); + System.out.println(); + + final Ref ref = new Ref(0); + + Integer value1 = new Atomic() { + public Integer atomically() { + return ref.get(); + } + }.execute(); + + System.out.println("value 1: " + value1); + + new Atomic() { + public Object atomically() { + return ref.set(5); + } + }.execute(); + + Integer value2 = new Atomic() { + public Integer atomically() { + return ref.get(); + } + }.execute(); + + System.out.println("value 2: " + value2); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/StmExamples.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/StmExamples.java new file mode 100644 index 0000000000..a8526f2dd0 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/StmExamples.java @@ -0,0 +1,18 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.Ref; +import se.scalablesolutions.akka.stm.local.Atomic; + +public class StmExamples { + public static void main(String[] args) { + System.out.println(); + System.out.println("STM examples"); + System.out.println(); + + CounterExample.main(args); + RefExample.main(args); + TransactionFactoryExample.main(args); + TransactionalMapExample.main(args); + TransactionalVectorExample.main(args); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionFactoryExample.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionFactoryExample.java new file mode 100644 index 0000000000..00dd87b7c5 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionFactoryExample.java @@ -0,0 +1,30 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.*; +import se.scalablesolutions.akka.stm.local.Atomic; + +import org.multiverse.api.ThreadLocalTransaction; +import org.multiverse.api.TransactionConfiguration; + +public class TransactionFactoryExample { + public static void main(String[] args) { + System.out.println(); + System.out.println("TransactionFactory example"); + System.out.println(); + + TransactionFactory txFactory = new TransactionFactoryBuilder() + .setFamilyName("example") + .setReadonly(true) + .build(); + + new Atomic(txFactory) { + public Object atomically() { + // check config has been passed to multiverse + TransactionConfiguration config = ThreadLocalTransaction.getThreadLocalTransaction().getConfiguration(); + System.out.println("family name: " + config.getFamilyName()); + System.out.println("readonly: " + config.isReadonly()); + return null; + } + }.execute(); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalMapExample.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalMapExample.java new file mode 100644 index 0000000000..7c4940c7a5 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalMapExample.java @@ -0,0 +1,35 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.*; +import se.scalablesolutions.akka.stm.local.Atomic; + +public class TransactionalMapExample { + public static void main(String[] args) { + System.out.println(); + System.out.println("TransactionalMap example"); + System.out.println(); + + final TransactionalMap users = new TransactionalMap(); + + // fill users map (in a transaction) + new Atomic() { + public Object atomically() { + users.put("bill", new User("bill")); + users.put("mary", new User("mary")); + users.put("john", new User("john")); + return null; + } + }.execute(); + + System.out.println("users: " + users); + + // access users map (in a transaction) + User user = new Atomic() { + public User atomically() { + return users.get("bill").get(); + } + }.execute(); + + System.out.println("user: " + user); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalVectorExample.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalVectorExample.java new file mode 100644 index 0000000000..7274848beb --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalVectorExample.java @@ -0,0 +1,34 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.*; +import se.scalablesolutions.akka.stm.local.Atomic; + +public class TransactionalVectorExample { + public static void main(String[] args) { + System.out.println(); + System.out.println("TransactionalVector example"); + System.out.println(); + + final TransactionalVector
addresses = new TransactionalVector
(); + + // fill addresses vector (in a transaction) + new Atomic() { + public Object atomically() { + addresses.add(new Address("somewhere")); + addresses.add(new Address("somewhere else")); + return null; + } + }.execute(); + + System.out.println("addresses: " + addresses); + + // access addresses vector (in a transaction) + Address address = new Atomic
() { + public Address atomically() { + return addresses.get(0); + } + }.execute(); + + System.out.println("address: " + address); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/User.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/User.java new file mode 100644 index 0000000000..c9dc4b3723 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/User.java @@ -0,0 +1,13 @@ +package se.scalablesolutions.akka.stm; + +public class User { + private String name; + + public User(String name) { + this.name = name; + } + + @Override public String toString() { + return "User(" + name + ")"; + } +} diff --git a/akka-core/src/test/resources/META-INF/aop.xml b/akka-core/src/test/resources/META-INF/aop.xml index 2f8d5159a8..bdc167ca54 100644 --- a/akka-core/src/test/resources/META-INF/aop.xml +++ b/akka-core/src/test/resources/META-INF/aop.xml @@ -1,7 +1,7 @@ - + diff --git a/akka-core/src/test/resources/logback-test.xml b/akka-core/src/test/resources/logback-test.xml new file mode 100755 index 0000000000..78eae40ec4 --- /dev/null +++ b/akka-core/src/test/resources/logback-test.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + [%4p] [%d{ISO8601}] [%t] %c{1}: %m%n + + + + + + + diff --git a/akka-core/src/test/scala/ActiveObjectContextSpec.scala b/akka-core/src/test/scala/ActiveObjectContextSpec.scala deleted file mode 100644 index 5a54f0a505..0000000000 --- a/akka-core/src/test/scala/ActiveObjectContextSpec.scala +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.actor - -import org.scalatest.Spec -import org.scalatest.Assertions -import org.scalatest.matchers.ShouldMatchers -import org.scalatest.BeforeAndAfterAll -import org.scalatest.junit.JUnitRunner -import org.junit.runner.RunWith - -import se.scalablesolutions.akka.dispatch.DefaultCompletableFuture; - -@RunWith(classOf[JUnitRunner]) -class ActiveObjectContextSpec extends - Spec with - ShouldMatchers with - BeforeAndAfterAll { - - describe("ActiveObjectContext") { - it("context.sender should return the sender Active Object reference") { - val pojo = ActiveObject.newInstance(classOf[SimpleJavaPojo]) - val pojoCaller = ActiveObject.newInstance(classOf[SimpleJavaPojoCaller]) - pojoCaller.setPojo(pojo) - try { - pojoCaller.getSenderFromSimpleJavaPojo should equal (pojoCaller) - } catch { - case e => fail("no sender available") - } - } - - it("context.senderFuture should return the senderFuture Active Object reference") { - val pojo = ActiveObject.newInstance(classOf[SimpleJavaPojo]) - val pojoCaller = ActiveObject.newInstance(classOf[SimpleJavaPojoCaller]) - pojoCaller.setPojo(pojo) - try { - pojoCaller.getSenderFutureFromSimpleJavaPojo.getClass.getName should equal (classOf[DefaultCompletableFuture[_]].getName) - } catch { - case e => fail("no sender future available", e) - } - } - } -} diff --git a/akka-core/src/test/scala/ActiveObjectLifecycleSpec.scala b/akka-core/src/test/scala/ActiveObjectLifecycleSpec.scala deleted file mode 100644 index 2b8e4d502f..0000000000 --- a/akka-core/src/test/scala/ActiveObjectLifecycleSpec.scala +++ /dev/null @@ -1,167 +0,0 @@ -package se.scalablesolutions.akka.actor - -import org.junit.runner.RunWith -import org.scalatest.{BeforeAndAfterAll, Spec} -import org.scalatest.junit.JUnitRunner -import org.scalatest.matchers.ShouldMatchers - -import se.scalablesolutions.akka.actor.ActiveObject._ - -import se.scalablesolutions.akka.config.{OneForOneStrategy, ActiveObjectConfigurator} -import se.scalablesolutions.akka.config.JavaConfig._ - -/** - * @author Martin Krasser - */ -@RunWith(classOf[JUnitRunner]) -class ActiveObjectLifecycleSpec extends Spec with ShouldMatchers with BeforeAndAfterAll { - var conf1: ActiveObjectConfigurator = _ - var conf2: ActiveObjectConfigurator = _ - var conf3: ActiveObjectConfigurator = _ - var conf4: ActiveObjectConfigurator = _ - - override protected def beforeAll() = { - val strategy = new RestartStrategy(new AllForOne(), 3, 1000, Array(classOf[Exception])) - val comp1 = new Component(classOf[SamplePojoAnnotated], new LifeCycle(new Permanent()), 1000) - val comp2 = new Component(classOf[SamplePojoAnnotated], new LifeCycle(new Temporary()), 1000) - val comp3 = new Component(classOf[SamplePojo], new LifeCycle(new Permanent(), new RestartCallbacks("pre", "post")), 1000) - val comp4 = new Component(classOf[SamplePojo], new LifeCycle(new Temporary(), new ShutdownCallback("down")), 1000) - conf1 = new ActiveObjectConfigurator().configure(strategy, Array(comp1)).supervise - conf2 = new ActiveObjectConfigurator().configure(strategy, Array(comp2)).supervise - conf3 = new ActiveObjectConfigurator().configure(strategy, Array(comp3)).supervise - conf4 = new ActiveObjectConfigurator().configure(strategy, Array(comp4)).supervise - } - - override protected def afterAll() = { - conf1.stop - conf2.stop - conf3.stop - conf4.stop - } - - describe("ActiveObject lifecycle management") { - it("should restart supervised, annotated active object on failure") { - val obj = conf1.getInstance[SamplePojoAnnotated](classOf[SamplePojoAnnotated]) - val cdl = obj.newCountdownLatch(2) - assert(AspectInitRegistry.initFor(obj) ne null) - try { - obj.fail - fail("expected exception not thrown") - } catch { - case e: RuntimeException => { - cdl.await - assert(obj._pre) - assert(obj._post) - assert(!obj._down) - assert(AspectInitRegistry.initFor(obj) ne null) - } - } - } - - it("should shutdown supervised, annotated active object on failure") { - val obj = conf2.getInstance[SamplePojoAnnotated](classOf[SamplePojoAnnotated]) - val cdl = obj.newCountdownLatch(1) - assert(AspectInitRegistry.initFor(obj) ne null) - try { - obj.fail - fail("expected exception not thrown") - } catch { - case e: RuntimeException => { - cdl.await - assert(!obj._pre) - assert(!obj._post) - assert(obj._down) - assert(AspectInitRegistry.initFor(obj) eq null) - } - } - } - - it("should restart supervised, non-annotated active object on failure") { - val obj = conf3.getInstance[SamplePojo](classOf[SamplePojo]) - val cdl = obj.newCountdownLatch(2) - assert(AspectInitRegistry.initFor(obj) ne null) - try { - obj.fail - fail("expected exception not thrown") - } catch { - case e: RuntimeException => { - cdl.await - assert(obj._pre) - assert(obj._post) - assert(!obj._down) - assert(AspectInitRegistry.initFor(obj) ne null) - } - } - } - - it("should shutdown supervised, non-annotated active object on failure") { - val obj = conf4.getInstance[SamplePojo](classOf[SamplePojo]) - val cdl = obj.newCountdownLatch(1) - assert(AspectInitRegistry.initFor(obj) ne null) - try { - obj.fail - fail("expected exception not thrown") - } catch { - case e: RuntimeException => { - cdl.await - assert(!obj._pre) - assert(!obj._post) - assert(obj._down) - assert(AspectInitRegistry.initFor(obj) eq null) - } - } - } - - it("should shutdown non-supervised, annotated active object on ActiveObject.stop") { - val obj = ActiveObject.newInstance(classOf[SamplePojoAnnotated]) - assert(AspectInitRegistry.initFor(obj) ne null) - assert("hello akka" === obj.greet("akka")) - ActiveObject.stop(obj) - assert(AspectInitRegistry.initFor(obj) eq null) - assert(!obj._pre) - assert(!obj._post) - assert(obj._down) - try { - obj.greet("akka") - fail("access to stopped active object") - } catch { - case e: Exception => { /* test passed */ } - } - } - - it("should shutdown non-supervised, annotated active object on ActorRegistry.shutdownAll") { - val obj = ActiveObject.newInstance(classOf[SamplePojoAnnotated]) - assert(AspectInitRegistry.initFor(obj) ne null) - assert("hello akka" === obj.greet("akka")) - ActorRegistry.shutdownAll - assert(AspectInitRegistry.initFor(obj) eq null) - assert(!obj._pre) - assert(!obj._post) - assert(obj._down) - try { - obj.greet("akka") - fail("access to stopped active object") - } catch { - case e: Exception => { /* test passed */ } - } - } - - it("should shutdown non-supervised, non-initialized active object on ActiveObject.stop") { - val obj = ActiveObject.newInstance(classOf[SamplePojoAnnotated]) - ActiveObject.stop(obj) - assert(!obj._pre) - assert(!obj._post) - assert(obj._down) - } - - it("both preRestart and postRestart methods should be invoked when an actor is restarted") { - val pojo = ActiveObject.newInstance(classOf[SimpleJavaPojo]) - val supervisor = ActiveObject.newInstance(classOf[SimpleJavaPojo]) - link(supervisor,pojo, new OneForOneStrategy(3, 2000),Array(classOf[Throwable])) - pojo.throwException - Thread.sleep(500) - pojo.pre should be(true) - pojo.post should be(true) - } - } -} \ No newline at end of file diff --git a/akka-core/src/test/scala/Messages.scala b/akka-core/src/test/scala/Messages.scala index 436257e3b5..ad1fcf8885 100644 --- a/akka-core/src/test/scala/Messages.scala +++ b/akka-core/src/test/scala/Messages.scala @@ -7,9 +7,9 @@ package se.scalablesolutions.akka import se.scalablesolutions.akka.serialization.Serializable import sbinary._ import sbinary.Operations._ -import sbinary.DefaultProtocol._ sealed abstract class TestMessage + case object Ping extends TestMessage case object Pong extends TestMessage case object OneWay extends TestMessage diff --git a/akka-core/src/test/scala/RefSpec.scala b/akka-core/src/test/scala/RefSpec.scala deleted file mode 100644 index 805e0834ea..0000000000 --- a/akka-core/src/test/scala/RefSpec.scala +++ /dev/null @@ -1,156 +0,0 @@ -package se.scalablesolutions.akka.stm - -import org.scalatest.Spec -import org.scalatest.matchers.ShouldMatchers -import org.scalatest.junit.JUnitRunner -import org.junit.runner.RunWith - -import se.scalablesolutions.akka.actor.Actor._ - -@RunWith(classOf[JUnitRunner]) -class RefSpec extends Spec with ShouldMatchers { - - describe("A Ref") { - import local._ - - it("should optionally accept an initial value") { - val emptyRef = Ref[Int] - val empty = atomic { emptyRef.get } - - empty should be(None) - - val ref = Ref(3) - val value = atomic { ref.get.get } - - value should be(3) - } - - it("should keep the initial value, even if the first transaction is rolled back") { - val ref = Ref(3) - - try { - atomic(DefaultLocalTransactionFactory) { - ref.swap(5) - throw new Exception - } - } catch { - case e => {} - } - - val value = atomic { ref.get.get } - - value should be(3) - } - - it("should be settable using swap") { - val ref = Ref[Int] - - atomic { ref.swap(3) } - - val value = atomic { ref.get.get } - - value should be(3) - } - - it("should be changeable using alter") { - val ref = Ref(0) - - def increment = atomic { - ref alter (_ + 1) - } - - increment - increment - increment - - val value = atomic { ref.get.get } - - value should be(3) - } - - it("should not be changeable using alter if no value has been set") { - val ref = Ref[Int] - - def increment = atomic { - ref alter (_ + 1) - } - - evaluating { increment } should produce [RuntimeException] - } - - it("should be able to be mapped") { - val ref1 = Ref(1) - - val ref2 = atomic { - ref1 map (_ + 1) - } - - val value1 = atomic { ref1.get.get } - val value2 = atomic { ref2.get.get } - - value1 should be(1) - value2 should be(2) - } - - it("should be able to be used in a 'foreach' for comprehension") { - val ref = Ref(3) - - var result = 0 - - atomic { - for (value <- ref) { - result += value - } - } - - result should be(3) - } - - it("should be able to be used in a 'map' for comprehension") { - val ref1 = Ref(1) - - val ref2 = atomic { - for (value <- ref1) yield value + 2 - } - - val value2 = atomic { ref2.get.get } - - value2 should be(3) - } - - it("should be able to be used in a 'flatMap' for comprehension") { - val ref1 = Ref(1) - val ref2 = Ref(2) - - val ref3 = atomic { - for { - value1 <- ref1 - value2 <- ref2 - } yield value1 + value2 - } - - val value3 = atomic { ref3.get.get } - - value3 should be(3) - } - - it("should be able to be used in a 'filter' for comprehension") { - val ref1 = Ref(1) - - val refLess2 = atomic { - for (value <- ref1 if value < 2) yield value - } - - val optLess2 = atomic { refLess2.get } - - val refGreater2 = atomic { - for (value <- ref1 if value > 2) yield value - } - - val optGreater2 = atomic { refGreater2.get } - - optLess2 should be(Some(1)) - optGreater2 should be(None) - } - } -} diff --git a/akka-core/src/test/scala/SchedulerSpec.scala b/akka-core/src/test/scala/SchedulerSpec.scala deleted file mode 100644 index 0fe7c45ea5..0000000000 --- a/akka-core/src/test/scala/SchedulerSpec.scala +++ /dev/null @@ -1,38 +0,0 @@ -package se.scalablesolutions.akka.actor - -import org.scalatest.junit.JUnitSuite -import Actor._ -import java.util.concurrent.{CountDownLatch, TimeUnit} -import org.junit.{After, Test} - -class SchedulerSpec extends JUnitSuite { - - @Test def schedulerShouldScheduleMoreThanOnce = { - - case object Tick - val countDownLatch = new CountDownLatch(3) - val tickActor = actor { - case Tick => countDownLatch.countDown - } - // run every 50 millisec - Scheduler.schedule(tickActor, Tick, 0, 50, TimeUnit.MILLISECONDS) - - // after max 1 second it should be executed at least the 3 times already - assert(countDownLatch.await(1, TimeUnit.SECONDS)) - } - - @Test def schedulerShouldScheduleOnce = { - case object Tick - val countDownLatch = new CountDownLatch(2) - val tickActor = actor { - case Tick => countDownLatch.countDown - } - // run every 50 millisec - Scheduler.scheduleOnce(tickActor, Tick, 50, TimeUnit.MILLISECONDS) - - // after 1 second the wait should fail - assert(countDownLatch.await(1, TimeUnit.SECONDS) == false) - // should still be 1 left - assert(countDownLatch.getCount == 1) - } -} diff --git a/akka-core/src/test/scala/ActorFireForgetRequestReplySpec.scala b/akka-core/src/test/scala/actor/actor/ActorFireForgetRequestReplySpec.scala similarity index 75% rename from akka-core/src/test/scala/ActorFireForgetRequestReplySpec.scala rename to akka-core/src/test/scala/actor/actor/ActorFireForgetRequestReplySpec.scala index 9fbd39f90e..6d3b4d5124 100644 --- a/akka-core/src/test/scala/ActorFireForgetRequestReplySpec.scala +++ b/akka-core/src/test/scala/actor/actor/ActorFireForgetRequestReplySpec.scala @@ -1,6 +1,7 @@ package se.scalablesolutions.akka.actor import java.util.concurrent.{TimeUnit, CyclicBarrier, TimeoutException} +import se.scalablesolutions.akka.config.ScalaConfig._ import org.scalatest.junit.JUnitSuite import org.junit.Test @@ -19,6 +20,16 @@ object ActorFireForgetRequestReplySpec { } } + class CrashingTemporaryActor extends Actor { + self.lifeCycle = Some(LifeCycle(Temporary)) + + def receive = { + case "Die" => + state.finished.await + throw new Exception("Expected exception") + } + } + class SenderActor(replyActor: ActorRef) extends Actor { self.dispatcher = Dispatchers.newThreadBasedDispatcher(self) @@ -66,4 +77,16 @@ class ActorFireForgetRequestReplySpec extends JUnitSuite { catch { case e: TimeoutException => fail("Never got the message") } assert("ReplyImplicit" === state.s) } + + @Test + def shouldShutdownCrashedTemporaryActor = { + state.finished.reset + val actor = actorOf[CrashingTemporaryActor].start + assert(actor.isRunning) + actor ! "Die" + try { state.finished.await(1L, TimeUnit.SECONDS) } + catch { case e: TimeoutException => fail("Never got the message") } + Thread.sleep(100) + assert(actor.isShutdown) + } } diff --git a/akka-core/src/test/scala/AgentSpec.scala b/akka-core/src/test/scala/actor/actor/AgentSpec.scala similarity index 95% rename from akka-core/src/test/scala/AgentSpec.scala rename to akka-core/src/test/scala/actor/actor/AgentSpec.scala index de4326c646..71911c3ad8 100644 --- a/akka-core/src/test/scala/AgentSpec.scala +++ b/akka-core/src/test/scala/actor/actor/AgentSpec.scala @@ -1,10 +1,6 @@ package se.scalablesolutions.akka.actor import se.scalablesolutions.akka.actor.Actor.transactor -import se.scalablesolutions.akka.stm.Transaction.Global.atomic -import se.scalablesolutions.akka.util.Logging -import Actor._ - import org.scalatest.Suite import org.scalatest.junit.JUnitRunner import org.scalatest.matchers.MustMatchers diff --git a/akka-core/src/test/scala/Bench.scala b/akka-core/src/test/scala/actor/actor/Bench.scala similarity index 100% rename from akka-core/src/test/scala/Bench.scala rename to akka-core/src/test/scala/actor/actor/Bench.scala diff --git a/akka-core/src/test/scala/ForwardActorSpec.scala b/akka-core/src/test/scala/actor/actor/ForwardActorSpec.scala similarity index 100% rename from akka-core/src/test/scala/ForwardActorSpec.scala rename to akka-core/src/test/scala/actor/actor/ForwardActorSpec.scala diff --git a/akka-core/src/test/scala/ReceiveTimeoutSpec.scala b/akka-core/src/test/scala/actor/actor/ReceiveTimeoutSpec.scala similarity index 100% rename from akka-core/src/test/scala/ReceiveTimeoutSpec.scala rename to akka-core/src/test/scala/actor/actor/ReceiveTimeoutSpec.scala index 5c50337894..ff43467efc 100644 --- a/akka-core/src/test/scala/ReceiveTimeoutSpec.scala +++ b/akka-core/src/test/scala/actor/actor/ReceiveTimeoutSpec.scala @@ -3,9 +3,9 @@ package se.scalablesolutions.akka.actor import org.scalatest.junit.JUnitSuite import org.junit.Test -import Actor._ import java.util.concurrent.TimeUnit import org.multiverse.api.latches.StandardLatch +import Actor._ class ReceiveTimeoutSpec extends JUnitSuite { diff --git a/akka-core/src/test/scala/TransactorSpec.scala b/akka-core/src/test/scala/actor/actor/TransactorSpec.scala similarity index 97% rename from akka-core/src/test/scala/TransactorSpec.scala rename to akka-core/src/test/scala/actor/actor/TransactorSpec.scala index 1f48d0e740..dd23a76a88 100644 --- a/akka-core/src/test/scala/TransactorSpec.scala +++ b/akka-core/src/test/scala/actor/actor/TransactorSpec.scala @@ -35,9 +35,9 @@ class StatefulTransactor(expectedInvocationCount: Int) extends Transactor { val notifier = new CountDownLatch(expectedInvocationCount) - private lazy val mapState = TransactionalMap[String, String]() - private lazy val vectorState = TransactionalVector[String]() - private lazy val refState = Ref[String]() + private val mapState = TransactionalMap[String, String]() + private val vectorState = TransactionalVector[String]() + private val refState = Ref[String]() def receive = { case GetNotifier => @@ -49,7 +49,7 @@ class StatefulTransactor(expectedInvocationCount: Int) extends Transactor { self.reply(vectorState.length.asInstanceOf[AnyRef]) notifier.countDown case GetRefState => - self.reply(refState.get.get) + self.reply(refState.get) notifier.countDown case SetMapState(key, msg) => mapState.put(key, msg) diff --git a/akka-core/src/test/scala/actor/supervisor/RestartStrategySpec.scala b/akka-core/src/test/scala/actor/supervisor/RestartStrategySpec.scala new file mode 100644 index 0000000000..5023c756e1 --- /dev/null +++ b/akka-core/src/test/scala/actor/supervisor/RestartStrategySpec.scala @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.actor + +import org.scalatest.junit.JUnitSuite +import org.junit.Test + +import Actor._ +import se.scalablesolutions.akka.config.OneForOneStrategy +import java.util.concurrent.{TimeUnit, CountDownLatch} +import se.scalablesolutions.akka.config.ScalaConfig.{Permanent, LifeCycle} +import org.multiverse.api.latches.StandardLatch + +class RestartStrategySpec extends JUnitSuite { + + object Ping + object Crash + + @Test + def slaveShouldStayDeadAfterMaxRestarts = { + + val boss = actorOf(new Actor{ + self.trapExit = List(classOf[Throwable]) + self.faultHandler = Some(OneForOneStrategy(1, 1000)) + protected def receive = { case _ => () } + }).start + + val restartLatch = new StandardLatch + val secondRestartLatch = new StandardLatch + val countDownLatch = new CountDownLatch(2) + + + val slave = actorOf(new Actor{ + + protected def receive = { + case Ping => countDownLatch.countDown + case Crash => throw new Exception("Crashing...") + } + override def postRestart(reason: Throwable) = { + restartLatch.open + } + + override def shutdown = { + if (restartLatch.isOpen) { + secondRestartLatch.open + } + } + }) + boss.startLink(slave) + + slave ! Ping + slave ! Crash + slave ! Ping + + // test restart and post restart ping + assert(restartLatch.tryAwait(1, TimeUnit.SECONDS)) + assert(countDownLatch.await(1, TimeUnit.SECONDS)) + + // now crash again... should not restart + slave ! Crash + + assert(secondRestartLatch.tryAwait(1, TimeUnit.SECONDS)) + val exceptionLatch = new StandardLatch + try { + slave ! Ping // this should fail + } catch { + case e => exceptionLatch.open // expected here + } + assert(exceptionLatch.tryAwait(1, TimeUnit.SECONDS)) + } +} + diff --git a/akka-core/src/test/scala/SupervisorHierarchySpec.scala b/akka-core/src/test/scala/actor/supervisor/SupervisorHierarchySpec.scala similarity index 96% rename from akka-core/src/test/scala/SupervisorHierarchySpec.scala rename to akka-core/src/test/scala/actor/supervisor/SupervisorHierarchySpec.scala index 138313bafc..ffc9dbd860 100644 --- a/akka-core/src/test/scala/SupervisorHierarchySpec.scala +++ b/akka-core/src/test/scala/actor/supervisor/SupervisorHierarchySpec.scala @@ -14,7 +14,7 @@ import java.util.concurrent.{TimeUnit, CountDownLatch} object SupervisorHierarchySpec { class FireWorkerException(msg: String) extends Exception(msg) - + class CountDownActor(countDown: CountDownLatch) extends Actor { protected def receive = { case _ => () } override def postRestart(reason: Throwable) = countDown.countDown @@ -57,7 +57,7 @@ class SupervisorHierarchySpec extends JUnitSuite { assert(countDown.await(2, TimeUnit.SECONDS)) } - + @Test def supervisorShouldReceiveNotificationMessageWhenMaximumNumberOfRestartsWithinTimeRangeIsReached = { val countDown = new CountDownLatch(2) @@ -65,7 +65,7 @@ class SupervisorHierarchySpec extends JUnitSuite { val boss = actorOf(new Actor{ self.trapExit = List(classOf[Throwable]) self.faultHandler = Some(OneForOneStrategy(1, 5000)) - protected def receive = { + protected def receive = { case MaximumNumberOfRestartsWithinTimeRangeReached(_, _, _, _) => countDown.countDown } @@ -75,7 +75,7 @@ class SupervisorHierarchySpec extends JUnitSuite { crasher ! Exit(crasher, new FireWorkerException("Fire the worker!")) crasher ! Exit(crasher, new FireWorkerException("Fire the worker!")) - assert(countDown.await(2, TimeUnit.SECONDS)) + assert(countDown.await(2, TimeUnit.SECONDS)) } } diff --git a/akka-core/src/test/scala/SupervisorSpec.scala b/akka-core/src/test/scala/actor/supervisor/SupervisorSpec.scala similarity index 99% rename from akka-core/src/test/scala/SupervisorSpec.scala rename to akka-core/src/test/scala/actor/supervisor/SupervisorSpec.scala index fcbedd476b..01eb9cb006 100644 --- a/akka-core/src/test/scala/SupervisorSpec.scala +++ b/akka-core/src/test/scala/actor/supervisor/SupervisorSpec.scala @@ -6,7 +6,6 @@ package se.scalablesolutions.akka.actor import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.config.OneForOneStrategy -import se.scalablesolutions.akka.dispatch.Dispatchers import se.scalablesolutions.akka.{OneWay, Die, Ping} import Actor._ diff --git a/akka-core/src/test/scala/DeclarativelySupervisedNestedTransactionalActiveObjectSpec.scala b/akka-core/src/test/scala/actor/typed-actor/NestedTransactionalTypedActorSpec.scala similarity index 57% rename from akka-core/src/test/scala/DeclarativelySupervisedNestedTransactionalActiveObjectSpec.scala rename to akka-core/src/test/scala/actor/typed-actor/NestedTransactionalTypedActorSpec.scala index ea244bf966..7338e8df41 100644 --- a/akka-core/src/test/scala/DeclarativelySupervisedNestedTransactionalActiveObjectSpec.scala +++ b/akka-core/src/test/scala/actor/typed-actor/NestedTransactionalTypedActorSpec.scala @@ -1,4 +1,4 @@ -/** + /** * Copyright (C) 2009-2010 Scalable Solutions AB */ @@ -11,150 +11,92 @@ import org.scalatest.BeforeAndAfterAll import org.scalatest.junit.JUnitRunner import org.junit.runner.RunWith -import se.scalablesolutions.akka.config.Config -import se.scalablesolutions.akka.config._ -import se.scalablesolutions.akka.config.ActiveObjectConfigurator -import se.scalablesolutions.akka.config.JavaConfig._ import se.scalablesolutions.akka.actor._ @RunWith(classOf[JUnitRunner]) -class DeclarativelySupervisedNestedTransactionalActiveObjectSpec extends +class NestedTransactionalTypedActorSpec extends Spec with ShouldMatchers with BeforeAndAfterAll { - private val conf = new ActiveObjectConfigurator private var messageLog = "" - override def beforeAll { - Config.config - conf.configure( - new RestartStrategy(new AllForOne, 3, 5000, List(classOf[Exception]).toArray), - List( - new Component(classOf[TransactionalActiveObject], - new LifeCycle(new Permanent), - 10000), - new Component(classOf[NestedTransactionalActiveObject], - new LifeCycle(new Permanent), - 10000), - new Component(classOf[ActiveObjectFailer], - new LifeCycle(new Permanent), - 10000) - ).toArray).supervise - } - override def afterAll { - conf.stop + // ActorRegistry.shutdownAll } - describe("Declaratively nested supervised transactional in-memory Active Object") { + describe("Declaratively nested supervised transactional in-memory TypedActor") { it("map should not rollback state for stateful server in case of success") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) stateful.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") // set init state - Thread.sleep(100) - val nested = conf.getInstance(classOf[NestedTransactionalActiveObject]) - nested.init + val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl]) nested.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") // set init state - Thread.sleep(100) stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state", nested) // transactionrequired - Thread.sleep(100) stateful.getMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess") should equal("new state") nested.getMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess") should equal("new state") } it("map should rollback state for stateful server in case of failure") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state - Thread.sleep(100) - val nested = conf.getInstance(classOf[NestedTransactionalActiveObject]) - nested.init + val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl]) nested.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state - Thread.sleep(100) - val failer = conf.getInstance(classOf[ActiveObjectFailer]) + val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl]) try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer) - Thread.sleep(100) fail("should have thrown an exception") } catch { case e => {} } stateful.getMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure") should equal("init") - Thread.sleep(100) nested.getMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure") should equal("init") } it("vector should not rollback state for stateful server in case of success") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) stateful.setVectorState("init") // set init state - Thread.sleep(100) - val nested = conf.getInstance(classOf[NestedTransactionalActiveObject]) - nested.init - Thread.sleep(100) + val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl]) nested.setVectorState("init") // set init state - Thread.sleep(100) stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state", nested) // transactionrequired - Thread.sleep(100) stateful.getVectorState should equal("new state") - Thread.sleep(100) nested.getVectorState should equal("new state") } it("vector should rollback state for stateful server in case of failure") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) stateful.setVectorState("init") // set init state - Thread.sleep(100) - val nested = conf.getInstance(classOf[NestedTransactionalActiveObject]) - nested.init + val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl]) nested.setVectorState("init") // set init state - Thread.sleep(100) - val failer = conf.getInstance(classOf[ActiveObjectFailer]) + val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl]) try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer) - Thread.sleep(100) fail("should have thrown an exception") } catch { case e => {} } stateful.getVectorState should equal("init") - Thread.sleep(100) nested.getVectorState should equal("init") } it("ref should not rollback state for stateful server in case of success") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init - val nested = conf.getInstance(classOf[NestedTransactionalActiveObject]) - nested.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) + val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl]) stateful.setRefState("init") // set init state - Thread.sleep(100) nested.setRefState("init") // set init state - Thread.sleep(100) stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state", nested) - Thread.sleep(100) stateful.getRefState should equal("new state") - Thread.sleep(100) nested.getRefState should equal("new state") } it("ref should rollback state for stateful server in case of failure") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init - val nested = conf.getInstance(classOf[NestedTransactionalActiveObject]) - nested.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) + val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl]) stateful.setRefState("init") // set init state - Thread.sleep(100) nested.setRefState("init") // set init state - Thread.sleep(100) - val failer = conf.getInstance(classOf[ActiveObjectFailer]) + val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl]) try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer) - Thread.sleep(100) fail("should have thrown an exception") } catch { case e => {} } stateful.getRefState should equal("init") - Thread.sleep(100) nested.getRefState should equal("init") } } -} \ No newline at end of file +} diff --git a/akka-core/src/test/scala/actor/typed-actor/RestartNestedTransactionalTypedActorSpec.scala b/akka-core/src/test/scala/actor/typed-actor/RestartNestedTransactionalTypedActorSpec.scala new file mode 100644 index 0000000000..1769a5c47b --- /dev/null +++ b/akka-core/src/test/scala/actor/typed-actor/RestartNestedTransactionalTypedActorSpec.scala @@ -0,0 +1,118 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.actor + +import org.scalatest.Spec +import org.scalatest.Assertions +import org.scalatest.matchers.ShouldMatchers +import org.scalatest.BeforeAndAfterAll +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith + +import se.scalablesolutions.akka.config.Config +import se.scalablesolutions.akka.config._ +import se.scalablesolutions.akka.config.TypedActorConfigurator +import se.scalablesolutions.akka.config.JavaConfig._ +import se.scalablesolutions.akka.actor._ + +@RunWith(classOf[JUnitRunner]) +class RestartNestedTransactionalTypedActorSpec extends + Spec with + ShouldMatchers with + BeforeAndAfterAll { + + private val conf = new TypedActorConfigurator + private var messageLog = "" + + override def beforeAll { + /* + Config.config + conf.configure( + new RestartStrategy(new AllForOne, 3, 5000, List(classOf[Exception]).toArray), + List( + new Component(classOf[TransactionalTypedActor], + new LifeCycle(new Permanent), + 10000), + new Component(classOf[NestedTransactionalTypedActor], + new LifeCycle(new Permanent), + 10000), + new Component(classOf[TypedActorFailer], + new LifeCycle(new Permanent), + 10000) + ).toArray).supervise + */ + } + + override def afterAll { + /* + conf.stop + ActorRegistry.shutdownAll + */ + } + + describe("Restart nested supervised transactional Typed Actor") { +/* + it("map should rollback state for stateful server in case of failure") { + val stateful = conf.getInstance(classOf[TransactionalTypedActor]) + stateful.init + stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state + + val nested = conf.getInstance(classOf[NestedTransactionalTypedActor]) + nested.init + nested.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state + + val failer = conf.getInstance(classOf[TypedActorFailer]) + try { + stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer) + + fail("should have thrown an exception") + } catch { case e => {} } + stateful.getMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure") should equal("init") + + nested.getMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure") should equal("init") + } + + it("vector should rollback state for stateful server in case of failure") { + val stateful = conf.getInstance(classOf[TransactionalTypedActor]) + stateful.init + stateful.setVectorState("init") // set init state + + val nested = conf.getInstance(classOf[NestedTransactionalTypedActor]) + nested.init + nested.setVectorState("init") // set init state + + val failer = conf.getInstance(classOf[TypedActorFailer]) + try { + stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer) + + fail("should have thrown an exception") + } catch { case e => {} } + stateful.getVectorState should equal("init") + + nested.getVectorState should equal("init") + } + + it("ref should rollback state for stateful server in case of failure") { + val stateful = conf.getInstance(classOf[TransactionalTypedActor]) + stateful.init + val nested = conf.getInstance(classOf[NestedTransactionalTypedActor]) + nested.init + stateful.setRefState("init") // set init state + + nested.setRefState("init") // set init state + + val failer = conf.getInstance(classOf[TypedActorFailer]) + try { + stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer) + + fail("should have thrown an exception") + } catch { case e => {} } + stateful.getRefState should equal("init") + + nested.getRefState should equal("init") + } + */ + } +} diff --git a/akka-core/src/test/scala/actor/typed-actor/RestartTransactionalTypedActorSpec.scala b/akka-core/src/test/scala/actor/typed-actor/RestartTransactionalTypedActorSpec.scala new file mode 100644 index 0000000000..56b1e6ec5b --- /dev/null +++ b/akka-core/src/test/scala/actor/typed-actor/RestartTransactionalTypedActorSpec.scala @@ -0,0 +1,92 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.actor + +import org.scalatest.Spec +import org.scalatest.Assertions +import org.scalatest.matchers.ShouldMatchers +import org.scalatest.BeforeAndAfterAll +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith + +import se.scalablesolutions.akka.config.Config +import se.scalablesolutions.akka.config._ +import se.scalablesolutions.akka.config.TypedActorConfigurator +import se.scalablesolutions.akka.config.JavaConfig._ +import se.scalablesolutions.akka.actor._ + +@RunWith(classOf[JUnitRunner]) +class RestartTransactionalTypedActorSpec extends + Spec with + ShouldMatchers with + BeforeAndAfterAll { + + private val conf = new TypedActorConfigurator + private var messageLog = "" + + def before { + Config.config + conf.configure( + new RestartStrategy(new AllForOne, 3, 5000, List(classOf[Exception]).toArray), + List( + new Component( + classOf[TransactionalTypedActor], + new LifeCycle(new Temporary), + 10000), + new Component( + classOf[TypedActorFailer], + new LifeCycle(new Temporary), + 10000) + ).toArray).supervise + } + + def after { + conf.stop + ActorRegistry.shutdownAll + } + + describe("Restart supervised transactional Typed Actor ") { +/* + it("map should rollback state for stateful server in case of failure") { + before + val stateful = conf.getInstance(classOf[TransactionalTypedActor]) + stateful.init + stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") + val failer = conf.getInstance(classOf[TypedActorFailer]) + try { + stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) + fail("should have thrown an exception") + } catch { case e => {} } + stateful.getMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure") should equal("init") + after + } + + it("vector should rollback state for stateful server in case of failure") { + before + val stateful = conf.getInstance(classOf[TransactionalTypedActor]) + stateful.init + stateful.setVectorState("init") // set init state + val failer = conf.getInstance(classOf[TypedActorFailer]) + try { + stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) + fail("should have thrown an exception") + } catch { case e => {} } + stateful.getVectorState should equal("init") + after + } + + it("ref should rollback state for stateful server in case of failure") { + val stateful = conf.getInstance(classOf[TransactionalTypedActor]) + stateful.init + stateful.setRefState("init") // set init state + val failer = conf.getInstance(classOf[TypedActorFailer]) + try { + stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) + fail("should have thrown an exception") + } catch { case e => {} } + stateful.getRefState should equal("init") + } +*/ } +} diff --git a/akka-core/src/test/scala/DeclarativelySupervisedTransactionalActiveObjectSpec.scala b/akka-core/src/test/scala/actor/typed-actor/TransactionalTypedActorSpec.scala similarity index 63% rename from akka-core/src/test/scala/DeclarativelySupervisedTransactionalActiveObjectSpec.scala rename to akka-core/src/test/scala/actor/typed-actor/TransactionalTypedActorSpec.scala index 95d7b8e5df..b55f52c875 100644 --- a/akka-core/src/test/scala/DeclarativelySupervisedTransactionalActiveObjectSpec.scala +++ b/akka-core/src/test/scala/actor/typed-actor/TransactionalTypedActorSpec.scala @@ -11,56 +11,32 @@ import org.scalatest.BeforeAndAfterAll import org.scalatest.junit.JUnitRunner import org.junit.runner.RunWith -import se.scalablesolutions.akka.config.Config -import se.scalablesolutions.akka.config._ -import se.scalablesolutions.akka.config.ActiveObjectConfigurator -import se.scalablesolutions.akka.config.JavaConfig._ import se.scalablesolutions.akka.actor._ @RunWith(classOf[JUnitRunner]) -class DeclarativelySupervisedTransactionalActiveObjectSpec extends +class TransactionalTypedActorSpec extends Spec with ShouldMatchers with BeforeAndAfterAll { - private val conf = new ActiveObjectConfigurator private var messageLog = "" - override def beforeAll { - Config.config - conf.configure( - new RestartStrategy(new AllForOne, 3, 5000, List(classOf[Exception]).toArray), - List( - new Component( - classOf[TransactionalActiveObject], - new LifeCycle(new Permanent), - 10000), - new Component( - classOf[ActiveObjectFailer], - new LifeCycle(new Permanent), - 10000) - ).toArray).supervise - } - override def afterAll { - conf.stop +// ActorRegistry.shutdownAll } - - describe("Declaratively supervised transactional in-memory Active Object ") { + describe("Declaratively supervised transactional in-memory Typed Actor ") { it("map should not rollback state for stateful server in case of success") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) stateful.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") stateful.getMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess") should equal("new state") } it("map should rollback state for stateful server in case of failure") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") - val failer = conf.getInstance(classOf[ActiveObjectFailer]) + val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl]) try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) fail("should have thrown an exception") @@ -69,18 +45,16 @@ class DeclarativelySupervisedTransactionalActiveObjectSpec extends } it("vector should not rollback state for stateful server in case of success") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) stateful.setVectorState("init") // set init state stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") stateful.getVectorState should equal("new state") } it("vector should rollback state for stateful server in case of failure") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) stateful.setVectorState("init") // set init state - val failer = conf.getInstance(classOf[ActiveObjectFailer]) + val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl]) try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) fail("should have thrown an exception") @@ -89,18 +63,16 @@ class DeclarativelySupervisedTransactionalActiveObjectSpec extends } it("ref should not rollback state for stateful server in case of success") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) stateful.setRefState("init") // set init state stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") stateful.getRefState should equal("new state") } it("ref should rollback state for stateful server in case of failure") { - val stateful = conf.getInstance(classOf[TransactionalActiveObject]) - stateful.init + val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl]) stateful.setRefState("init") // set init state - val failer = conf.getInstance(classOf[ActiveObjectFailer]) + val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl]) try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) fail("should have thrown an exception") diff --git a/akka-core/src/test/scala/actor/typed-actor/TypedActorContextSpec.scala b/akka-core/src/test/scala/actor/typed-actor/TypedActorContextSpec.scala new file mode 100644 index 0000000000..adc0879c84 --- /dev/null +++ b/akka-core/src/test/scala/actor/typed-actor/TypedActorContextSpec.scala @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.actor + +import org.scalatest.Spec +import org.scalatest.Assertions +import org.scalatest.matchers.ShouldMatchers +import org.scalatest.BeforeAndAfterAll +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith + +import se.scalablesolutions.akka.dispatch.DefaultCompletableFuture; + +@RunWith(classOf[JUnitRunner]) +class TypedActorContextSpec extends + Spec with + ShouldMatchers with + BeforeAndAfterAll { + + describe("TypedActorContext") { + it("context.sender should return the sender TypedActor reference") { + val pojo = TypedActor.newInstance(classOf[SimpleJavaPojo], classOf[SimpleJavaPojoImpl]) + val pojoCaller = TypedActor.newInstance(classOf[SimpleJavaPojoCaller], classOf[SimpleJavaPojoCallerImpl]) + pojoCaller.setPojo(pojo) + pojoCaller.getSenderFromSimpleJavaPojo.isInstanceOf[Option[_]] should equal (true) + pojoCaller.getSenderFromSimpleJavaPojo.asInstanceOf[Option[_]].isDefined should equal (true) + pojoCaller.getSenderFromSimpleJavaPojo.asInstanceOf[Option[_]].get should equal (pojoCaller) + } + it("context.senderFuture should return the senderFuture TypedActor reference") { + val pojo = TypedActor.newInstance(classOf[SimpleJavaPojo], classOf[SimpleJavaPojoImpl]) + val pojoCaller = TypedActor.newInstance(classOf[SimpleJavaPojoCaller], classOf[SimpleJavaPojoCallerImpl]) + pojoCaller.setPojo(pojo) + pojoCaller.getSenderFutureFromSimpleJavaPojo.getClass.getName should equal (classOf[DefaultCompletableFuture[_]].getName) + } + } +} diff --git a/akka-core/src/test/scala/ActiveObjectGuiceConfiguratorSpec.scala b/akka-core/src/test/scala/actor/typed-actor/TypedActorGuiceConfiguratorSpec.scala similarity index 89% rename from akka-core/src/test/scala/ActiveObjectGuiceConfiguratorSpec.scala rename to akka-core/src/test/scala/actor/typed-actor/TypedActorGuiceConfiguratorSpec.scala index 3cb871d0d8..d076ec52cf 100644 --- a/akka-core/src/test/scala/ActiveObjectGuiceConfiguratorSpec.scala +++ b/akka-core/src/test/scala/actor/typed-actor/TypedActorGuiceConfiguratorSpec.scala @@ -15,18 +15,18 @@ import org.scalatest.junit.JUnitRunner import org.junit.runner.RunWith import se.scalablesolutions.akka.config.Config -import se.scalablesolutions.akka.config.ActiveObjectConfigurator +import se.scalablesolutions.akka.config.TypedActorConfigurator import se.scalablesolutions.akka.config.JavaConfig._ import se.scalablesolutions.akka.dispatch._ import se.scalablesolutions.akka.dispatch.FutureTimeoutException @RunWith(classOf[JUnitRunner]) -class ActiveObjectGuiceConfiguratorSpec extends +class TypedActorGuiceConfiguratorSpec extends Spec with ShouldMatchers with BeforeAndAfterAll { - private val conf = new ActiveObjectConfigurator + private val conf = new TypedActorConfigurator private var messageLog = "" override def beforeAll { @@ -40,6 +40,7 @@ class ActiveObjectGuiceConfiguratorSpec extends List( new Component( classOf[Foo], + classOf[FooImpl], new LifeCycle(new Permanent), 1000, dispatcher), @@ -55,9 +56,9 @@ class ActiveObjectGuiceConfiguratorSpec extends override def afterAll = conf.stop - describe("ActiveObjectGuiceConfigurator") { + describe("TypedActorGuiceConfigurator") { /* - it("should inject active object using guice") { + it("should inject typed actor using guice") { messageLog = "" val foo = conf.getInstance(classOf[Foo]) val bar = conf.getInstance(classOf[Bar]) @@ -81,7 +82,7 @@ class ActiveObjectGuiceConfiguratorSpec extends } } - it("should be able to invoke active object") { + it("should be able to invoke typed actor") { messageLog = "" val foo = conf.getInstance(classOf[Foo]) messageLog += foo.foo("foo ") @@ -91,7 +92,7 @@ class ActiveObjectGuiceConfiguratorSpec extends messageLog should equal("foo return_foo before_bar ") } - it("should be able to invoke active object's invocation") { + it("should be able to invoke typed actor's invocation") { messageLog = "" val foo = conf.getInstance(classOf[Foo]) val bar = conf.getInstance(classOf[Bar]) diff --git a/akka-core/src/test/scala/actor/typed-actor/TypedActorLifecycleSpec.scala b/akka-core/src/test/scala/actor/typed-actor/TypedActorLifecycleSpec.scala new file mode 100644 index 0000000000..10fc40493b --- /dev/null +++ b/akka-core/src/test/scala/actor/typed-actor/TypedActorLifecycleSpec.scala @@ -0,0 +1,169 @@ +package se.scalablesolutions.akka.actor + +import org.junit.runner.RunWith +import org.scalatest.{BeforeAndAfterAll, Spec} +import org.scalatest.junit.JUnitRunner +import org.scalatest.matchers.ShouldMatchers + +import se.scalablesolutions.akka.actor.TypedActor._ + +import se.scalablesolutions.akka.config.{OneForOneStrategy, TypedActorConfigurator} +import se.scalablesolutions.akka.config.JavaConfig._ + +import java.util.concurrent.CountDownLatch + +/** + * @author Martin Krasser + */ +@RunWith(classOf[JUnitRunner]) +class TypedActorLifecycleSpec extends Spec with ShouldMatchers with BeforeAndAfterAll { + var conf1: TypedActorConfigurator = _ + var conf2: TypedActorConfigurator = _ + + override protected def beforeAll() = { + val strategy = new RestartStrategy(new AllForOne(), 3, 1000, Array(classOf[Exception])) + val comp3 = new Component(classOf[SamplePojo], classOf[SamplePojoImpl], new LifeCycle(new Permanent()), 1000) + val comp4 = new Component(classOf[SamplePojo], classOf[SamplePojoImpl], new LifeCycle(new Temporary()), 1000) + conf1 = new TypedActorConfigurator().configure(strategy, Array(comp3)).supervise + conf2 = new TypedActorConfigurator().configure(strategy, Array(comp4)).supervise + } + + override protected def afterAll() = { + conf1.stop + conf2.stop + } + + describe("TypedActor lifecycle management") { + it("should restart supervised, non-annotated typed actor on failure") { + SamplePojoImpl.reset + val obj = conf1.getInstance[SamplePojo](classOf[SamplePojo]) + val cdl = new CountDownLatch(2) + SamplePojoImpl.latch = cdl + assert(AspectInitRegistry.initFor(obj) ne null) + try { + obj.fail + fail("expected exception not thrown") + } catch { + case e: RuntimeException => { + cdl.await + assert(SamplePojoImpl._pre) + assert(SamplePojoImpl._post) + assert(!SamplePojoImpl._down) +// assert(AspectInitRegistry.initFor(obj) ne null) + } + } + } + + it("should shutdown supervised, non-annotated typed actor on failure") { + SamplePojoImpl.reset + val obj = conf2.getInstance[SamplePojo](classOf[SamplePojo]) + val cdl = new CountDownLatch(1) + SamplePojoImpl.latch = cdl + assert(AspectInitRegistry.initFor(obj) ne null) + try { + obj.fail + fail("expected exception not thrown") + } catch { + case e: RuntimeException => { + cdl.await + assert(!SamplePojoImpl._pre) + assert(!SamplePojoImpl._post) + assert(SamplePojoImpl._down) + // assert(AspectInitRegistry.initFor(obj) eq null) + } + } + } + + it("should shutdown non-supervised, non-initialized typed actor on TypedActor.stop") { + SamplePojoImpl.reset + val obj = TypedActor.newInstance(classOf[SamplePojo], classOf[SamplePojoImpl]) + TypedActor.stop(obj) + assert(!SamplePojoImpl._pre) + assert(!SamplePojoImpl._post) + assert(SamplePojoImpl._down) + } + + it("both preRestart and postRestart methods should be invoked when an actor is restarted") { + SamplePojoImpl.reset + val pojo = TypedActor.newInstance(classOf[SimpleJavaPojo], classOf[SimpleJavaPojoImpl]) + val supervisor = TypedActor.newInstance(classOf[SimpleJavaPojo], classOf[SimpleJavaPojoImpl]) + link(supervisor, pojo, new OneForOneStrategy(3, 2000), Array(classOf[Throwable])) + pojo.throwException + Thread.sleep(500) + SimpleJavaPojoImpl._pre should be(true) + SimpleJavaPojoImpl._post should be(true) + } + + /* + it("should shutdown non-supervised, annotated typed actor on TypedActor.stop") { + val obj = TypedActor.newInstance(classOf[SamplePojoAnnotated]) + assert(AspectInitRegistry.initFor(obj) ne null) + assert("hello akka" === obj.greet("akka")) + TypedActor.stop(obj) + assert(AspectInitRegistry.initFor(obj) eq null) + assert(!obj.pre) + assert(!obj.post) + assert(obj.down) + try { + obj.greet("akka") + fail("access to stopped typed actor") + } catch { + case e: Exception => {} + } + } + + it("should shutdown non-supervised, annotated typed actor on ActorRegistry.shutdownAll") { + val obj = TypedActor.newInstance(classOf[SamplePojoAnnotated]) + assert(AspectInitRegistry.initFor(obj) ne null) + assert("hello akka" === obj.greet("akka")) + ActorRegistry.shutdownAll + assert(AspectInitRegistry.initFor(obj) eq null) + assert(!obj.pre) + assert(!obj.post) + assert(obj.down) + try { + obj.greet("akka") + fail("access to stopped typed actor") + } catch { + case e: Exception => { } + } + } + + it("should restart supervised, annotated typed actor on failure") { + val obj = conf1.getInstance[SamplePojoAnnotated](classOf[SamplePojoAnnotated]) + val cdl = obj.newCountdownLatch(2) + assert(AspectInitRegistry.initFor(obj) ne null) + try { + obj.fail + fail("expected exception not thrown") + } catch { + case e: RuntimeException => { + cdl.await + assert(obj.pre) + assert(obj.post) + assert(!obj.down) + assert(AspectInitRegistry.initFor(obj) ne null) + } + } + } + + it("should shutdown supervised, annotated typed actor on failure") { + val obj = conf2.getInstance[SamplePojoAnnotated](classOf[SamplePojoAnnotated]) + val cdl = obj.newCountdownLatch(1) + assert(AspectInitRegistry.initFor(obj) ne null) + try { + obj.fail + fail("expected exception not thrown") + } catch { + case e: RuntimeException => { + cdl.await + assert(!obj.pre) + assert(!obj.post) + assert(obj.down) + assert(AspectInitRegistry.initFor(obj) eq null) + } + } + } + */ + } +} diff --git a/akka-core/src/test/scala/actor/typed-actor/TypedActorSpec.scala b/akka-core/src/test/scala/actor/typed-actor/TypedActorSpec.scala new file mode 100644 index 0000000000..7de0a8f5df --- /dev/null +++ b/akka-core/src/test/scala/actor/typed-actor/TypedActorSpec.scala @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.actor + +import org.scalatest.Spec +import org.scalatest.Assertions +import org.scalatest.matchers.ShouldMatchers +import org.scalatest.BeforeAndAfterAll +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith + +import se.scalablesolutions.akka.dispatch.DefaultCompletableFuture; + +@RunWith(classOf[JUnitRunner]) +class TypedActorSpec extends + Spec with + ShouldMatchers with + BeforeAndAfterAll { + + describe("TypedActor") { + it("should resolve Future return from method defined to return a Future") { + val pojo = TypedActor.newInstance(classOf[SimpleJavaPojo], classOf[SimpleJavaPojoImpl]) + val future = pojo.square(10) + future.await + future.result.isDefined should equal (true) + future.result.get should equal (100) + } + } +} diff --git a/akka-core/src/test/scala/ActorObjectUtilFunctionsSpec.scala b/akka-core/src/test/scala/actor/typed-actor/TypedActorUtilFunctionsSpec.scala similarity index 100% rename from akka-core/src/test/scala/ActorObjectUtilFunctionsSpec.scala rename to akka-core/src/test/scala/actor/typed-actor/TypedActorUtilFunctionsSpec.scala diff --git a/akka-core/src/test/scala/dispatch/DispatchersSpec.scala b/akka-core/src/test/scala/dispatch/DispatchersSpec.scala new file mode 100644 index 0000000000..bb548b9251 --- /dev/null +++ b/akka-core/src/test/scala/dispatch/DispatchersSpec.scala @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ +package se.scalablesolutions.akka.actor.dispatch + +import java.util.concurrent.{CountDownLatch, TimeUnit} +import org.scalatest.junit.JUnitSuite +import org.junit.Test + +import net.lag.configgy.Config +import scala.reflect.{Manifest} +import se.scalablesolutions.akka.dispatch._ + +object DispatchersSpec { + import Dispatchers._ + // + val tipe = "type" + val keepalivems = "keep-alive-ms" + val corepoolsizefactor = "core-pool-size-factor" + val maxpoolsizefactor = "max-pool-size-factor" + val executorbounds = "executor-bounds" + val allowcoretimeout = "allow-core-timeout" + val rejectionpolicy = "rejection-policy" // abort, caller-runs, discard-oldest, discard + val throughput = "throughput" // Throughput for ExecutorBasedEventDrivenDispatcher + val aggregate = "aggregate" // Aggregate on/off for HawtDispatchers + + def instance(dispatcher: MessageDispatcher): (MessageDispatcher) => Boolean = _ == dispatcher + def ofType[T <: MessageDispatcher : Manifest]: (MessageDispatcher) => Boolean = _.getClass == manifest[T].erasure + + def typesAndValidators: Map[String,(MessageDispatcher) => Boolean] = Map( + "ReactorBasedSingleThreadEventDriven" -> ofType[ReactorBasedSingleThreadEventDrivenDispatcher], + "ExecutorBasedEventDrivenWorkStealing" -> ofType[ExecutorBasedEventDrivenWorkStealingDispatcher], + "ExecutorBasedEventDriven" -> ofType[ExecutorBasedEventDrivenDispatcher], + "ReactorBasedThreadPoolEventDriven" -> ofType[ReactorBasedThreadPoolEventDrivenDispatcher], + "Hawt" -> ofType[HawtDispatcher], + "GlobalReactorBasedSingleThreadEventDriven" -> instance(globalReactorBasedSingleThreadEventDrivenDispatcher), + "GlobalReactorBasedThreadPoolEventDriven" -> instance(globalReactorBasedThreadPoolEventDrivenDispatcher), + "GlobalExecutorBasedEventDriven" -> instance(globalExecutorBasedEventDrivenDispatcher), + "GlobalHawt" -> instance(globalHawtDispatcher) + ) + + def validTypes = typesAndValidators.keys.toList + + lazy val allDispatchers: Map[String,Option[MessageDispatcher]] = { + validTypes.map(t => (t,from(Config.fromMap(Map(tipe -> t))))).toMap + } +} + +class DispatchersSpec extends JUnitSuite { + + import Dispatchers._ + import DispatchersSpec._ + + @Test def shouldYieldNoneIfTypeIsMissing { + assert(from(Config.fromMap(Map())) === None) + } + + @Test(expected = classOf[IllegalArgumentException]) + def shouldThrowIllegalArgumentExceptionIfTypeDoesntExist { + from(Config.fromMap(Map(tipe -> "typedoesntexist"))) + } + + @Test def shouldGetTheCorrectTypesOfDispatchers { + //It can create/obtain all defined types + assert(allDispatchers.values.forall(_.isDefined)) + //All created/obtained dispatchers are of the expeced type/instance + assert(typesAndValidators.forall( tuple => tuple._2(allDispatchers(tuple._1).get) )) + } + + @Test def defaultingToDefaultWhileLoadingTheDefaultShouldWork { + assert(from(Config.fromMap(Map())).getOrElse(defaultGlobalDispatcher) == defaultGlobalDispatcher) + } + +} diff --git a/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorSpec.scala b/akka-core/src/test/scala/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala similarity index 95% rename from akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorSpec.scala rename to akka-core/src/test/scala/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala index b25a02db5e..9cdf43682e 100644 --- a/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorSpec.scala +++ b/akka-core/src/test/scala/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala @@ -1,9 +1,10 @@ -package se.scalablesolutions.akka.actor +package se.scalablesolutions.akka.actor.dispatch import java.util.concurrent.{CountDownLatch, TimeUnit} import org.scalatest.junit.JUnitSuite import org.junit.Test import se.scalablesolutions.akka.dispatch.Dispatchers +import se.scalablesolutions.akka.actor.Actor import Actor._ object ExecutorBasedEventDrivenDispatcherActorSpec { diff --git a/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorsSpec.scala b/akka-core/src/test/scala/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala similarity index 93% rename from akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorsSpec.scala rename to akka-core/src/test/scala/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala index b2c9cf4422..fc8f1aa37f 100644 --- a/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorsSpec.scala +++ b/akka-core/src/test/scala/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala @@ -1,10 +1,10 @@ -package se.scalablesolutions.akka.actor +package se.scalablesolutions.akka.actor.dispatch import org.scalatest.junit.JUnitSuite import org.junit.Test -import se.scalablesolutions.akka.dispatch.Dispatchers import org.scalatest.matchers.MustMatchers import java.util.concurrent.CountDownLatch +import se.scalablesolutions.akka.actor.Actor import Actor._ /** diff --git a/akka-core/src/test/scala/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala b/akka-core/src/test/scala/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala similarity index 95% rename from akka-core/src/test/scala/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala rename to akka-core/src/test/scala/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala index 8ad3e3f212..cde57a0544 100644 --- a/akka-core/src/test/scala/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala +++ b/akka-core/src/test/scala/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala @@ -1,4 +1,4 @@ -package se.scalablesolutions.akka.actor +package se.scalablesolutions.akka.actor.dispatch import org.scalatest.matchers.MustMatchers import org.scalatest.junit.JUnitSuite @@ -6,9 +6,10 @@ import org.scalatest.junit.JUnitSuite import org.junit.Test import se.scalablesolutions.akka.dispatch.Dispatchers -import Actor._ import java.util.concurrent.{TimeUnit, CountDownLatch} +import se.scalablesolutions.akka.actor.{IllegalActorStateException, Actor} +import Actor._ object ExecutorBasedEventDrivenWorkStealingDispatcherSpec { val delayableActorDispatcher = Dispatchers.newExecutorBasedEventDrivenWorkStealingDispatcher("pooled-dispatcher") diff --git a/akka-core/src/test/scala/FutureSpec.scala b/akka-core/src/test/scala/dispatch/FutureSpec.scala similarity index 98% rename from akka-core/src/test/scala/FutureSpec.scala rename to akka-core/src/test/scala/dispatch/FutureSpec.scala index 39fc563007..f740763fdf 100644 --- a/akka-core/src/test/scala/FutureSpec.scala +++ b/akka-core/src/test/scala/dispatch/FutureSpec.scala @@ -36,7 +36,7 @@ class FutureSpec extends JUnitSuite { val future = actor !!! "Failure" future.await assert(future.exception.isDefined) - assert("Expected exception; to test fault-tolerance" === future.exception.get._2.getMessage) + assert("Expected exception; to test fault-tolerance" === future.exception.get.getMessage) actor.stop } diff --git a/akka-core/src/test/scala/dispatch/HawtDispatcherActorSpec.scala b/akka-core/src/test/scala/dispatch/HawtDispatcherActorSpec.scala new file mode 100644 index 0000000000..dcc8f7eafb --- /dev/null +++ b/akka-core/src/test/scala/dispatch/HawtDispatcherActorSpec.scala @@ -0,0 +1,69 @@ +package se.scalablesolutions.akka.actor.dispatch + +import java.util.concurrent.{CountDownLatch, TimeUnit} +import org.scalatest.junit.JUnitSuite +import org.junit.Test +import se.scalablesolutions.akka.actor.Actor +import Actor._ +import se.scalablesolutions.akka.dispatch.{HawtDispatcher, Dispatchers} + +object HawtDispatcherActorSpec { + class TestActor extends Actor { + self.dispatcher = new HawtDispatcher() + def receive = { + case "Hello" => + self.reply("World") + case "Failure" => + throw new RuntimeException("Expected exception; to test fault-tolerance") + } + } + + object OneWayTestActor { + val oneWay = new CountDownLatch(1) + } + class OneWayTestActor extends Actor { + self.dispatcher = new HawtDispatcher() + def receive = { + case "OneWay" => OneWayTestActor.oneWay.countDown + } + } +} + +class HawtDispatcherActorSpec extends JUnitSuite { + import HawtDispatcherActorSpec._ + + private val unit = TimeUnit.MILLISECONDS + + @Test def shouldSendOneWay = { + val actor = actorOf[OneWayTestActor].start + val result = actor ! "OneWay" + assert(OneWayTestActor.oneWay.await(1, TimeUnit.SECONDS)) + actor.stop + } + + @Test def shouldSendReplySync = { + val actor = actorOf[TestActor].start + val result = (actor !! ("Hello", 10000)).as[String] + assert("World" === result.get) + actor.stop + } + + @Test def shouldSendReplyAsync = { + val actor = actorOf[TestActor].start + val result = actor !! "Hello" + assert("World" === result.get.asInstanceOf[String]) + actor.stop + } + + @Test def shouldSendReceiveException = { + val actor = actorOf[TestActor].start + try { + actor !! "Failure" + fail("Should have thrown an exception") + } catch { + case e => + assert("Expected exception; to test fault-tolerance" === e.getMessage()) + } + actor.stop + } +} diff --git a/akka-core/src/test/scala/dispatch/HawtDispatcherEchoServer.scala b/akka-core/src/test/scala/dispatch/HawtDispatcherEchoServer.scala new file mode 100644 index 0000000000..208433bd4b --- /dev/null +++ b/akka-core/src/test/scala/dispatch/HawtDispatcherEchoServer.scala @@ -0,0 +1,221 @@ +/** + * Copyright (C) 2010, Progress Software Corporation and/or its + * subsidiaries or affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package se.scalablesolutions.akka.actor.dispatch + +import collection.mutable.ListBuffer +import java.util.concurrent.TimeUnit +import java.net.InetSocketAddress +import java.io.IOException +import java.nio.ByteBuffer +import java.nio.channels.{SocketChannel, SelectionKey, ServerSocketChannel} + +import se.scalablesolutions.akka.actor._ +import se.scalablesolutions.akka.actor.Actor._ +import se.scalablesolutions.akka.dispatch.HawtDispatcher +import org.fusesource.hawtdispatch.DispatchSource +import org.fusesource.hawtdispatch.ScalaDispatch._ + +/** + * This is an example of how to crate an Akka actor based TCP echo server using + * the HawtDispatch dispatcher and NIO event sources. + * + */ +object HawtDispatcherEchoServer { + + private val hawt = new HawtDispatcher + var port=4444; + var useReactorPattern=true + + def main(args:Array[String]):Unit = run + + def run() = { + val server = actorOf(new Server(port)) + server.start + Scheduler.schedule(server, DisplayStats, 1, 5, TimeUnit.SECONDS) + + println("Press enter to shutdown."); + System.in.read + server ! Shutdown + } + + case object Shutdown + case object DisplayStats + case class SessionClosed(session:ActorRef) + + class Server(val port: Int) extends Actor { + + self.dispatcher = hawt + + var channel:ServerSocketChannel = _ + var accept_source:DispatchSource = _ + var sessions = ListBuffer[ActorRef]() + + override def init = { + channel = ServerSocketChannel.open(); + channel.socket().bind(new InetSocketAddress(port)); + channel.configureBlocking(false); + + // Setup the accept source, it will callback to the handler methods + // via the actor's mailbox so you don't need to worry about + // synchronizing with the local variables + accept_source = createSource(channel, SelectionKey.OP_ACCEPT, HawtDispatcher.queue(self)); + accept_source.setEventHandler(^{ accept }); + accept_source.setDisposer(^{ + channel.close(); + println("Closed port: "+port); + }); + + accept_source.resume + + println("Listening on port: "+port); + } + + + private def accept() = { + var socket = channel.accept(); + while( socket!=null ) { + try { + socket.configureBlocking(false); + val session = actorOf(new Session(self, socket)) + session.start() + sessions += session + } catch { + case e: Exception => + socket.close + } + socket = channel.accept(); + } + } + + def receive = { + case SessionClosed(session) => + sessions = sessions.filterNot( _ == session ) + session.stop + case DisplayStats => + sessions.foreach { session=> + session ! DisplayStats + } + case Shutdown => + sessions.foreach { session=> + session.stop + } + sessions.clear + accept_source.release + self.stop + } + } + + class Session(val server:ActorRef, val channel: SocketChannel) extends Actor { + + self.dispatcher = hawt + + val buffer = ByteBuffer.allocate(1024); + val remote_address = channel.socket.getRemoteSocketAddress.toString + + var read_source:DispatchSource = _ + var write_source:DispatchSource = _ + + var readCounter = 0L + var writeCounter = 0L + var closed = false + + override def init = { + + if(useReactorPattern) { + // Then we will be using the reactor pattern for handling IO: + // Pin this actor to a single thread. The read/write event sources will poll + // a Selector on the pinned thread. Since the IO events are generated on the same + // thread as where the Actor is pinned to, it can avoid a substantial amount + // thread synchronization. Plus your GC will perform better since all the IO + // processing is done on a single thread. + HawtDispatcher.pin(self) + } else { + // Then we will be using sing the proactor pattern for handling IO: + // Then the actor will not be pinned to a specific thread. The read/write + // event sources will poll a Selector and then asynchronously dispatch the + // event's to the actor via the thread pool. + } + + // Setup the sources, they will callback to the handler methods + // via the actor's mailbox so you don't need to worry about + // synchronizing with the local variables + read_source = createSource(channel, SelectionKey.OP_READ, HawtDispatcher.queue(self)); + read_source.setEventHandler(^{ read }) + read_source.setCancelHandler(^{ close }) + + write_source = createSource(channel, SelectionKey.OP_READ, HawtDispatcher.queue(self)); + write_source.setEventHandler(^{ write }) + write_source.setCancelHandler(^{ close }) + + read_source.resume + println("Accepted connection from: "+remote_address); + } + + override def shutdown = { + closed = true + read_source.release + write_source.release + channel.close + } + + private def catchio(func: =>Unit):Unit = { + try { + func + } catch { + case e:IOException => close + } + } + + def read():Unit = catchio { + channel.read(buffer) match { + case -1 => + close // peer disconnected. + case 0 => + case count:Int => + readCounter += count + buffer.flip; + read_source.suspend + write_source.resume + write() + } + } + + def write() = catchio { + writeCounter += channel.write(buffer) + if (buffer.remaining == 0) { + buffer.clear + write_source.suspend + read_source.resume + } + } + + + def close() = { + if( !closed ) { + closed = true + server ! SessionClosed(self) + } + } + + def receive = { + case DisplayStats => + println("connection to %s reads: %,d bytes, writes: %,d".format(remote_address, readCounter, writeCounter)) + } + + } + +} diff --git a/akka-core/src/test/scala/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala b/akka-core/src/test/scala/dispatch/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala similarity index 95% rename from akka-core/src/test/scala/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala rename to akka-core/src/test/scala/dispatch/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala index a84cdfc8e5..de9b912bf5 100644 --- a/akka-core/src/test/scala/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala +++ b/akka-core/src/test/scala/dispatch/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala @@ -1,11 +1,12 @@ -package se.scalablesolutions.akka.actor +package se.scalablesolutions.akka.actor.dispatch import java.util.concurrent.{CountDownLatch, TimeUnit} import org.scalatest.junit.JUnitSuite import org.junit.Test -import Actor._ import se.scalablesolutions.akka.dispatch.Dispatchers +import se.scalablesolutions.akka.actor.Actor +import Actor._ object ReactorBasedSingleThreadEventDrivenDispatcherActorSpec { class TestActor extends Actor { diff --git a/akka-core/src/test/scala/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala b/akka-core/src/test/scala/dispatch/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala similarity index 95% rename from akka-core/src/test/scala/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala rename to akka-core/src/test/scala/dispatch/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala index f917b462b5..4001df8f56 100644 --- a/akka-core/src/test/scala/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala +++ b/akka-core/src/test/scala/dispatch/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala @@ -1,10 +1,11 @@ -package se.scalablesolutions.akka.actor +package se.scalablesolutions.akka.actor.dispatch import java.util.concurrent.{CountDownLatch, TimeUnit} import org.scalatest.junit.JUnitSuite import org.junit.Test import se.scalablesolutions.akka.dispatch.Dispatchers +import se.scalablesolutions.akka.actor.Actor import Actor._ object ReactorBasedThreadPoolEventDrivenDispatcherActorSpec { diff --git a/akka-core/src/test/scala/ThreadBasedActorSpec.scala b/akka-core/src/test/scala/dispatch/ThreadBasedActorSpec.scala similarity index 94% rename from akka-core/src/test/scala/ThreadBasedActorSpec.scala rename to akka-core/src/test/scala/dispatch/ThreadBasedActorSpec.scala index fc726ecf49..d69ee984d8 100644 --- a/akka-core/src/test/scala/ThreadBasedActorSpec.scala +++ b/akka-core/src/test/scala/dispatch/ThreadBasedActorSpec.scala @@ -1,10 +1,11 @@ -package se.scalablesolutions.akka.actor +package se.scalablesolutions.akka.actor.dispatch import java.util.concurrent.{CountDownLatch, TimeUnit} import org.scalatest.junit.JUnitSuite import org.junit.Test import se.scalablesolutions.akka.dispatch.Dispatchers +import se.scalablesolutions.akka.actor.Actor import Actor._ object ThreadBasedActorSpec { diff --git a/akka-core/src/test/scala/ThreadBasedDispatcherSpec.scala b/akka-core/src/test/scala/dispatch/ThreadBasedDispatcherSpec.scala similarity index 100% rename from akka-core/src/test/scala/ThreadBasedDispatcherSpec.scala rename to akka-core/src/test/scala/dispatch/ThreadBasedDispatcherSpec.scala diff --git a/akka-core/src/test/scala/ActorRegistrySpec.scala b/akka-core/src/test/scala/misc/ActorRegistrySpec.scala similarity index 81% rename from akka-core/src/test/scala/ActorRegistrySpec.scala rename to akka-core/src/test/scala/misc/ActorRegistrySpec.scala index 6914472e2c..8c9e0778ca 100644 --- a/akka-core/src/test/scala/ActorRegistrySpec.scala +++ b/akka-core/src/test/scala/misc/ActorRegistrySpec.scala @@ -3,6 +3,7 @@ package se.scalablesolutions.akka.actor import org.scalatest.junit.JUnitSuite import org.junit.Test import Actor._ +import java.util.concurrent.{CyclicBarrier, TimeUnit, CountDownLatch} object ActorRegistrySpec { var record = "" @@ -76,6 +77,17 @@ class ActorRegistrySpec extends JUnitSuite { actor.stop } + @Test def shouldFindThingsFromActorRegistry { + ActorRegistry.shutdownAll + val actor = actorOf[TestActor] + actor.start + val found = ActorRegistry.find({ case a: ActorRef if a.actor.isInstanceOf[TestActor] => a }) + assert(found.isDefined) + assert(found.get.actor.isInstanceOf[TestActor]) + assert(found.get.id === "MyID") + actor.stop + } + @Test def shouldGetActorsByIdFromActorRegistry { ActorRegistry.shutdownAll val actor1 = actorOf[TestActor] @@ -203,4 +215,41 @@ class ActorRegistrySpec extends JUnitSuite { ActorRegistry.unregister(actor2) assert(ActorRegistry.actors.size === 0) } + + @Test def shouldBeAbleToRegisterActorsConcurrently { + ActorRegistry.shutdownAll + + val latch = new CountDownLatch(3) + val barrier = new CyclicBarrier(3) + + def mkTestActor(i:Int) = actorOf( new Actor { + self.id = i.toString + def receive = { case _ => } + }) + + def mkTestActors = for(i <- 1 to 10;j <- 1 to 1000) yield mkTestActor(i) + + def mkThread(actors: Iterable[ActorRef]) = new Thread { + start + override def run { + barrier.await + actors foreach { _.start } + latch.countDown + } + } + + val testActors1 = mkTestActors + val testActors2 = mkTestActors + val testActors3 = mkTestActors + + mkThread(testActors1) + mkThread(testActors2) + mkThread(testActors3) + + assert(latch.await(30,TimeUnit.SECONDS) === true) + + for(i <- 1 to 10) { + assert(ActorRegistry.actorsFor(i.toString).length === 3000) + } + } } diff --git a/akka-core/src/test/scala/misc/SchedulerSpec.scala b/akka-core/src/test/scala/misc/SchedulerSpec.scala new file mode 100644 index 0000000000..16dd21f327 --- /dev/null +++ b/akka-core/src/test/scala/misc/SchedulerSpec.scala @@ -0,0 +1,127 @@ +package se.scalablesolutions.akka.actor + +import org.scalatest.junit.JUnitSuite +import Actor._ +import java.util.concurrent.{CountDownLatch, TimeUnit} +import se.scalablesolutions.akka.config.ScalaConfig._ +import org.multiverse.api.latches.StandardLatch +import org.junit.Test + +class SchedulerSpec extends JUnitSuite { + + def withCleanEndState(action: => Unit) { + action + Scheduler.restart + ActorRegistry.shutdownAll + } + + + @Test def schedulerShouldScheduleMoreThanOnce = withCleanEndState { + + case object Tick + val countDownLatch = new CountDownLatch(3) + val tickActor = actor { + case Tick => countDownLatch.countDown + } + // run every 50 millisec + Scheduler.schedule(tickActor, Tick, 0, 50, TimeUnit.MILLISECONDS) + + // after max 1 second it should be executed at least the 3 times already + assert(countDownLatch.await(1, TimeUnit.SECONDS)) + + val countDownLatch2 = new CountDownLatch(3) + + Scheduler.schedule( () => countDownLatch2.countDown, 0, 50, TimeUnit.MILLISECONDS) + + // after max 1 second it should be executed at least the 3 times already + assert(countDownLatch2.await(1, TimeUnit.SECONDS)) + } + + @Test def schedulerShouldScheduleOnce = withCleanEndState { + case object Tick + val countDownLatch = new CountDownLatch(3) + val tickActor = actor { + case Tick => countDownLatch.countDown + } + // run every 50 millisec + Scheduler.scheduleOnce(tickActor, Tick, 50, TimeUnit.MILLISECONDS) + Scheduler.scheduleOnce( () => countDownLatch.countDown, 50, TimeUnit.MILLISECONDS) + + // after 1 second the wait should fail + assert(countDownLatch.await(1, TimeUnit.SECONDS) == false) + // should still be 1 left + assert(countDownLatch.getCount == 1) + } + + /** + * ticket #372 + */ + @Test def schedulerShouldntCreateActors = withCleanEndState { + object Ping + val ticks = new CountDownLatch(1000) + val actor = actorOf(new Actor { + def receive = { case Ping => ticks.countDown } + }).start + val numActors = ActorRegistry.actors.length + (1 to 1000).foreach( _ => Scheduler.scheduleOnce(actor,Ping,1,TimeUnit.MILLISECONDS) ) + assert(ticks.await(10,TimeUnit.SECONDS)) + assert(ActorRegistry.actors.length === numActors) + } + + /** + * ticket #372 + */ + @Test def schedulerShouldBeCancellable = withCleanEndState { + object Ping + val ticks = new CountDownLatch(1) + + val actor = actorOf(new Actor { + def receive = { case Ping => ticks.countDown } + }).start + + (1 to 10).foreach { i => + val future = Scheduler.scheduleOnce(actor,Ping,1,TimeUnit.SECONDS) + future.cancel(true) + } + assert(ticks.await(3,TimeUnit.SECONDS) == false) //No counting down should've been made + } + + /** + * ticket #307 + */ + @Test def actorRestartShouldPickUpScheduleAgain = withCleanEndState { + + object Ping + object Crash + + val restartLatch = new StandardLatch + val pingLatch = new CountDownLatch(6) + + val actor = actorOf(new Actor { + self.lifeCycle = Some(LifeCycle(Permanent)) + + def receive = { + case Ping => pingLatch.countDown + case Crash => throw new Exception("CRASH") + } + + override def postRestart(reason: Throwable) = restartLatch.open + }) + Supervisor( + SupervisorConfig( + RestartStrategy(AllForOne, 3, 1000, + List(classOf[Exception])), + Supervise( + actor, + LifeCycle(Permanent)) + :: Nil)).start + + Scheduler.schedule(actor, Ping, 500, 500, TimeUnit.MILLISECONDS) + // appx 2 pings before crash + Scheduler.scheduleOnce(actor, Crash, 1000, TimeUnit.MILLISECONDS) + + assert(restartLatch.tryAwait(2, TimeUnit.SECONDS)) + // should be enough time for the ping countdown to recover and reach 6 pings + assert(pingLatch.await(4, TimeUnit.SECONDS)) + } +} diff --git a/akka-core/src/test/scala/ClientInitiatedRemoteActorSpec.scala b/akka-core/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala similarity index 65% rename from akka-core/src/test/scala/ClientInitiatedRemoteActorSpec.scala rename to akka-core/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala index ea48d45252..7ff46ab910 100644 --- a/akka-core/src/test/scala/ClientInitiatedRemoteActorSpec.scala +++ b/akka-core/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala @@ -1,63 +1,65 @@ -package se.scalablesolutions.akka.actor +package se.scalablesolutions.akka.actor.remote import java.util.concurrent.{CountDownLatch, TimeUnit} -import junit.framework.TestCase - import org.scalatest.junit.JUnitSuite import org.junit.{Test, Before, After} import se.scalablesolutions.akka.remote.{RemoteServer, RemoteClient} import se.scalablesolutions.akka.dispatch.Dispatchers +import se.scalablesolutions.akka.actor.{ActorRef, Actor} import Actor._ -case class Send(actor: Actor) +object ClientInitiatedRemoteActorSpec { + case class Send(actor: Actor) -object RemoteActorSpecActorUnidirectional { - val latch = new CountDownLatch(1) -} -class RemoteActorSpecActorUnidirectional extends Actor { - self.dispatcher = Dispatchers.newThreadBasedDispatcher(self) - - def receive = { - case "OneWay" => - RemoteActorSpecActorUnidirectional.latch.countDown + object RemoteActorSpecActorUnidirectional { + val latch = new CountDownLatch(1) } -} + class RemoteActorSpecActorUnidirectional extends Actor { + self.dispatcher = Dispatchers.newThreadBasedDispatcher(self) -class RemoteActorSpecActorBidirectional extends Actor { - def receive = { - case "Hello" => - self.reply("World") - case "Failure" => - throw new RuntimeException("Expected exception; to test fault-tolerance") + def receive = { + case "OneWay" => + RemoteActorSpecActorUnidirectional.latch.countDown + } } -} -class SendOneWayAndReplyReceiverActor extends Actor { - def receive = { - case "Hello" => - self.reply("World") + class RemoteActorSpecActorBidirectional extends Actor { + def receive = { + case "Hello" => + self.reply("World") + case "Failure" => + throw new RuntimeException("Expected exception; to test fault-tolerance") + } } -} -object SendOneWayAndReplySenderActor { - val latch = new CountDownLatch(1) -} -class SendOneWayAndReplySenderActor extends Actor { - var state: Option[AnyRef] = None - var sendTo: ActorRef = _ - var latch: CountDownLatch = _ + class SendOneWayAndReplyReceiverActor extends Actor { + def receive = { + case "Hello" => + self.reply("World") + } + } - def sendOff = sendTo ! "Hello" + object SendOneWayAndReplySenderActor { + val latch = new CountDownLatch(1) + } + class SendOneWayAndReplySenderActor extends Actor { + var state: Option[AnyRef] = None + var sendTo: ActorRef = _ + var latch: CountDownLatch = _ - def receive = { - case msg: AnyRef => - state = Some(msg) - SendOneWayAndReplySenderActor.latch.countDown + def sendOff = sendTo ! "Hello" + + def receive = { + case msg: AnyRef => + state = Some(msg) + SendOneWayAndReplySenderActor.latch.countDown + } } } class ClientInitiatedRemoteActorSpec extends JUnitSuite { + import ClientInitiatedRemoteActorSpec._ se.scalablesolutions.akka.config.Config.config val HOSTNAME = "localhost" diff --git a/akka-core/src/test/scala/RemoteSupervisorSpec.scala b/akka-core/src/test/scala/remote/RemoteSupervisorSpec.scala similarity index 98% rename from akka-core/src/test/scala/RemoteSupervisorSpec.scala rename to akka-core/src/test/scala/remote/RemoteSupervisorSpec.scala index 10f8c3bcf9..936d1cf5c4 100644 --- a/akka-core/src/test/scala/RemoteSupervisorSpec.scala +++ b/akka-core/src/test/scala/remote/RemoteSupervisorSpec.scala @@ -2,20 +2,17 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.actor +package se.scalablesolutions.akka.actor.remote import java.util.concurrent.{LinkedBlockingQueue, TimeUnit, BlockingQueue} import se.scalablesolutions.akka.serialization.BinaryString import se.scalablesolutions.akka.config.ScalaConfig._ -import se.scalablesolutions.akka.config.Config -import se.scalablesolutions.akka.remote.{RemoteNode, RemoteServer, RemoteClient} +import se.scalablesolutions.akka.remote.{RemoteServer, RemoteClient} import se.scalablesolutions.akka.OneWay -import se.scalablesolutions.akka.dispatch.Dispatchers -import Actor._ - import org.scalatest.junit.JUnitSuite -import org.junit.Test import org.junit.{Test, Before, After} +import se.scalablesolutions.akka.actor.{SupervisorFactory, Supervisor, ActorRef, Actor} +import Actor._ object Log { val messageLog: BlockingQueue[String] = new LinkedBlockingQueue[String] diff --git a/akka-core/src/test/scala/RemoteTransactionalActiveObjectSpec.scala b/akka-core/src/test/scala/remote/RemoteTransactionalTypedActorSpec.scala similarity index 71% rename from akka-core/src/test/scala/RemoteTransactionalActiveObjectSpec.scala rename to akka-core/src/test/scala/remote/RemoteTransactionalTypedActorSpec.scala index 897318ce7d..71d44fd4bd 100644 --- a/akka-core/src/test/scala/RemoteTransactionalActiveObjectSpec.scala +++ b/akka-core/src/test/scala/remote/RemoteTransactionalTypedActorSpec.scala @@ -13,25 +13,25 @@ import org.junit.runner.RunWith import org.junit.{Test, Before, After} import se.scalablesolutions.akka.config.Config -import se.scalablesolutions.akka.config.ActiveObjectConfigurator +import se.scalablesolutions.akka.config.TypedActorConfigurator import se.scalablesolutions.akka.remote.{RemoteNode, RemoteServer, RemoteClient} -object RemoteTransactionalActiveObjectSpec { +object RemoteTransactionalTypedActorSpec { val HOSTNAME = "localhost" val PORT = 9988 var server: RemoteServer = null } @RunWith(classOf[JUnitRunner]) -class RemoteTransactionalActiveObjectSpec extends +class RemoteTransactionalTypedActorSpec extends Spec with ShouldMatchers with BeforeAndAfterAll { - import RemoteTransactionalActiveObjectSpec._ + import RemoteTransactionalTypedActorSpec._ Config.config - private val conf = new ActiveObjectConfigurator + private val conf = new TypedActorConfigurator private var messageLog = "" override def beforeAll = { @@ -51,19 +51,19 @@ class RemoteTransactionalActiveObjectSpec extends } } - describe("Remote transactional in-memory Active Object ") { + describe("Remote transactional in-memory TypedActor ") { /* it("map should not rollback state for stateful server in case of success") { - val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT) + val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT) stateful.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") // set init state stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired stateful.getMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess") should equal("new state") } it("map should rollback state for stateful server in case of failure") { - val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT) + val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT) stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state - val failer =ActiveObject.newRemoteInstance(classOf[ActiveObjectFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[ActiveObjectFailer]) + val failer =TypedActor.newRemoteInstance(classOf[TypedActorFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[TypedActorFailer]) try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method fail("should have thrown an exception") @@ -72,16 +72,16 @@ class RemoteTransactionalActiveObjectSpec extends } it("vector should not rollback state for stateful server in case of success") { - val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT) + val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT) stateful.setVectorState("init") // set init state stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired stateful.getVectorState should equal("new state") } it("vector should rollback state for stateful server in case of failure") { - val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT) + val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT) stateful.setVectorState("init") // set init state - val failer =ActiveObject.newRemoteInstance(classOf[ActiveObjectFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[ActiveObjectFailer]) + val failer =TypedActor.newRemoteInstance(classOf[TypedActorFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[TypedActorFailer]) try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method fail("should have thrown an exception") @@ -90,16 +90,16 @@ class RemoteTransactionalActiveObjectSpec extends } it("ref should not rollback state for stateful server in case of success") { - val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT) + val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT) stateful.setRefState("init") // set init state stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired stateful.getRefState should equal("new state") } it("ref should rollback state for stateful server in case of failure") { - val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT) + val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT) stateful.setRefState("init") // set init state - val failer =ActiveObject.newRemoteInstance(classOf[ActiveObjectFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[ActiveObjectFailer]) + val failer =TypedActor.newRemoteInstance(classOf[TypedActorFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[TypedActorFailer]) try { stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method fail("should have thrown an exception") diff --git a/akka-core/src/test/scala/remote/RemoteTypedActorSpec.scala b/akka-core/src/test/scala/remote/RemoteTypedActorSpec.scala new file mode 100644 index 0000000000..c49882f61d --- /dev/null +++ b/akka-core/src/test/scala/remote/RemoteTypedActorSpec.scala @@ -0,0 +1,128 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.actor.remote + +import org.scalatest.Spec +import org.scalatest.Assertions +import org.scalatest.matchers.ShouldMatchers +import org.scalatest.BeforeAndAfterAll +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith + +import se.scalablesolutions.akka.config.Config +import se.scalablesolutions.akka.config._ +import se.scalablesolutions.akka.config.TypedActorConfigurator +import se.scalablesolutions.akka.config.JavaConfig._ +import se.scalablesolutions.akka.actor._ +import se.scalablesolutions.akka.remote.{RemoteServer, RemoteClient} + +import java.util.concurrent.{LinkedBlockingQueue, TimeUnit, BlockingQueue} + +object RemoteTypedActorSpec { + val HOSTNAME = "localhost" + val PORT = 9988 + var server: RemoteServer = null +} + +object RemoteTypedActorLog { + val messageLog: BlockingQueue[String] = new LinkedBlockingQueue[String] + val oneWayLog = new LinkedBlockingQueue[String] + + def clearMessageLogs { + messageLog.clear + oneWayLog.clear + } +} + +@RunWith(classOf[JUnitRunner]) +class RemoteTypedActorSpec extends + Spec with + ShouldMatchers with + BeforeAndAfterAll { + + import RemoteTypedActorLog._ + import RemoteTypedActorSpec._ + + private val conf = new TypedActorConfigurator + + override def beforeAll = { + server = new RemoteServer() + server.start("localhost", 9995) + Config.config + conf.configure( + new RestartStrategy(new AllForOne, 3, 5000, List(classOf[Exception]).toArray), + List( + new Component( + classOf[RemoteTypedActorOne], + classOf[RemoteTypedActorOneImpl], + new LifeCycle(new Permanent), + 10000, + new RemoteAddress("localhost", 9995)), + new Component( + classOf[RemoteTypedActorTwo], + classOf[RemoteTypedActorTwoImpl], + new LifeCycle(new Permanent), + 10000, + new RemoteAddress("localhost", 9995)) + ).toArray).supervise + Thread.sleep(1000) + } + + override def afterAll = { + conf.stop + try { + server.shutdown + RemoteClient.shutdownAll + Thread.sleep(1000) + } catch { + case e => () + } + ActorRegistry.shutdownAll + } + + describe("Remote Typed Actor ") { + + it("should receive one-way message") { + clearMessageLogs + val ta = conf.getInstance(classOf[RemoteTypedActorOne]) + + expect("oneway") { + ta.oneWay + oneWayLog.poll(5, TimeUnit.SECONDS) + } + } + + it("should respond to request-reply message") { + clearMessageLogs + val ta = conf.getInstance(classOf[RemoteTypedActorOne]) + + expect("pong") { + ta.requestReply("ping") + } + } + + it("should be restarted on failure") { + clearMessageLogs + val ta = conf.getInstance(classOf[RemoteTypedActorOne]) + + intercept[RuntimeException] { + ta.requestReply("die") + } + messageLog.poll(5, TimeUnit.SECONDS) should equal ("Expected exception; to test fault-tolerance") + } + + it("should restart linked friends on failure") { + clearMessageLogs + val ta1 = conf.getInstance(classOf[RemoteTypedActorOne]) + val ta2 = conf.getInstance(classOf[RemoteTypedActorTwo]) + + intercept[RuntimeException] { + ta1.requestReply("die") + } + messageLog.poll(5, TimeUnit.SECONDS) should equal ("Expected exception; to test fault-tolerance") + messageLog.poll(5, TimeUnit.SECONDS) should equal ("Expected exception; to test fault-tolerance") + } + } +} diff --git a/akka-core/src/test/scala/ServerInitiatedRemoteActorSample.scala b/akka-core/src/test/scala/remote/ServerInitiatedRemoteActorSample.scala similarity index 94% rename from akka-core/src/test/scala/ServerInitiatedRemoteActorSample.scala rename to akka-core/src/test/scala/remote/ServerInitiatedRemoteActorSample.scala index dad54e6d3a..b9b8946bee 100644 --- a/akka-core/src/test/scala/ServerInitiatedRemoteActorSample.scala +++ b/akka-core/src/test/scala/remote/ServerInitiatedRemoteActorSample.scala @@ -1,4 +1,4 @@ -package sample +package se.scalablesolutions.akka.actor.remote import se.scalablesolutions.akka.actor.Actor import se.scalablesolutions.akka.remote.{RemoteClient, RemoteNode} diff --git a/akka-core/src/test/scala/ServerInitiatedRemoteActorSpec.scala b/akka-core/src/test/scala/remote/ServerInitiatedRemoteActorSpec.scala similarity index 84% rename from akka-core/src/test/scala/ServerInitiatedRemoteActorSpec.scala rename to akka-core/src/test/scala/remote/ServerInitiatedRemoteActorSpec.scala index 51685a0073..59cfe3778d 100644 --- a/akka-core/src/test/scala/ServerInitiatedRemoteActorSpec.scala +++ b/akka-core/src/test/scala/remote/ServerInitiatedRemoteActorSpec.scala @@ -1,12 +1,12 @@ -package se.scalablesolutions.akka.actor +package se.scalablesolutions.akka.actor.remote import java.util.concurrent.{CountDownLatch, TimeUnit} import org.scalatest.junit.JUnitSuite import org.junit.{Test, Before, After} -import Actor._ import se.scalablesolutions.akka.remote.{RemoteServer, RemoteClient} -import se.scalablesolutions.akka.dispatch.Dispatchers +import se.scalablesolutions.akka.actor.{ActorRef, Actor} +import Actor._ object ServerInitiatedRemoteActorSpec { val HOSTNAME = "localhost" @@ -52,8 +52,6 @@ object ServerInitiatedRemoteActorSpec { class ServerInitiatedRemoteActorSpec extends JUnitSuite { import ServerInitiatedRemoteActorSpec._ - import se.scalablesolutions.akka.config.Config.config - private val unit = TimeUnit.MILLISECONDS @Before @@ -84,7 +82,7 @@ class ServerInitiatedRemoteActorSpec extends JUnitSuite { @Test def shouldSendWithBang { val actor = RemoteClient.actorFor( - "se.scalablesolutions.akka.actor.ServerInitiatedRemoteActorSpec$RemoteActorSpecActorUnidirectional", + "se.scalablesolutions.akka.actor.remote.ServerInitiatedRemoteActorSpec$RemoteActorSpecActorUnidirectional", 5000L, HOSTNAME, PORT) val result = actor ! "OneWay" @@ -95,7 +93,7 @@ class ServerInitiatedRemoteActorSpec extends JUnitSuite { @Test def shouldSendWithBangBangAndGetReply { val actor = RemoteClient.actorFor( - "se.scalablesolutions.akka.actor.ServerInitiatedRemoteActorSpec$RemoteActorSpecActorBidirectional", + "se.scalablesolutions.akka.actor.remote.ServerInitiatedRemoteActorSpec$RemoteActorSpecActorBidirectional", 5000L, HOSTNAME, PORT) val result = actor !! "Hello" @@ -107,7 +105,7 @@ class ServerInitiatedRemoteActorSpec extends JUnitSuite { def shouldSendWithBangAndGetReplyThroughSenderRef { implicit val timeout = 500000000L val actor = RemoteClient.actorFor( - "se.scalablesolutions.akka.actor.ServerInitiatedRemoteActorSpec$RemoteActorSpecActorBidirectional", + "se.scalablesolutions.akka.actor.remote.ServerInitiatedRemoteActorSpec$RemoteActorSpecActorBidirectional", timeout, HOSTNAME, PORT) val sender = actorOf[RemoteActorSpecActorAsyncSender] @@ -122,7 +120,7 @@ class ServerInitiatedRemoteActorSpec extends JUnitSuite { def shouldSendWithBangBangAndReplyWithException { implicit val timeout = 500000000L val actor = RemoteClient.actorFor( - "se.scalablesolutions.akka.actor.ServerInitiatedRemoteActorSpec$RemoteActorSpecActorBidirectional", + "se.scalablesolutions.akka.actor.remote.ServerInitiatedRemoteActorSpec$RemoteActorSpecActorBidirectional", timeout, HOSTNAME, PORT) try { diff --git a/akka-core/src/test/scala/ShutdownSpec.scala b/akka-core/src/test/scala/remote/ShutdownSpec.scala similarity index 100% rename from akka-core/src/test/scala/ShutdownSpec.scala rename to akka-core/src/test/scala/remote/ShutdownSpec.scala diff --git a/akka-core/src/test/scala/RoutingSpec.scala b/akka-core/src/test/scala/routing/RoutingSpec.scala similarity index 96% rename from akka-core/src/test/scala/RoutingSpec.scala rename to akka-core/src/test/scala/routing/RoutingSpec.scala index 06b8117c24..747363efe6 100644 --- a/akka-core/src/test/scala/RoutingSpec.scala +++ b/akka-core/src/test/scala/routing/RoutingSpec.scala @@ -1,6 +1,5 @@ -package se.scalablesolutions.akka.routing +package se.scalablesolutions.akka.actor.routing -import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.actor.Actor import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.util.Logging @@ -9,12 +8,11 @@ import org.scalatest.Suite import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner import org.scalatest.matchers.MustMatchers -import org.junit.{Before, After, Test} - -import scala.collection.mutable.HashSet +import org.junit.Test import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.{CountDownLatch, TimeUnit} +import se.scalablesolutions.akka.routing._ @RunWith(classOf[JUnitRunner]) class RoutingSpec extends junit.framework.TestCase with Suite with MustMatchers with Logging { diff --git a/akka-core/src/test/scala/ProtobufActorMessageSerializationSpec.scala b/akka-core/src/test/scala/serialization/ProtobufActorMessageSerializationSpec.scala similarity index 91% rename from akka-core/src/test/scala/ProtobufActorMessageSerializationSpec.scala rename to akka-core/src/test/scala/serialization/ProtobufActorMessageSerializationSpec.scala index fbf5411e02..011c656f8d 100644 --- a/akka-core/src/test/scala/ProtobufActorMessageSerializationSpec.scala +++ b/akka-core/src/test/scala/serialization/ProtobufActorMessageSerializationSpec.scala @@ -1,12 +1,11 @@ -package se.scalablesolutions.akka.actor +package se.scalablesolutions.akka.actor.serialization -import java.util.concurrent.{CountDownLatch, TimeUnit} +import java.util.concurrent.TimeUnit import org.scalatest.junit.JUnitSuite import org.junit.{Test, Before, After} import se.scalablesolutions.akka.remote.{RemoteServer, RemoteClient} -import se.scalablesolutions.akka.dispatch.Dispatchers - +import se.scalablesolutions.akka.actor.{ProtobufProtocol, Actor} import ProtobufProtocol.ProtobufPOJO import Actor._ diff --git a/akka-core/src/test/scala/SerializableTypeClassActorSpec.scala b/akka-core/src/test/scala/serialization/SerializableTypeClassActorSpec.scala similarity index 97% rename from akka-core/src/test/scala/SerializableTypeClassActorSpec.scala rename to akka-core/src/test/scala/serialization/SerializableTypeClassActorSpec.scala index 2b26c9ad81..a9bcc35790 100644 --- a/akka-core/src/test/scala/SerializableTypeClassActorSpec.scala +++ b/akka-core/src/test/scala/serialization/SerializableTypeClassActorSpec.scala @@ -1,16 +1,15 @@ -package se.scalablesolutions.akka.actor +package se.scalablesolutions.akka.actor.serialization -import Actor._ import org.scalatest.Spec -import org.scalatest.Assertions import org.scalatest.matchers.ShouldMatchers import org.scalatest.BeforeAndAfterAll import org.scalatest.junit.JUnitRunner import org.junit.runner.RunWith -import com.google.protobuf.Message +import se.scalablesolutions.akka.actor._ import ActorSerialization._ +import Actor._ @RunWith(classOf[JUnitRunner]) class SerializableTypeClassActorSpec extends @@ -166,7 +165,7 @@ class MyStatelessActor extends Actor { class MyStatelessActorWithMessagesInMailbox extends Actor { def receive = { case "hello" => - println("# messages in mailbox " + self.mailbox.size) + println("# messages in mailbox " + self.mailboxSize) Thread.sleep(500) case "hello-reply" => self.reply("world") } @@ -175,7 +174,7 @@ class MyStatelessActorWithMessagesInMailbox extends Actor { @serializable class MyJavaSerializableActor extends Actor { var count = 0 self.receiveTimeout = Some(1000) - + def receive = { case "hello" => count = count + 1 diff --git a/akka-core/src/test/scala/SerializerSpec.scala b/akka-core/src/test/scala/serialization/SerializerSpec.scala similarity index 93% rename from akka-core/src/test/scala/SerializerSpec.scala rename to akka-core/src/test/scala/serialization/SerializerSpec.scala index 95b291ed9d..bff387ec99 100644 --- a/akka-core/src/test/scala/SerializerSpec.scala +++ b/akka-core/src/test/scala/serialization/SerializerSpec.scala @@ -1,9 +1,7 @@ package se.scalablesolutions.akka.serialization -import junit.framework.TestCase - import org.scalatest.junit.JUnitSuite -import org.junit.{Test, Before, After} +import org.junit.Test import scala.reflect.BeanInfo diff --git a/akka-core/src/test/scala/serialization/UntypedActorSerializationSpec.scala b/akka-core/src/test/scala/serialization/UntypedActorSerializationSpec.scala new file mode 100644 index 0000000000..6588c7cdc4 --- /dev/null +++ b/akka-core/src/test/scala/serialization/UntypedActorSerializationSpec.scala @@ -0,0 +1,124 @@ +package se.scalablesolutions.akka.actor.serialization + + +import org.scalatest.Spec +import org.scalatest.matchers.ShouldMatchers +import org.scalatest.BeforeAndAfterAll +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith + +import se.scalablesolutions.akka.actor._ +import ActorSerialization._ +import Actor._ + +@RunWith(classOf[JUnitRunner]) +class UntypedActorSerializationSpec extends + Spec with + ShouldMatchers with + BeforeAndAfterAll { + + import se.scalablesolutions.akka.serialization.Serializer + + class MyUntypedActorFormat extends Format[MyUntypedActor] { + def fromBinary(bytes: Array[Byte], act: MyUntypedActor) = { + val p = Serializer.Protobuf.fromBinary(bytes, Some(classOf[ProtobufProtocol.Counter])).asInstanceOf[ProtobufProtocol.Counter] + act.count = p.getCount + act + } + def toBinary(ac: MyUntypedActor) = + ProtobufProtocol.Counter.newBuilder.setCount(ac.count).build.toByteArray + } + + class MyUntypedActorWithDualCounterFormat extends Format[MyUntypedActorWithDualCounter] { + def fromBinary(bytes: Array[Byte], act: MyUntypedActorWithDualCounter) = { + val p = Serializer.Protobuf.fromBinary(bytes, Some(classOf[ProtobufProtocol.DualCounter])).asInstanceOf[ProtobufProtocol.DualCounter] + act.count1 = p.getCount1 + act.count2 = p.getCount2 + act + } + def toBinary(ac: MyUntypedActorWithDualCounter) = + ProtobufProtocol.DualCounter.newBuilder.setCount1(ac.count1).setCount2(ac.count2).build.toByteArray + } + + object MyUntypedStatelessActorFormat extends StatelessActorFormat[MyUntypedStatelessActor] + + describe("Serializable untyped actor") { + it("should be able to serialize and de-serialize a stateful untyped actor") { + val actor1 = UntypedActor.actorOf[MyUntypedActor](classOf[MyUntypedActor]).start + actor1.sendRequestReply("hello") should equal("world 1") + actor1.sendRequestReply("debasish") should equal("hello debasish 2") + + val f = new MyUntypedActorFormat + val bytes = toBinaryJ(actor1, f) + val actor2 = fromBinaryJ(bytes, f) + actor2.start + actor2.sendRequestReply("hello") should equal("world 3") + } + + it("should be able to serialize and de-serialize a stateful actor with compound state") { + val actor1 = actorOf[MyUntypedActorWithDualCounter].start + actor1.sendRequestReply("hello") should equal("world 1 1") + actor1.sendRequestReply("hello") should equal("world 2 2") + + val f = new MyUntypedActorWithDualCounterFormat + val bytes = toBinaryJ(actor1, f) + val actor2 = fromBinaryJ(bytes, f) + actor2.start + actor2.sendRequestReply("hello") should equal("world 3 3") + } + + it("should be able to serialize and de-serialize a stateless actor") { + val actor1 = actorOf[MyUntypedStatelessActor].start + actor1.sendRequestReply("hello") should equal("world") + actor1.sendRequestReply("hello") should equal("world") + + val bytes = toBinaryJ(actor1, MyUntypedStatelessActorFormat) + val actor2 = fromBinaryJ(bytes, MyUntypedStatelessActorFormat) + actor2.start + actor2.sendRequestReply("hello") should equal("world") + } + } +} + +class MyUntypedActor extends UntypedActor { + var count = 0 + def onReceive(message: Any): Unit = message match { + case m: String if m == "hello" => + count = count + 1 + getContext.replyUnsafe("world " + count) + case m: String => + count = count + 1 + getContext.replyUnsafe("hello " + m + " " + count) + case _ => + throw new Exception("invalid message type") + } +} + +class MyUntypedActorWithDualCounter extends UntypedActor { + var count1 = 0 + var count2 = 0 + + def onReceive(message: Any): Unit = message match { + case m: String if m == "hello" => + count1 = count1 + 1 + count2 = count2 + 1 + getContext.replyUnsafe("world " + count1 + " " + count2) + case m: String => + count1 = count1 + 1 + count2 = count2 + 1 + getContext.replyUnsafe("hello " + m + " " + count1 + " " + count2) + case _ => + throw new Exception("invalid message type") + } +} + +class MyUntypedStatelessActor extends UntypedActor { + def onReceive(message: Any): Unit = message match { + case m: String if m == "hello" => + getContext.replyUnsafe("world") + case m: String => + getContext.replyUnsafe("hello " + m) + case _ => + throw new Exception("invalid message type") + } +} diff --git a/akka-core/src/test/scala/stm/JavaStmSpec.scala b/akka-core/src/test/scala/stm/JavaStmSpec.scala new file mode 100644 index 0000000000..70dcefd79e --- /dev/null +++ b/akka-core/src/test/scala/stm/JavaStmSpec.scala @@ -0,0 +1,5 @@ +package se.scalablesolutions.akka.stm + +import org.scalatest.junit.JUnitWrapperSuite + +class JavaStmSpec extends JUnitWrapperSuite("se.scalablesolutions.akka.stm.JavaStmTests", Thread.currentThread.getContextClassLoader) diff --git a/akka-core/src/test/scala/stm/RefSpec.scala b/akka-core/src/test/scala/stm/RefSpec.scala new file mode 100644 index 0000000000..2a8d39a065 --- /dev/null +++ b/akka-core/src/test/scala/stm/RefSpec.scala @@ -0,0 +1,152 @@ +package se.scalablesolutions.akka.stm + +import org.scalatest.WordSpec +import org.scalatest.matchers.MustMatchers + +class RefSpec extends WordSpec with MustMatchers { + + import se.scalablesolutions.akka.stm.local._ + + "A Ref" should { + + "optionally accept an initial value" in { + val emptyRef = Ref[Int] + val empty = atomic { emptyRef.opt } + + empty must be(None) + + val ref = Ref(3) + val value = atomic { ref.get } + + value must be (3) + } + + "keep the initial value, even if the first transaction is rolled back" in { + val ref = Ref(3) + + try { + atomic(DefaultLocalTransactionFactory) { + ref.swap(5) + throw new Exception + } + } catch { + case e => {} + } + + val value = atomic { ref.get } + + value must be (3) + } + + "be settable using set" in { + val ref = Ref[Int] + + atomic { ref.set(3) } + + val value = atomic { ref.get } + + value must be (3) + } + + "be settable using swap" in { + val ref = Ref[Int] + + atomic { ref.swap(3) } + + val value = atomic { ref.get } + + value must be (3) + } + + "be changeable using alter" in { + val ref = Ref(0) + + def increment = atomic { + ref alter (_ + 1) + } + + increment + increment + increment + + val value = atomic { ref.get } + + value must be (3) + } + + "be able to be mapped" in { + val ref1 = Ref(1) + + val ref2 = atomic { + ref1 map (_ + 1) + } + + val value1 = atomic { ref1.get } + val value2 = atomic { ref2.get } + + value1 must be (1) + value2 must be (2) + } + + "be able to be used in a 'foreach' for comprehension" in { + val ref = Ref(3) + + var result = 0 + + atomic { + for (value <- ref) { + result += value + } + } + + result must be (3) + } + + "be able to be used in a 'map' for comprehension" in { + val ref1 = Ref(1) + + val ref2 = atomic { + for (value <- ref1) yield value + 2 + } + + val value2 = atomic { ref2.get } + + value2 must be (3) + } + + "be able to be used in a 'flatMap' for comprehension" in { + val ref1 = Ref(1) + val ref2 = Ref(2) + + val ref3 = atomic { + for { + value1 <- ref1 + value2 <- ref2 + } yield value1 + value2 + } + + val value3 = atomic { ref3.get } + + value3 must be (3) + } + + "be able to be used in a 'filter' for comprehension" in { + val ref1 = Ref(1) + + val refLess2 = atomic { + for (value <- ref1 if value < 2) yield value + } + + val optLess2 = atomic { refLess2.opt } + + val refGreater2 = atomic { + for (value <- ref1 if value > 2) yield value + } + + val optGreater2 = atomic { refGreater2.opt } + + optLess2 must be (Some(1)) + optGreater2 must be (None) + } + } +} diff --git a/akka-core/src/test/scala/StmSpec.scala b/akka-core/src/test/scala/stm/StmSpec.scala similarity index 68% rename from akka-core/src/test/scala/StmSpec.scala rename to akka-core/src/test/scala/stm/StmSpec.scala index 16912726a9..133725bb98 100644 --- a/akka-core/src/test/scala/StmSpec.scala +++ b/akka-core/src/test/scala/stm/StmSpec.scala @@ -3,72 +3,66 @@ package se.scalablesolutions.akka.stm import se.scalablesolutions.akka.actor.{Actor, Transactor} import Actor._ -import org.scalatest.Spec -import org.scalatest.Assertions -import org.scalatest.matchers.ShouldMatchers -import org.scalatest.BeforeAndAfterAll -import org.scalatest.junit.JUnitRunner -import org.junit.runner.RunWith +import org.scalatest.WordSpec +import org.scalatest.matchers.MustMatchers -@RunWith(classOf[JUnitRunner]) -class StmSpec extends - Spec with - ShouldMatchers with - BeforeAndAfterAll { +class StmSpec extends WordSpec with MustMatchers { - describe("Local STM") { - it("should be able to do multiple consecutive atomic {..} statements") { - import local._ + "Local STM" should { - lazy val ref = Ref[Int]() + import se.scalablesolutions.akka.stm.local._ + + "be able to do multiple consecutive atomic {..} statements" in { + val ref = Ref(0) def increment = atomic { - ref.swap(ref.get.getOrElse(0) + 1) + ref alter (_ + 1) } def total: Int = atomic { - ref.get.getOrElse(0) + ref.getOrElse(0) } increment increment increment - total should equal(3) + + total must be (3) } - it("should be able to do nested atomic {..} statements") { - import local._ - - lazy val ref = Ref[Int]() + "be able to do nested atomic {..} statements" in { + val ref = Ref(0) def increment = atomic { - ref.swap(ref.get.getOrElse(0) + 1) + ref alter (_ + 1) } + def total: Int = atomic { - ref.get.getOrElse(0) + ref.getOrElse(0) } atomic { increment increment } + atomic { increment - total should equal(3) + total must be (3) } } - it("should roll back failing nested atomic {..} statements") { - import local._ - - lazy val ref = Ref[Int]() + "roll back failing nested atomic {..} statements" in { + val ref = Ref(0) def increment = atomic { - ref.swap(ref.get.getOrElse(0) + 1) + ref alter (_ + 1) } + def total: Int = atomic { - ref.get.getOrElse(0) + ref.getOrElse(0) } + try { atomic(DefaultLocalTransactionFactory) { increment @@ -78,21 +72,22 @@ class StmSpec extends } catch { case e => {} } - total should equal(0) + + total must be (0) } } - describe("Global STM") { - it("should be able to initialize with atomic {..} block inside actor constructor") { + "Global STM" should { + "be able to initialize with atomic {..} block inside actor constructor" in { import GlobalTransactionVectorTestActor._ try { val actor = actorOf[GlobalTransactionVectorTestActor].start actor !! Add(5) val size1 = (actor !! Size).as[Int].getOrElse(fail("Could not get Vector::size")) - size1 should equal(2) + size1 must be (2) actor !! Add(2) val size2 = (actor !! Size).as[Int].getOrElse(fail("Could not get Vector::size")) - size2 should equal(3) + size2 must be (3) } catch { case e => e.printStackTrace @@ -100,25 +95,26 @@ class StmSpec extends } } } + /* - describe("Transactor") { - it("should be able receive message sent with !! and pass it along to nested transactor with !! and receive reply; multiple times in a row") { + "Transactor" should { + "be able receive message sent with !! and pass it along to nested transactor with !! and receive reply; multiple times in a row" in { import GlobalTransactionVectorTestActor._ val actor = actorOf[NestedTransactorLevelOneActor].start actor !! (Add(2), 10000) val size1 = (actor !! (Size, 10000)).as[Int].getOrElse(fail("Could not get size")) - size1 should equal(2) + size1 must be (2) actor !! (Add(7), 10000) actor ! "HiLevelOne" val size2 = (actor !! (Size, 10000)).as[Int].getOrElse(fail("Could not get size")) - size2 should equal(7) + size2 must be (7) actor !! (Add(0), 10000) actor ! "HiLevelTwo" val size3 = (actor !! (Size, 10000)).as[Int].getOrElse(fail("Could not get size")) - size3 should equal(0) + size3 must be (0) actor !! (Add(3), 10000) val size4 = (actor !! (Size, 10000)).as[Int].getOrElse(fail("Could not get size")) - size4 should equal(3) + size4 must be (3) } } */ @@ -129,6 +125,7 @@ object GlobalTransactionVectorTestActor { case object Size case object Success } + class GlobalTransactionVectorTestActor extends Actor { import GlobalTransactionVectorTestActor._ import se.scalablesolutions.akka.stm.global._ @@ -148,6 +145,7 @@ class GlobalTransactionVectorTestActor extends Actor { class NestedTransactorLevelOneActor extends Actor { import GlobalTransactionVectorTestActor._ + private val nested = actorOf[NestedTransactorLevelTwoActor].start self.timeout = 10000 @@ -165,6 +163,7 @@ class NestedTransactorLevelOneActor extends Actor { class NestedTransactorLevelTwoActor extends Transactor { import GlobalTransactionVectorTestActor._ + private val ref = Ref(0) self.timeout = 10000 diff --git a/akka-core/src/test/scala/ticket/Ticket001Spec.scala b/akka-core/src/test/scala/ticket/Ticket001Spec.scala new file mode 100644 index 0000000000..b94796d9a3 --- /dev/null +++ b/akka-core/src/test/scala/ticket/Ticket001Spec.scala @@ -0,0 +1,13 @@ +package se.scalablesolutions.akka.actor.ticket + +import org.scalatest.WordSpec +import org.scalatest.matchers.MustMatchers + +class Ticket001Spec extends WordSpec with MustMatchers { + + "An XXX" should { + "do YYY" in { + 1 must be (1) + } + } +} diff --git a/akka-http/src/main/scala/AkkaBroadcaster.scala b/akka-http/src/main/scala/AkkaBroadcaster.scala index 57d8ccd549..8f724ff445 100644 --- a/akka-http/src/main/scala/AkkaBroadcaster.scala +++ b/akka-http/src/main/scala/AkkaBroadcaster.scala @@ -6,11 +6,21 @@ package se.scalablesolutions.akka.comet import org.atmosphere.cpr.{AtmosphereResourceEvent, AtmosphereResource} import se.scalablesolutions.akka.actor.Actor._ +import se.scalablesolutions.akka.actor.Actor +import se.scalablesolutions.akka.dispatch.Dispatchers class AkkaBroadcaster extends org.atmosphere.jersey.JerseyBroadcaster { name = classOf[AkkaBroadcaster].getName - val caster = actor { case f : Function0[_] => f() } + val broadcasterDispatcher = Dispatchers.fromConfig("akka.rest.comet-dispatcher") + + //FIXME should be supervised + val caster = actorOf(new Actor { + self.dispatcher = broadcasterDispatcher + def receive = { + case f : Function0[_] => f() + } + }).start override def destroy { super.destroy diff --git a/akka-jta/src/main/scala/AtomikosTransactionService.scala b/akka-jta/src/main/scala/AtomikosTransactionService.scala index 937f31a54e..f85ff56e6a 100644 --- a/akka-jta/src/main/scala/AtomikosTransactionService.scala +++ b/akka-jta/src/main/scala/AtomikosTransactionService.scala @@ -10,6 +10,7 @@ import com.atomikos.icatch.jta.{J2eeTransactionManager, J2eeUserTransaction} import com.atomikos.icatch.config.{TSInitInfo, UserTransactionService, UserTransactionServiceImp} import se.scalablesolutions.akka.config.Config._ +import se.scalablesolutions.akka.util.Duration import se.scalablesolutions.akka.stm.{TransactionService, TransactionContainer} object AtomikosTransactionService extends AtomikosTransactionService @@ -20,8 +21,8 @@ object AtomikosTransactionService extends AtomikosTransactionService * @author Jonas Bonér */ class AtomikosTransactionService extends TransactionService with TransactionProtocol { + val JTA_TRANSACTION_TIMEOUT = Duration(config.getInt("akka.jta.timeout", 60), TIME_UNIT) - val JTA_TRANSACTION_TIMEOUT: Int = config.getInt("akka.jta.timeout", 60000) / 1000 private val txService: UserTransactionService = new UserTransactionServiceImp private val info: TSInitInfo = txService.createTSInitInfo @@ -29,10 +30,11 @@ class AtomikosTransactionService extends TransactionService with TransactionProt try { txService.init(info) val tm: TransactionManager = new J2eeTransactionManager - tm.setTransactionTimeout(JTA_TRANSACTION_TIMEOUT) + tm.setTransactionTimeout(JTA_TRANSACTION_TIMEOUT.toSeconds.toInt) tm } catch { - case e => throw new SystemException("Could not create a new Atomikos J2EE Transaction Manager, due to: " + e.toString) + case e => throw new SystemException( + "Could not create a new Atomikos J2EE Transaction Manager, due to: " + e.toString) } ))) // TODO: gracefully shutdown of the TM diff --git a/akka-karaf/akka-features/src/main/resources/features.xml b/akka-karaf/akka-features/src/main/resources/features.xml new file mode 100644 index 0000000000..7506820024 --- /dev/null +++ b/akka-karaf/akka-features/src/main/resources/features.xml @@ -0,0 +1,22 @@ + + + + + mvn:com.weiglewilczek.scala-lang-osgi/scala-library/2.8.0.RC2 + mvn:org.eclipse.scalamodules/scalamodules-core/2.0-M2 + + + + + mvn:se.scalablesolutions.akka.akka-wrap/dispatch-json_2.8.0.RC3_osgi/0.7.4 + mvn:org.objenesis/objenesis/1.2 + mvn:sjson.json/sjson/0.6-SNAPSHOT + + + + sjson + mvn:se.scalablesolutions.akka.akka-wrap/jgroups-wrapper_2.8.0.RC3_osgi/2.9.0.GA + mvn:org.jboss.netty/netty/3.2.0.CR1 + mvn:se.scalablesolutions.akka/akka-core_2.8.0.RC3_osgi/0.9 + + diff --git a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala index 135ee584b9..ccaf7518f1 100644 --- a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala +++ b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala @@ -7,11 +7,9 @@ package se.scalablesolutions.akka.persistence.common import se.scalablesolutions.akka.stm._ import se.scalablesolutions.akka.stm.TransactionManagement.transaction import se.scalablesolutions.akka.util.Logging +import se.scalablesolutions.akka.AkkaException -// FIXME move to 'stm' package + add message with more info -class NoTransactionInScopeException extends RuntimeException - -class StorageException(message: String) extends RuntimeException(message) +class StorageException(message: String) extends AkkaException(message) /** * Example Scala usage. @@ -90,7 +88,7 @@ trait PersistentMap[K, V] extends scala.collection.mutable.Map[K, V] val storage: MapStorageBackend[K, V] def commit = { - if (shouldClearOnCommit.isDefined && shouldClearOnCommit.get.get) storage.removeMapStorageFor(uuid) + if (shouldClearOnCommit.isDefined && shouldClearOnCommit.get) storage.removeMapStorageFor(uuid) removedEntries.toList.foreach(key => storage.removeMapStorageFor(uuid, key)) storage.insertMapStorageEntriesFor(uuid, newAndUpdatedEntries.toList) newAndUpdatedEntries.clear @@ -281,7 +279,7 @@ trait PersistentRef[T] extends Transactional with Committable with Abortable { val storage: RefStorageBackend[T] def commit = if (ref.isDefined) { - storage.insertRefStorageFor(uuid, ref.get.get) + storage.insertRefStorageFor(uuid, ref.get) ref.swap(null.asInstanceOf[T]) } @@ -292,7 +290,7 @@ trait PersistentRef[T] extends Transactional with Committable with Abortable { ref.swap(elem) } - def get: Option[T] = if (ref.isDefined) ref.get else storage.getRefStorageFor(uuid) + def get: Option[T] = if (ref.isDefined) ref.opt else storage.getRefStorageFor(uuid) def isDefined: Boolean = ref.isDefined || storage.getRefStorageFor(uuid).isDefined @@ -365,7 +363,7 @@ trait PersistentQueue[A] extends scala.collection.mutable.Queue[A] case DEQ => storage.dequeue(uuid) } } - if (shouldClearOnCommit.isDefined && shouldClearOnCommit.get.get) { + if (shouldClearOnCommit.isDefined && shouldClearOnCommit.get) { storage.remove(uuid) } enqueuedNDequeuedEntries.clear @@ -386,7 +384,7 @@ trait PersistentQueue[A] extends scala.collection.mutable.Queue[A] register elems.foreach(e => { enqueuedNDequeuedEntries.add((Some(e), ENQ)) - localQ.get.get.enqueue(e) + localQ.get.enqueue(e) }) } @@ -395,15 +393,15 @@ trait PersistentQueue[A] extends scala.collection.mutable.Queue[A] // record for later playback enqueuedNDequeuedEntries.add((None, DEQ)) - val i = pickMeForDQ.get.get + val i = pickMeForDQ.get if (i < storage.size(uuid)) { // still we can DQ from storage pickMeForDQ.swap(i + 1) storage.peek(uuid, i, 1)(0) } else { // check we have transient candidates in localQ for DQ - if (localQ.get.get.isEmpty == false) { - val (a, q) = localQ.get.get.dequeue + if (localQ.get.isEmpty == false) { + val (a, q) = localQ.get.dequeue localQ.swap(q) a } else throw new NoSuchElementException("trying to dequeue from empty queue") @@ -418,7 +416,7 @@ trait PersistentQueue[A] extends scala.collection.mutable.Queue[A] } override def size: Int = try { - storage.size(uuid) + localQ.get.get.length + storage.size(uuid) + localQ.get.length } catch { case e: Exception => 0 } override def isEmpty: Boolean = diff --git a/akka-persistence/akka-persistence-mongo/src/main/scala/MongoStorageBackend.scala b/akka-persistence/akka-persistence-mongo/src/main/scala/MongoStorageBackend.scala index d5581b373b..950165567d 100644 --- a/akka-persistence/akka-persistence-mongo/src/main/scala/MongoStorageBackend.scala +++ b/akka-persistence/akka-persistence-mongo/src/main/scala/MongoStorageBackend.scala @@ -78,10 +78,8 @@ private[akka] object MongoStorageBackend extends val o = dbo.get(VALUE).asInstanceOf[Map[AnyRef, AnyRef]] o.putAll(m) - // remove existing reference - removeMapStorageFor(name) - // and insert - coll.insert(new BasicDBObject().append(KEY, name).append(VALUE, o)) + val newdbo = new BasicDBObject().append(KEY, name).append(VALUE, o) + coll.update(new BasicDBObject().append(KEY, name), newdbo, true, false) } } } diff --git a/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala b/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala index 33b1c04a73..eef60784a0 100644 --- a/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala +++ b/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala @@ -72,7 +72,7 @@ private [akka] object RedisStorageBackend extends // need an explicit definition in akka-conf val nodes = config.getList("akka.storage.redis.cluster") - val db = + def connect() = nodes match { case Seq() => // no cluster defined @@ -89,6 +89,8 @@ private [akka] object RedisStorageBackend extends } } + var db = connect() + /** * Map storage in Redis. *

@@ -411,6 +413,10 @@ private [akka] object RedisStorageBackend extends try { body } catch { + case e: RedisConnectionException => { + db = connect() + body + } case e: java.lang.NullPointerException => throw new StorageException("Could not connect to Redis server") case e => diff --git a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala index fc53db6f22..0bf8dc4fdf 100644 --- a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala +++ b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala @@ -40,7 +40,7 @@ case class Cell(food: Int = 0, pher: Float = 0, ant: Option[Ant] = None, home: B object EmptyCell extends Cell -class Place(initCell: Cell = EmptyCell) extends Ref(Some(initCell)) { +class Place(initCell: Cell = EmptyCell) extends Ref(initCell) { def cell: Cell = getOrElse(EmptyCell) def food: Int = cell.food def food(i: Int) = alter(_.addFood(i)) @@ -70,7 +70,7 @@ object World { private val snapshotFactory = TransactionFactory(readonly = true, familyName = "snapshot", hooks = false) - def snapshot = atomic(snapshotFactory) { Array.tabulate(Dim, Dim)(place(_, _).get) } + def snapshot = atomic(snapshotFactory) { Array.tabulate(Dim, Dim)(place(_, _).opt) } def place(loc: (Int, Int)) = places(loc._1)(loc._2) diff --git a/akka-samples/akka-sample-camel/src/main/java/sample/camel/BeanImpl.java b/akka-samples/akka-sample-camel/src/main/java/sample/camel/BeanImpl.java index 10437e7624..5fa9e8c599 100644 --- a/akka-samples/akka-sample-camel/src/main/java/sample/camel/BeanImpl.java +++ b/akka-samples/akka-sample-camel/src/main/java/sample/camel/BeanImpl.java @@ -1,9 +1,10 @@ package sample.camel; +import se.scalablesolutions.akka.actor.TypedActor; /** * @author Martin Krasser */ -public class BeanImpl implements BeanIntf { +public class BeanImpl extends TypedActor implements BeanIntf { public String foo(String s) { return "hello " + s; diff --git a/akka-samples/akka-sample-camel/src/main/java/sample/camel/ConsumerPojo2.java b/akka-samples/akka-sample-camel/src/main/java/sample/camel/ConsumerPojo2.java deleted file mode 100644 index 429e6043ad..0000000000 --- a/akka-samples/akka-sample-camel/src/main/java/sample/camel/ConsumerPojo2.java +++ /dev/null @@ -1,17 +0,0 @@ -package sample.camel; - -import org.apache.camel.Body; -import org.apache.camel.Header; -import se.scalablesolutions.akka.actor.annotation.consume; - -/** - * @author Martin Krasser - */ -public class ConsumerPojo2 { - - @consume("direct:default") - public String foo(String body) { - return String.format("default: %s", body); - } - -} \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteTypedConsumer1.java b/akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteTypedConsumer1.java new file mode 100644 index 0000000000..3e451af524 --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteTypedConsumer1.java @@ -0,0 +1,15 @@ +package sample.camel; + +import org.apache.camel.Body; +import org.apache.camel.Header; + +import se.scalablesolutions.akka.camel.consume; + +/** + * @author Martin Krasser + */ +public interface RemoteTypedConsumer1 { + + @consume("jetty:http://localhost:6644/camel/remote-typed-actor-1") + public String foo(@Body String body, @Header("name") String header); +} diff --git a/akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteConsumerPojo1.java b/akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteTypedConsumer1Impl.java similarity index 62% rename from akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteConsumerPojo1.java rename to akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteTypedConsumer1Impl.java index ab7e878b0d..71eda6c270 100644 --- a/akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteConsumerPojo1.java +++ b/akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteTypedConsumer1Impl.java @@ -3,16 +3,14 @@ package sample.camel; import org.apache.camel.Body; import org.apache.camel.Header; -import se.scalablesolutions.akka.actor.annotation.consume; +import se.scalablesolutions.akka.actor.TypedActor; /** * @author Martin Krasser */ -public class RemoteConsumerPojo1 { +public class RemoteTypedConsumer1Impl extends TypedActor implements RemoteTypedConsumer1 { - @consume("jetty:http://localhost:6644/camel/remote-active-object-1") public String foo(@Body String body, @Header("name") String header) { return String.format("remote1: body=%s header=%s", body, header); } - } diff --git a/akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteConsumerPojo2.java b/akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteTypedConsumer2Impl.java similarity index 62% rename from akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteConsumerPojo2.java rename to akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteTypedConsumer2Impl.java index e982fe5025..cc85ef963a 100644 --- a/akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteConsumerPojo2.java +++ b/akka-samples/akka-sample-camel/src/main/java/sample/camel/RemoteTypedConsumer2Impl.java @@ -2,14 +2,14 @@ package sample.camel; import org.apache.camel.Body; import org.apache.camel.Header; -import se.scalablesolutions.akka.actor.annotation.consume; +import se.scalablesolutions.akka.camel.consume; /** * @author Martin Krasser */ -public class RemoteConsumerPojo2 { +public class RemoteTypedConsumer2Impl { - @consume("jetty:http://localhost:6644/camel/remote-active-object-2") + @consume("jetty:http://localhost:6644/camel/remote-typed-actor-2") public String foo(@Body String body, @Header("name") String header) { return String.format("remote2: body=%s header=%s", body, header); } diff --git a/akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer1.java b/akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer1.java new file mode 100644 index 0000000000..e495ee404c --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer1.java @@ -0,0 +1,17 @@ +package sample.camel; + +import org.apache.camel.Body; +import org.apache.camel.Header; + +import se.scalablesolutions.akka.camel.consume; + +/** + * @author Martin Krasser + */ +public interface TypedConsumer1 { + @consume("file:data/input/typed-actor") + public void foo(String body); + + @consume("jetty:http://0.0.0.0:8877/camel/typed-actor") + public String bar(@Body String body, @Header("name") String header); +} diff --git a/akka-samples/akka-sample-camel/src/main/java/sample/camel/ConsumerPojo1.java b/akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer1Impl.java similarity index 68% rename from akka-samples/akka-sample-camel/src/main/java/sample/camel/ConsumerPojo1.java rename to akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer1Impl.java index ed29ac30e6..dc4014fb1f 100644 --- a/akka-samples/akka-sample-camel/src/main/java/sample/camel/ConsumerPojo1.java +++ b/akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer1Impl.java @@ -3,22 +3,19 @@ package sample.camel; import org.apache.camel.Body; import org.apache.camel.Header; -import se.scalablesolutions.akka.actor.annotation.consume; +import se.scalablesolutions.akka.actor.TypedActor; /** * @author Martin Krasser */ -public class ConsumerPojo1 { +public class TypedConsumer1Impl extends TypedActor implements TypedConsumer1 { - @consume("file:data/input/pojo") public void foo(String body) { System.out.println("Received message:"); System.out.println(body); } - @consume("jetty:http://0.0.0.0:8877/camel/pojo") public String bar(@Body String body, @Header("name") String header) { return String.format("body=%s header=%s", body, header); } - } diff --git a/akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer2.java b/akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer2.java new file mode 100644 index 0000000000..96878a2938 --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer2.java @@ -0,0 +1,14 @@ +package sample.camel; + +import org.apache.camel.Body; +import org.apache.camel.Header; +import se.scalablesolutions.akka.camel.consume; + +/** + * @author Martin Krasser + */ +public interface TypedConsumer2 { + + @consume("direct:default") + public String foo(String body); +} \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer2Impl.java b/akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer2Impl.java new file mode 100644 index 0000000000..8a5fe1e4c9 --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/java/sample/camel/TypedConsumer2Impl.java @@ -0,0 +1,13 @@ +package sample.camel; + +import se.scalablesolutions.akka.actor.TypedActor; + +/** + * @author Martin Krasser + */ +public class TypedConsumer2Impl extends TypedActor implements TypedConsumer2 { + + public String foo(String body) { + return String.format("default: %s", body); + } +} \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/resources/context-boot.xml b/akka-samples/akka-sample-camel/src/main/resources/context-jms.xml similarity index 100% rename from akka-samples/akka-sample-camel/src/main/resources/context-boot.xml rename to akka-samples/akka-sample-camel/src/main/resources/context-jms.xml diff --git a/akka-samples/akka-sample-camel/src/main/resources/context-standalone.xml b/akka-samples/akka-sample-camel/src/main/resources/context-standalone.xml index 36645a936d..1defa6b9fb 100644 --- a/akka-samples/akka-sample-camel/src/main/resources/context-standalone.xml +++ b/akka-samples/akka-sample-camel/src/main/resources/context-standalone.xml @@ -20,6 +20,6 @@ http://camel.apache.org/schema/spring/camel-spring.xsd"> - + diff --git a/akka-samples/akka-sample-camel/src/main/scala/Actors.scala b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala index 275dc6b094..64bdb19dfd 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Actors.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala @@ -115,16 +115,20 @@ class PublisherBridge(uri: String, publisher: ActorRef) extends Actor with Consu class HttpConsumer(producer: ActorRef) extends Actor with Consumer { def endpointUri = "jetty:http://0.0.0.0:8875/" - + protected def receive = { - // only keep Exchange.HTTP_PATH message header (which needed by bridge endpoint) - case msg: Message => producer forward msg.setHeaders(msg.headers(Set(Exchange.HTTP_PATH))) + case msg => producer forward msg } } class HttpProducer(transformer: ActorRef) extends Actor with Producer { def endpointUri = "jetty://http://akkasource.org/?bridgeEndpoint=true" + override protected def receiveBeforeProduce = { + // only keep Exchange.HTTP_PATH message header (which needed by bridge endpoint) + case msg: Message => msg.setHeaders(msg.headers(Set(Exchange.HTTP_PATH))) + } + override protected def receiveAfterProduce = { // do not reply but forward result to transformer case msg => transformer forward msg diff --git a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala index 3894c24cca..98c7c34b7e 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala @@ -7,7 +7,7 @@ import org.apache.camel.spring.spi.ApplicationContextRegistry import org.springframework.context.support.ClassPathXmlApplicationContext import se.scalablesolutions.akka.actor.Actor._ -import se.scalablesolutions.akka.actor.{ActiveObject, Supervisor} +import se.scalablesolutions.akka.actor.{TypedActor, Supervisor} import se.scalablesolutions.akka.camel.CamelContextManager import se.scalablesolutions.akka.config.ScalaConfig._ @@ -16,16 +16,6 @@ import se.scalablesolutions.akka.config.ScalaConfig._ */ class Boot { - // ----------------------------------------------------------------------- - // Create CamelContext with Spring-based registry and custom route builder - // ----------------------------------------------------------------------- - - val context = new ClassPathXmlApplicationContext("/context-boot.xml", getClass) - val registry = new ApplicationContextRegistry(context) - - CamelContextManager.init(new DefaultCamelContext(registry)) - CamelContextManager.context.addRoutes(new CustomRouteBuilder) - // ----------------------------------------------------------------------- // Basic example // ----------------------------------------------------------------------- @@ -41,9 +31,17 @@ class Boot { // Supervise(actorOf[Consumer2], LifeCycle(Permanent)) :: Nil)) // ----------------------------------------------------------------------- - // Tranformer example + // Custom Camel route example // ----------------------------------------------------------------------- + // Create CamelContext and a Spring-based registry + val context = new ClassPathXmlApplicationContext("/context-jms.xml", getClass) + val registry = new ApplicationContextRegistry(context) + + // Use a custom Camel context and a custom touter builder + CamelContextManager.init(new DefaultCamelContext(registry)) + CamelContextManager.context.addRoutes(new CustomRouteBuilder) + val producer = actorOf[Producer1] val mediator = actorOf(new Transformer(producer)) val consumer = actorOf(new Consumer3(mediator)) @@ -52,12 +50,20 @@ class Boot { mediator.start consumer.start + // ----------------------------------------------------------------------- + // Asynchronous consumer-producer example (Akka homepage transformation) + // ----------------------------------------------------------------------- + + val httpTransformer = actorOf(new HttpTransformer).start + val httpProducer = actorOf(new HttpProducer(httpTransformer)).start + val httpConsumer = actorOf(new HttpConsumer(httpProducer)).start + // ----------------------------------------------------------------------- // Publish subscribe examples // ----------------------------------------------------------------------- // - // Cometd example commented out because camel-cometd is broken in Camel 2.3 + // Cometd example commented out because camel-cometd is broken since Camel 2.3 // //val cometdUri = "cometd://localhost:8111/test/abc?baseResource=file:target" @@ -79,19 +85,11 @@ class Boot { actorOf[Consumer4].start // POSTing "stop" to http://0.0.0.0:8877/camel/stop stops and unpublishes this actor actorOf[Consumer5].start // POSTing any msg to http://0.0.0.0:8877/camel/start starts and published Consumer4 again. - // ----------------------------------------------------------------------- - // Non-blocking consumer-producer example (Akka homepage transformation) - // ----------------------------------------------------------------------- - - val nbResponder = actorOf(new HttpTransformer).start - val nbProducer = actorOf(new HttpProducer(nbResponder)).start - val nbConsumer = actorOf(new HttpConsumer(nbProducer)).start - // ----------------------------------------------------------------------- // Active object example // ----------------------------------------------------------------------- - ActiveObject.newInstance(classOf[ConsumerPojo1]) + TypedActor.newInstance(classOf[TypedConsumer1], classOf[TypedConsumer1Impl]) } /** diff --git a/akka-samples/akka-sample-camel/src/main/scala/ClientApplication.scala b/akka-samples/akka-sample-camel/src/main/scala/ClientApplication.scala index 467d715360..7f671b0e45 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/ClientApplication.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/ClientApplication.scala @@ -1,33 +1,31 @@ package sample.camel import se.scalablesolutions.akka.actor.Actor._ -import se.scalablesolutions.akka.actor.{ActiveObject, Actor, ActorRef} +import se.scalablesolutions.akka.actor.{TypedActor, Actor, ActorRef} import se.scalablesolutions.akka.camel.Message import se.scalablesolutions.akka.remote.RemoteClient /** * @author Martin Krasser */ -object ClientApplication { +object ClientApplication extends Application { // // TODO: completion of example // - def main(args: Array[String]) { - val actor1 = actorOf[RemoteActor1] - val actor2 = RemoteClient.actorFor("remote2", "localhost", 7777) + val actor1 = actorOf[RemoteActor1] + val actor2 = RemoteClient.actorFor("remote2", "localhost", 7777) - val actobj1 = ActiveObject.newRemoteInstance(classOf[RemoteConsumerPojo1], "localhost", 7777) - //val actobj2 = TODO: create reference to server-managed active object (RemoteConsumerPojo2) + val actobj1 = TypedActor.newRemoteInstance( + classOf[RemoteTypedConsumer1], classOf[RemoteTypedConsumer1Impl], "localhost", 7777) + //val actobj2 = TODO: create reference to server-managed typed actor (RemoteTypedConsumer2Impl) - actor1.start + actor1.start - println(actor1 !! Message("actor1")) // activates and publishes actor remotely - println(actor2 !! Message("actor2")) // actor already activated and published remotely - - println(actobj1.foo("x", "y")) // activates and publishes active object methods remotely - // ... - } + println(actor1 !! Message("actor1")) // activates and publishes actor remotely + println(actor2 !! Message("actor2")) // actor already activated and published remotely + println(actobj1.foo("x", "y")) // activates and publishes typed actor methods remotely + // ... } diff --git a/akka-samples/akka-sample-camel/src/main/scala/ServerApplication.scala b/akka-samples/akka-sample-camel/src/main/scala/ServerApplication.scala index 7d90e89720..3eb40aaca9 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/ServerApplication.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/ServerApplication.scala @@ -1,21 +1,20 @@ package sample.camel import se.scalablesolutions.akka.actor.Actor._ -import se.scalablesolutions.akka.camel.CamelService import se.scalablesolutions.akka.remote.RemoteNode +import se.scalablesolutions.akka.camel.CamelServiceManager /** * @author Martin Krasser */ -object ServerApplication { +object ServerApplication extends Application { + import CamelServiceManager._ // // TODO: completion of example // - def main(args: Array[String]) { - val camelService = CamelService.newInstance.load - RemoteNode.start("localhost", 7777) - RemoteNode.register("remote2", actorOf[RemoteActor2].start) - } + startCamelService + RemoteNode.start("localhost", 7777) + RemoteNode.register("remote2", actorOf[RemoteActor2].start) } diff --git a/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala b/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala index 0a7304ba0e..c86295da57 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala @@ -5,79 +5,109 @@ import org.apache.camel.builder.RouteBuilder import org.apache.camel.spring.spi.ApplicationContextRegistry import org.springframework.context.support.ClassPathXmlApplicationContext -import se.scalablesolutions.akka.camel.{CamelService, CamelContextManager} -import se.scalablesolutions.akka.actor.{ActorRegistry, ActiveObject} +import se.scalablesolutions.akka.actor.{Actor, ActorRegistry, TypedActor} +import se.scalablesolutions.akka.camel._ /** * @author Martin Krasser */ -object StandaloneApplication { - def main(args: Array[String]) { - import CamelContextManager.context +object StandaloneApplication extends Application { + import CamelContextManager.context + import CamelServiceManager._ - // 'externally' register active objects - val registry = new SimpleRegistry - registry.put("pojo1", ActiveObject.newInstance(classOf[BeanIntf], new BeanImpl)) - registry.put("pojo2", ActiveObject.newInstance(classOf[BeanImpl])) + // 'externally' register typed actors + val registry = new SimpleRegistry + registry.put("sample", TypedActor.newInstance(classOf[BeanIntf], classOf[BeanImpl])) - // customize CamelContext - CamelContextManager.init(new DefaultCamelContext(registry)) - CamelContextManager.context.addRoutes(new StandaloneApplicationRoute) + // customize CamelContext + CamelContextManager.init(new DefaultCamelContext(registry)) + CamelContextManager.context.addRoutes(new StandaloneApplicationRoute) - // start CamelService - val camelService = CamelService.newInstance.load + startCamelService - // access 'externally' registered active objects - assert("hello msg1" == context.createProducerTemplate.requestBody("direct:test1", "msg1")) - assert("hello msg2" == context.createProducerTemplate.requestBody("direct:test2", "msg2")) + // access 'externally' registered typed actors + assert("hello msg1" == context.createProducerTemplate.requestBody("direct:test", "msg1")) - // 'internally' register active object (requires CamelService) - ActiveObject.newInstance(classOf[ConsumerPojo2]) + // set expectations on upcoming endpoint activation + val activation = service.expectEndpointActivationCount(1) - // internal registration is done in background. Wait a bit ... - Thread.sleep(1000) + // 'internally' register typed actor (requires CamelService) + TypedActor.newInstance(classOf[TypedConsumer2], classOf[TypedConsumer2Impl]) - // access 'internally' (automatically) registered active-objects - // (see @consume annotation value at ConsumerPojo2.foo method) - assert("default: msg3" == context.createProducerTemplate.requestBody("direct:default", "msg3")) + // internal registration is done in background. Wait a bit ... + activation.await - // shutdown CamelService - camelService.unload + // access 'internally' (automatically) registered typed-actors + // (see @consume annotation value at TypedConsumer2.foo method) + assert("default: msg3" == context.createProducerTemplate.requestBody("direct:default", "msg3")) - // shutdown all (internally) created actors - ActorRegistry.shutdownAll - } + stopCamelService + + ActorRegistry.shutdownAll } class StandaloneApplicationRoute extends RouteBuilder { def configure = { - // routes to active objects (in SimpleRegistry) - from("direct:test1").to("active-object:pojo1?method=foo") - from("direct:test2").to("active-object:pojo2?method=foo") + // route to typed actors (in SimpleRegistry) + from("direct:test").to("typed-actor:sample?method=foo") } } -object StandaloneSpringApplication { - def main(args: Array[String]) { - import CamelContextManager._ +object StandaloneSpringApplication extends Application { + import CamelContextManager._ - // load Spring application context - val appctx = new ClassPathXmlApplicationContext("/context-standalone.xml") + // load Spring application context + val appctx = new ClassPathXmlApplicationContext("/context-standalone.xml") - // access 'externally' registered active objects with active-object component - assert("hello msg3" == template.requestBody("direct:test3", "msg3")) + // access 'externally' registered typed actors with typed-actor component + assert("hello msg3" == template.requestBody("direct:test3", "msg3")) - // destroy Spring application context - appctx.close + appctx.close - // shutdown all (internally) created actors - ActorRegistry.shutdownAll - } + ActorRegistry.shutdownAll } class StandaloneSpringApplicationRoute extends RouteBuilder { def configure = { - // routes to active object (in ApplicationContextRegistry) - from("direct:test3").to("active-object:pojo3?method=foo") + // routes to typed actor (in ApplicationContextRegistry) + from("direct:test3").to("typed-actor:ta?method=foo") } } + +object StandaloneJmsApplication extends Application { + import CamelServiceManager._ + + val context = new ClassPathXmlApplicationContext("/context-jms.xml") + val registry = new ApplicationContextRegistry(context) + + // Init CamelContextManager with custom CamelContext + CamelContextManager.init(new DefaultCamelContext(registry)) + + startCamelService + + // Expect two consumer endpoints to be activated + val completion = service.expectEndpointActivationCount(2) + + val jmsUri = "jms:topic:test" + // Wire publisher and consumer using a JMS topic + val jmsSubscriber1 = Actor.actorOf(new Subscriber("jms-subscriber-1", jmsUri)).start + val jmsSubscriber2 = Actor.actorOf(new Subscriber("jms-subscriber-2", jmsUri)).start + val jmsPublisher = Actor.actorOf(new Publisher("jms-publisher", jmsUri)).start + + // wait for the consumer (subscriber) endpoint being activated + completion.await + + // Send 10 messages to via publisher actor + for(i <- 1 to 10) { + jmsPublisher ! ("Akka rocks (%d)" format i) + } + + // Send 10 messages to JMS topic directly + for(i <- 1 to 10) { + CamelContextManager.template.sendBody(jmsUri, "Camel rocks (%d)" format i) + } + + stopCamelService + + ActorRegistry.shutdownAll +} diff --git a/akka-samples/akka-sample-camel/src/test/scala/HttpConcurrencyTest.scala b/akka-samples/akka-sample-camel/src/test/scala/HttpConcurrencyTest.scala new file mode 100644 index 0000000000..1a115c6f76 --- /dev/null +++ b/akka-samples/akka-sample-camel/src/test/scala/HttpConcurrencyTest.scala @@ -0,0 +1,100 @@ +package sample.camel + +import collection.mutable.Set + +import java.util.concurrent.CountDownLatch + +import org.junit._ +import org.scalatest.junit.JUnitSuite + +import se.scalablesolutions.akka.actor.Actor._ +import se.scalablesolutions.akka.actor.{ActorRegistry, ActorRef, Actor} +import se.scalablesolutions.akka.camel._ +import se.scalablesolutions.akka.camel.CamelServiceManager._ +import se.scalablesolutions.akka.routing.CyclicIterator +import se.scalablesolutions.akka.routing.Routing._ + +/** + * @author Martin Krasser + */ +@Ignore // do not run concurrency test by default +class HttpConcurrencyTest extends JUnitSuite { + import HttpConcurrencyTest._ + + @Test def shouldProcessMessagesConcurrently = { + val num = 50 + val latch1 = new CountDownLatch(num) + val latch2 = new CountDownLatch(num) + val latch3 = new CountDownLatch(num) + val client1 = actorOf(new HttpClientActor("client1", latch1)).start + val client2 = actorOf(new HttpClientActor("client2", latch2)).start + val client3 = actorOf(new HttpClientActor("client3", latch3)).start + for (i <- 1 to num) { + client1 ! Message("client1", Map(Message.MessageExchangeId -> i)) + client2 ! Message("client2", Map(Message.MessageExchangeId -> i)) + client3 ! Message("client3", Map(Message.MessageExchangeId -> i)) + } + latch1.await + latch2.await + latch3.await + assert(num == (client1 !! "getCorrelationIdCount").as[Int].get) + assert(num == (client2 !! "getCorrelationIdCount").as[Int].get) + assert(num == (client3 !! "getCorrelationIdCount").as[Int].get) + } +} + +object HttpConcurrencyTest { + @BeforeClass + def beforeClass = { + startCamelService + + val workers = for (i <- 1 to 8) yield actorOf[HttpServerWorker].start + val balancer = loadBalancerActor(new CyclicIterator(workers.toList)) + + val completion = service.expectEndpointActivationCount(1) + val server = actorOf(new HttpServerActor(balancer)).start + completion.await + } + + @AfterClass + def afterClass = { + stopCamelService + ActorRegistry.shutdownAll + } + + class HttpClientActor(label: String, latch: CountDownLatch) extends Actor with Producer { + def endpointUri = "jetty:http://0.0.0.0:8855/echo" + var correlationIds = Set[Any]() + + override protected def receive = { + case "getCorrelationIdCount" => self.reply(correlationIds.size) + case msg => super.receive(msg) + } + + override protected def receiveAfterProduce = { + case msg: Message => { + val corr = msg.headers(Message.MessageExchangeId) + val body = msg.bodyAs[String] + correlationIds += corr + assert(label == body) + latch.countDown + print(".") + } + } + } + + class HttpServerActor(balancer: ActorRef) extends Actor with Consumer { + def endpointUri = "jetty:http://0.0.0.0:8855/echo" + var counter = 0 + + def receive = { + case msg => balancer forward msg + } + } + + class HttpServerWorker extends Actor { + protected def receive = { + case msg => self.reply(msg) + } + } +} diff --git a/akka-samples/akka-sample-chat/README b/akka-samples/akka-sample-chat/README index 475cb60015..fec39724e1 100644 --- a/akka-samples/akka-sample-chat/README +++ b/akka-samples/akka-sample-chat/README @@ -18,8 +18,8 @@ Then to run the sample: - Run 'sbt console' to start up a REPL (interpreter). 4. In the first REPL you get execute: - scala> import sample.chat._ - - scala> import se.scalablesolutions.akka.actor.Actor - - scala> val chatService = Actor.actorOf[ChatService].start + - scala> import se.scalablesolutions.akka.actor.Actor._ + - scala> val chatService = actorOf[ChatService].start 5. In the second REPL you get execute: - scala> import sample.chat._ - scala> Runner.run diff --git a/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkers.scala b/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkers.scala new file mode 100644 index 0000000000..789df176a2 --- /dev/null +++ b/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkers.scala @@ -0,0 +1,139 @@ +package dining.hakkers + +//Akka adaptation of +//http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/ + +import se.scalablesolutions.akka.actor.{Scheduler, ActorRef, Actor} +import se.scalablesolutions.akka.actor.Actor._ +import java.util.concurrent.TimeUnit + +/* + * First we define our messages, they basically speak for themselves + */ +sealed trait DiningHakkerMessage +case class Busy(chopstick: ActorRef) extends DiningHakkerMessage +case class Put(hakker: ActorRef) extends DiningHakkerMessage +case class Take(hakker: ActorRef) extends DiningHakkerMessage +case class Taken(chopstick: ActorRef) extends DiningHakkerMessage +object Eat extends DiningHakkerMessage +object Think extends DiningHakkerMessage + +/* + * A Chopstick is an actor, it can be taken, and put back + */ +class Chopstick(name: String) extends Actor { + self.id = name + + //When a Chopstick is taken by a hakker + //It will refuse to be taken by other hakkers + //But the owning hakker can put it back + def takenBy(hakker: ActorRef): Receive = { + case Take(otherHakker) => + otherHakker ! Busy(self) + case Put(`hakker`) => + become(available) + } + + //When a Chopstick is available, it can be taken by a hakker + def available: Receive = { + case Take(hakker) => + become(takenBy(hakker)) + hakker ! Taken(self) + } + + //A Chopstick begins its existence as available + def receive = available +} + +/* + * A hakker is an awesome dude or dudett who either thinks about hacking or has to eat ;-) + */ +class Hakker(name: String,left: ActorRef, right: ActorRef) extends Actor { + self.id = name + + //When a hakker is thinking it can become hungry + //and try to pick up its chopsticks and eat + def thinking: Receive = { + case Eat => + become(hungry) + left ! Take(self) + right ! Take(self) + } + + //When a hakker is hungry it tries to pick up its chopsticks and eat + //When it picks one up, it goes into wait for the other + //If the hakkers first attempt at grabbing a chopstick fails, + //it starts to wait for the response of the other grab + def hungry: Receive = { + case Taken(`left`) => + become(waiting_for(right,left)) + case Taken(`right`) => + become(waiting_for(left,right)) + case Busy(chopstick) => + become(denied_a_chopstick) + } + + //When a hakker is waiting for the last chopstick it can either obtain it + //and start eating, or the other chopstick was busy, and the hakker goes + //back to think about how he should obtain his chopsticks :-) + def waiting_for(chopstickToWaitFor: ActorRef, otherChopstick: ActorRef): Receive = { + case Taken(`chopstickToWaitFor`) => + log.info("%s has picked up %s and %s, and starts to eat",name,left.id,right.id) + become(eating) + Scheduler.scheduleOnce(self,Think,5,TimeUnit.SECONDS) + + case Busy(chopstick) => + become(thinking) + otherChopstick ! Put(self) + self ! Eat + } + + //When the results of the other grab comes back, + //he needs to put it back if he got the other one. + //Then go back and think and try to grab the chopsticks again + def denied_a_chopstick: Receive = { + case Taken(chopstick) => + become(thinking) + chopstick ! Put(self) + self ! Eat + case Busy(chopstick) => + become(thinking) + self ! Eat + } + + //When a hakker is eating, he can decide to start to think, + //then he puts down his chopsticks and starts to think + def eating: Receive = { + case Think => + become(thinking) + left ! Put(self) + right ! Put(self) + log.info("%s puts down his chopsticks and starts to think",name) + Scheduler.scheduleOnce(self,Eat,5,TimeUnit.SECONDS) + } + + //All hakkers start in a non-eating state + def receive = { + case Think => + log.info("%s starts to think",name) + become(thinking) + Scheduler.scheduleOnce(self,Eat,5,TimeUnit.SECONDS) + } +} + +/* + * Alright, here's our test-harness + */ +object DiningHakkers { + def run { + //Create 5 chopsticks + val chopsticks = for(i <- 1 to 5) yield actorOf(new Chopstick("Chopstick "+i)).start + //Create 5 awesome hakkers and assign them their left and right chopstick + val hakkers = for { + (name,i) <- List("Ghosh","Bonér","Klang","Krasser","Manie").zipWithIndex + } yield actorOf(new Hakker(name,chopsticks(i),chopsticks((i+1) % 5))).start + + //Signal all hakkers that they should start thinking, and watch the show + hakkers.foreach(_ ! Think) + } +} diff --git a/akka-samples/akka-sample-lift/src/main/scala/akka/SimpleService.scala b/akka-samples/akka-sample-lift/src/main/scala/akka/SimpleService.scala index 7557404da9..d5358a7d89 100644 --- a/akka-samples/akka-sample-lift/src/main/scala/akka/SimpleService.scala +++ b/akka-samples/akka-sample-lift/src/main/scala/akka/SimpleService.scala @@ -1,35 +1,24 @@ package sample.lift -import se.scalablesolutions.akka.actor.{Transactor, Actor} +import se.scalablesolutions.akka.actor._ +import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.stm.TransactionalMap import se.scalablesolutions.akka.persistence.cassandra.CassandraStorage -import Actor._ - +import scala.xml.Node import java.lang.Integer import javax.ws.rs.{GET, Path, Produces} import java.nio.ByteBuffer +import net.liftweb.http._ +import net.liftweb.http.rest._ -/** - * Try service out by invoking (multiple times): - *

- * curl http://localhost:9998/liftcount
- * 
- * Or browse to the URL from a web browser. - */ -@Path("/liftcount") -class SimpleService extends Transactor { - case object Tick +class SimpleServiceActor extends Transactor { private val KEY = "COUNTER" private var hasStartedTicking = false private lazy val storage = TransactionalMap[String, Integer]() - @GET - @Produces(Array("text/html")) - def count = (self !! Tick).getOrElse(

Error in counter

) - def receive = { - case Tick => if (hasStartedTicking) { + case "Tick" => if (hasStartedTicking) { val counter = storage.get(KEY).get.asInstanceOf[Integer].intValue storage.put(KEY, new Integer(counter + 1)) self.reply(

Tick: {counter + 1}

) @@ -41,27 +30,14 @@ class SimpleService extends Transactor { } } -/** - * Try service out by invoking (multiple times): - *
- * curl http://localhost:9998/persistentliftcount
- * 
- * Or browse to the URL from a web browser. - */ -@Path("/persistentliftcount") -class PersistentSimpleService extends Transactor { +class PersistentServiceActor extends Transactor { - case object Tick private val KEY = "COUNTER" private var hasStartedTicking = false private lazy val storage = CassandraStorage.newMap - @GET - @Produces(Array("text/html")) - def count = (self !! Tick).getOrElse(

Error in counter

) - def receive = { - case Tick => if (hasStartedTicking) { + case "Tick" => if (hasStartedTicking) { val bytes = storage.get(KEY.getBytes).get val counter = ByteBuffer.wrap(bytes).getInt storage.put(KEY.getBytes, ByteBuffer.allocate(4).putInt(counter + 1).array) @@ -73,3 +49,46 @@ class PersistentSimpleService extends Transactor { } } } + + +/** + * Try service out by invoking (multiple times): + *
+ * curl http://localhost:8080/liftcount
+ * 
+ * Or browse to the URL from a web browser. + */ + +object SimpleRestService extends RestHelper { + serve { + case Get("liftcount" :: _, req) => + //Fetch the first actor of type SimpleServiceActor + //Send it the "Tick" message and expect a Node back + val result = for( a <- ActorRegistry.actorFor[SimpleServiceActor]; + r <- (a !! "Tick").as[Node] ) yield r + + //Return either the resulting NodeSeq or a default one + (result getOrElse

Error in counter

).asInstanceOf[Node] + } +} + + +/** + * Try service out by invoking (multiple times): + *
+ * curl http://localhost:8080/persistentliftcount
+ * 
+ * Or browse to the URL from a web browser. + */ + object PersistentRestService extends RestHelper { + serve { + case Get("persistentliftcount" :: _, req) => + //Fetch the first actor of type SimpleServiceActor + //Send it the "Tick" message and expect a Node back + val result = for( a <- ActorRegistry.actorFor[PersistentServiceActor]; + r <- (a !! "Tick").as[Node] ) yield r + + //Return either the resulting NodeSeq or a default one + (result getOrElse

Error in counter

).asInstanceOf[Node] + } + } diff --git a/akka-samples/akka-sample-lift/src/main/scala/bootstrap/liftweb/Boot.scala b/akka-samples/akka-sample-lift/src/main/scala/bootstrap/liftweb/Boot.scala index 0f4a0e9020..2e56a5857a 100644 --- a/akka-samples/akka-sample-lift/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/akka-samples/akka-sample-lift/src/main/scala/bootstrap/liftweb/Boot.scala @@ -13,7 +13,7 @@ import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.util.Logging -import sample.lift.{PersistentSimpleService, SimpleService} +import sample.lift._ /** * A class that's instantiated early and run. It allows the application @@ -35,6 +35,8 @@ class Boot extends Logging { true } } + LiftRules.statelessDispatchTable.append(SimpleRestService) + LiftRules.statelessDispatchTable.append(PersistentRestService) LiftRules.passNotFoundToChain = true @@ -42,10 +44,10 @@ class Boot extends Logging { SupervisorConfig( RestartStrategy(OneForOne, 3, 100, List(classOf[Exception])), Supervise( - actorOf[SimpleService], + actorOf[SimpleServiceActor], LifeCycle(Permanent)) :: Supervise( - actorOf[PersistentSimpleService], + actorOf[PersistentServiceActor], LifeCycle(Permanent)) :: Nil)) factory.newInstance.start diff --git a/akka-samples/akka-sample-lift/src/main/webapp/WEB-INF/web.xml b/akka-samples/akka-sample-lift/src/main/webapp/WEB-INF/web.xml index 23348604bb..3a1b672cec 100644 --- a/akka-samples/akka-sample-lift/src/main/webapp/WEB-INF/web.xml +++ b/akka-samples/akka-sample-lift/src/main/webapp/WEB-INF/web.xml @@ -13,7 +13,7 @@ AkkaServlet - se.scalablesolutions.akka.rest.AkkaServlet + se.scalablesolutions.akka.comet.AkkaServlet AkkaServlet diff --git a/akka-samples/akka-sample-osgi/src/main/scala/osgiExample.scala b/akka-samples/akka-sample-osgi/src/main/scala/osgiExample.scala new file mode 100644 index 0000000000..18323d04d3 --- /dev/null +++ b/akka-samples/akka-sample-osgi/src/main/scala/osgiExample.scala @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka +package sample.osgi + +import actor.{ Actor, ActorRegistry } +import actor.Actor._ + +import org.osgi.framework.{ BundleActivator, BundleContext } + +class Activator extends BundleActivator { + + def start(context: BundleContext) { + println("Starting the OSGi example ...") + val echo = actorOf[EchoActor].start + val answer = (echo !! "OSGi example") + println(answer getOrElse "No answer!") + } + + def stop(context: BundleContext) { + ActorRegistry.shutdownAll() + println("Stopped the OSGi example.") + } +} + +class EchoActor extends Actor { + + override def receive = { + case x => self reply x + } +} diff --git a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/Boot.java b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/Boot.java index 60eb4f11af..d9b41cd136 100644 --- a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/Boot.java +++ b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/Boot.java @@ -4,21 +4,23 @@ package sample.rest.java; -import se.scalablesolutions.akka.config.ActiveObjectConfigurator; +import se.scalablesolutions.akka.config.TypedActorConfigurator; import static se.scalablesolutions.akka.config.JavaConfig.*; public class Boot { - public final static ActiveObjectConfigurator configurator = new ActiveObjectConfigurator(); + public final static TypedActorConfigurator configurator = new TypedActorConfigurator(); static { configurator.configure( new RestartStrategy(new OneForOne(), 3, 5000, new Class[]{Exception.class}), new Component[] { new Component( SimpleService.class, + SimpleServiceImpl.class, new LifeCycle(new Permanent()), 1000), new Component( PersistentSimpleService.class, + PersistentSimpleServiceImpl.class, new LifeCycle(new Permanent()), 1000) }).supervise(); diff --git a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/PersistentSimpleService.java b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/PersistentSimpleService.java index 1108fcdb63..67368ceedd 100644 --- a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/PersistentSimpleService.java +++ b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/PersistentSimpleService.java @@ -4,42 +4,6 @@ package sample.rest.java; -import se.scalablesolutions.akka.actor.annotation.transactionrequired; -import se.scalablesolutions.akka.actor.annotation.prerestart; -import se.scalablesolutions.akka.actor.annotation.postrestart; -import se.scalablesolutions.akka.persistence.common.PersistentMap; -import se.scalablesolutions.akka.persistence.cassandra.CassandraStorage; - -import java.nio.ByteBuffer; - -@transactionrequired -public class PersistentSimpleService { - private String KEY = "COUNTER"; - - private boolean hasStartedTicking = false; - private PersistentMap storage; - - public String count() { - if (storage == null) storage = CassandraStorage.newMap(); - if (!hasStartedTicking) { - storage.put(KEY.getBytes(), ByteBuffer.allocate(4).putInt(0).array()); - hasStartedTicking = true; - return "Tick: 0\n"; - } else { - byte[] bytes = (byte[])storage.get(KEY.getBytes()).get(); - int counter = ByteBuffer.wrap(bytes).getInt(); - storage.put(KEY.getBytes(), ByteBuffer.allocate(4).putInt(counter + 1).array()); - return "Tick: " + counter + "\n"; - } - } - - @prerestart - public void preRestart() { - System.out.println("Prepare for restart by supervisor"); - } - - @postrestart - public void postRestart() { - System.out.println("Reinitialize after restart by supervisor"); - } +public interface PersistentSimpleService { + public String count(); } \ No newline at end of file diff --git a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/PersistentSimpleServiceImpl.java b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/PersistentSimpleServiceImpl.java new file mode 100644 index 0000000000..2b83f4acbb --- /dev/null +++ b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/PersistentSimpleServiceImpl.java @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package sample.rest.java; + +import se.scalablesolutions.akka.actor.TypedTransactor; +import se.scalablesolutions.akka.persistence.common.PersistentMap; +import se.scalablesolutions.akka.persistence.cassandra.CassandraStorage; + +import java.nio.ByteBuffer; + +public class PersistentSimpleServiceImpl extends TypedTransactor implements PersistentSimpleService { + private String KEY = "COUNTER"; + + private boolean hasStartedTicking = false; + private PersistentMap storage; + + public String count() { + if (storage == null) storage = CassandraStorage.newMap(); + if (!hasStartedTicking) { + storage.put(KEY.getBytes(), ByteBuffer.allocate(4).putInt(0).array()); + hasStartedTicking = true; + return "Tick: 0\n"; + } else { + byte[] bytes = (byte[])storage.get(KEY.getBytes()).get(); + int counter = ByteBuffer.wrap(bytes).getInt(); + storage.put(KEY.getBytes(), ByteBuffer.allocate(4).putInt(counter + 1).array()); + return "Tick: " + counter + "\n"; + } + } + + @Override + public void preRestart(Throwable cause) { + System.out.println("Prepare for restart by supervisor"); + } + + @Override + public void postRestart(Throwable cause) { + System.out.println("Reinitialize after restart by supervisor"); + } +} \ No newline at end of file diff --git a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/Receiver.java b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/Receiver.java index 06631659a2..91654e7c15 100644 --- a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/Receiver.java +++ b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/Receiver.java @@ -4,17 +4,6 @@ package sample.rest.java; -import javax.ws.rs.Path; -import javax.ws.rs.GET; -import javax.ws.rs.Produces; - -import se.scalablesolutions.akka.actor.ActiveObject; -import se.scalablesolutions.akka.actor.ActiveObjectContext; - -public class Receiver { - private ActiveObjectContext context = null; - public SimpleService receive() { - System.out.println("------ RECEIVE"); - return (SimpleService) context.getSender(); - } +public interface Receiver { + SimpleService get(); } diff --git a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/ReceiverImpl.java b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/ReceiverImpl.java new file mode 100644 index 0000000000..4a200dfa77 --- /dev/null +++ b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/ReceiverImpl.java @@ -0,0 +1,13 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package sample.rest.java; + +import se.scalablesolutions.akka.actor.TypedActor; + +public class ReceiverImpl extends TypedActor implements Receiver { + public SimpleService get() { + return (SimpleService) getContext().getSender(); + } +} diff --git a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/SimpleService.java b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/SimpleService.java index 097ba810b5..8055b3383a 100644 --- a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/SimpleService.java +++ b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/SimpleService.java @@ -4,43 +4,6 @@ package sample.rest.java; -import se.scalablesolutions.akka.actor.ActiveObject; -import se.scalablesolutions.akka.actor.ActiveObjectContext; -import se.scalablesolutions.akka.actor.annotation.transactionrequired; -import se.scalablesolutions.akka.actor.annotation.prerestart; -import se.scalablesolutions.akka.actor.annotation.postrestart; -import se.scalablesolutions.akka.stm.TransactionalMap; - -@transactionrequired -public class SimpleService { - private String KEY = "COUNTER"; - - private boolean hasStartedTicking = false; - private TransactionalMap storage; - private Receiver receiver = ActiveObject.newInstance(Receiver.class); - - public String count() { - if (storage == null) storage = new TransactionalMap(); - if (!hasStartedTicking) { - storage.put(KEY, 0); - hasStartedTicking = true; - return "Tick: 0\n"; - } else { - // Grabs the sender address and returns it - //SimpleService sender = receiver.receive(); - int counter = (Integer)storage.get(KEY).get() + 1; - storage.put(KEY, counter); - return "Tick: " + counter + "\n"; - } - } - - @prerestart - public void preRestart() { - System.out.println("Prepare for restart by supervisor"); - } - - @postrestart - public void postRestart() { - System.out.println("Reinitialize after restart by supervisor"); - } +public interface SimpleService { + public String count(); } \ No newline at end of file diff --git a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/SimpleServiceImpl.java b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/SimpleServiceImpl.java new file mode 100644 index 0000000000..27008de212 --- /dev/null +++ b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/SimpleServiceImpl.java @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package sample.rest.java; + +import se.scalablesolutions.akka.actor.TypedActor; +import se.scalablesolutions.akka.actor.TypedTransactor; +import se.scalablesolutions.akka.stm.TransactionalMap; + +public class SimpleServiceImpl extends TypedTransactor implements SimpleService { + private String KEY = "COUNTER"; + + private boolean hasStartedTicking = false; + private TransactionalMap storage; + private Receiver receiver = TypedActor.newInstance(Receiver.class, ReceiverImpl.class); + + public String count() { + if (storage == null) storage = new TransactionalMap(); + if (!hasStartedTicking) { + storage.put(KEY, 0); + hasStartedTicking = true; + return "Tick: 0\n"; + } else { + // Grabs the sender address and returns it + //SimpleService sender = receiver.receive(); + int counter = (Integer)storage.get(KEY).get() + 1; + storage.put(KEY, counter); + return "Tick: " + counter + "\n"; + } + } + + @Override + public void preRestart(Throwable cause) { + System.out.println("Prepare for restart by supervisor"); + } + + @Override + public void postRestart(Throwable cause) { + System.out.println("Reinitialize after restart by supervisor"); + } +} \ No newline at end of file diff --git a/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala b/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala index fc96bba182..c3b71a3fdf 100644 --- a/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala +++ b/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala @@ -16,7 +16,7 @@ import java.lang.Integer import java.nio.ByteBuffer import javax.ws.rs.core.MultivaluedMap import javax.ws.rs.{GET, POST, Path, Produces, WebApplicationException, Consumes,PathParam} -import se.scalablesolutions.akka.actor.ActorRegistry.actorsFor +import se.scalablesolutions.akka.actor.ActorRegistry.actorFor import org.atmosphere.annotation.{Broadcast, Suspend,Cluster} import org.atmosphere.util.XSSHtmlFilter import org.atmosphere.cpr.{Broadcaster, BroadcastFilter} @@ -53,7 +53,7 @@ class SimpleService { def count = { //Fetch the first actor of type SimpleServiceActor //Send it the "Tick" message and expect a NodeSeq back - val result = for{a <- actorsFor(classOf[SimpleServiceActor]).headOption + val result = for{a <- actorFor[SimpleServiceActor] r <- (a !! "Tick").as[NodeSeq]} yield r //Return either the resulting NodeSeq or a default one result getOrElse Error in counter @@ -108,7 +108,7 @@ class PersistentSimpleService { def count = { //Fetch the first actor of type PersistentSimpleServiceActor //Send it the "Tick" message and expect a NodeSeq back - val result = for{a <- actorsFor(classOf[PersistentSimpleServiceActor]).headOption + val result = for{a <- actorFor[PersistentSimpleServiceActor] r <- (a !! "Tick").as[NodeSeq]} yield r //Return either the resulting NodeSeq or a default one result getOrElse Error in counter @@ -155,7 +155,7 @@ class Chat { val msg = ChatMsg(form.getFirst("name"),form.getFirst("action"),form.getFirst("message")) //Fetch the first actor of type ChatActor //Send it the "Tick" message and expect a NodeSeq back - val result = for{a <- actorsFor(classOf[ChatActor]).headOption + val result = for{a <- actorFor[ChatActor] r <- (a !! msg).as[String]} yield r //Return either the resulting String or a default one result getOrElse "System__error" diff --git a/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala b/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala index e5c8029eb8..02af6174c6 100644 --- a/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala +++ b/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala @@ -10,7 +10,7 @@ import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.util.Logging import se.scalablesolutions.akka.security.{BasicAuthenticationActor,BasicCredentials,SpnegoAuthenticationActor,DigestAuthenticationActor, UserInfo} import se.scalablesolutions.akka.stm.TransactionalMap -import se.scalablesolutions.akka.actor.ActorRegistry.actorsFor +import se.scalablesolutions.akka.actor.ActorRegistry.actorFor class Boot { val factory = SupervisorFactory( @@ -122,7 +122,7 @@ class SecureTickService { def tick = { //Fetch the first actor of type PersistentSimpleServiceActor //Send it the "Tick" message and expect a NdeSeq back - val result = for{a <- actorsFor(classOf[SecureTickActor]).headOption + val result = for{a <- actorFor[SecureTickActor] r <- (a !! "Tick").as[Integer]} yield r //Return either the resulting NodeSeq or a default one result match { 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/main/resources/se/scalablesolutions/akka/spring/foo/supervisor-config.xml b/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/supervisor-config.xml deleted file mode 100644 index 9d35a40742..0000000000 --- a/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/supervisor-config.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - java.io.IOException - java.lang.NullPointerException - - - - - - - - - - - - - - - java.lang.Exception - - - - - - - - - - - - - - - java.lang.Exception - - - - - - - - - - - - - - - - - java.lang.Exception - - - - - - - - - - java.lang.IOException - - - - - - - - - \ 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/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/test-config.xml deleted file mode 100644 index 665d03a05e..0000000000 --- a/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/test-config.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - java.io.IOException - java.lang.NullPointerException - - - - - - - - - - - - \ No newline at end of file diff --git a/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/ActiveObjectConfigurationTest.java b/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/ActiveObjectConfigurationTest.java deleted file mode 100644 index 7cc691f3e3..0000000000 --- a/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/ActiveObjectConfigurationTest.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 active objects and supervisor configuration. - * @author michaelkober - */ -public class ActiveObjectConfigurationTest { - - private ApplicationContext context = null; - - @Before - public void setUp() { - context = new ClassPathXmlApplicationContext("se/scalablesolutions/akka/spring/foo/test-config.xml"); - } - - /** - * Tests that the <akka:active-object/> 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-active-object")); - assertTrue(beanFactory.containsBeanDefinition("remote-active-object")); - assertTrue(beanFactory.containsBeanDefinition("supervision1")); - assertTrue(beanFactory.containsBeanDefinition("dispatcher1")); - } - - @Test - public void testSimpleActiveObject() { - MyPojo myPojo = (MyPojo) context.getBean("simple-active-object"); - String msg = myPojo.getFoo(); - msg += myPojo.getBar(); - assertEquals("wrong invocation order", "foobar", msg); - } - - @Test(expected = FutureTimeoutException.class) - public void testSimpleActiveObject_Timeout() { - MyPojo myPojo = (MyPojo) context.getBean("simple-active-object"); - myPojo.longRunning(); - } - - @Test - public void testSimpleActiveObject_NoTimeout() { - MyPojo myPojo = (MyPojo) context.getBean("simple-active-object-long-timeout"); - String msg = myPojo.longRunning(); - assertEquals("this took long", msg); - } - - @Test - public void testTransactionalActiveObject() { - MyPojo myPojo = (MyPojo) context.getBean("transactional-active-object"); - String msg = myPojo.getFoo(); - msg += myPojo.getBar(); - assertEquals("wrong invocation order", "foobar", msg); - } - - @Test - public void testRemoteActiveObject() { - 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-active-object"); - assertEquals("foo", myPojo.getFoo()); - } - - -} 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 9f941e4142..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("active-object-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 659433cb9f..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.ActiveObject; -import se.scalablesolutions.akka.config.ActiveObjectConfigurator; -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 ActiveObjectConfigurator bean from spring context - ActiveObjectConfigurator myConfigurator = (ActiveObjectConfigurator) context - .getBean("supervision1"); - // get ActiveObjects - 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() { - ActiveObjectConfigurator conf = (ActiveObjectConfigurator) 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 = ActiveObject.newInstance(StatefulPojo.class, - 1000, true); - assertTrue("should be inititalized", stateful.isInitialized()); - } - - @Test - public void testSupervisionWithDispatcher() { - ActiveObjectConfigurator myConfigurator = (ActiveObjectConfigurator) context - .getBean("supervision-with-dispatcher"); - // get ActiveObjects - Foo foo = myConfigurator.getInstance(Foo.class); - assertNotNull(foo); - // TODO how to check dispatcher? - } - - @Test - public void testRemoteActiveObject() { - new Thread(new Runnable() { - public void run() { - RemoteNode.start(); - } - }).start(); - try { - Thread.currentThread().sleep(1000); - } catch (Exception e) { - } - Foo instance = ActiveObject.newRemoteInstance(Foo.class, 2000, "localhost", 9999); - System.out.println(instance.foo()); - } - - - @Test - public void testSupervisedRemoteActiveObject() { - new Thread(new Runnable() { - public void run() { - RemoteNode.start(); - } - }).start(); - try { - Thread.currentThread().sleep(1000); - } catch (Exception e) { - } - - ActiveObjectConfigurator conf = new ActiveObjectConfigurator(); - 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/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 6eb0ec48fa..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 @@ -38,9 +38,11 @@ + + @@ -74,6 +76,7 @@ + @@ -105,85 +108,100 @@ - - - - - - Pre restart callback method that is called during restart. - - - - - - - Post restart callback method that is called during restart. - - - - - - - - - - - Shutdown callback method that is called during shut down. - - - - - - - + + - - - + - Name of the target class. + Name of the interface implemented by implementation class. + + + + + + + Name of the implementation class. - default timeout for '!!' invocations + Theh default timeout for '!!' invocations. - Set to true if messages should have REQUIRES_NEW semantics - - - - - - - Interface implemented by target class. + Set this to true if messages should have REQUIRES_NEW semantics. - Lifecycle, permanent or temporary + Defines the lifecycle, can be either 'permanent' or 'temporary'. - + - Supported scopes are singleton and prototype + Supported scopes are 'singleton' and 'prototype'. + + + + + + + + + + + + + + + + + + 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'. @@ -196,10 +214,17 @@ - - + + - + + + + + + + + @@ -211,21 +236,21 @@ - Failover scheme, AllForOne or OneForOne + Failover scheme, can be one of 'AllForOne' or 'OneForOne'. - Maximal number of retries. + Maximal number of restarts. - Timerange for restart. + Time range for maximal number of restart. @@ -235,7 +260,8 @@ - + + @@ -252,8 +278,11 @@ - - + + + + + diff --git a/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala b/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala deleted file mode 100644 index 6f62c5a8c4..0000000000 --- a/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.spring - -import java.beans.PropertyDescriptor -import java.lang.reflect.Method -import javax.annotation.PreDestroy -import javax.annotation.PostConstruct -import reflect.BeanProperty - -import org.springframework.beans.BeanWrapperImpl -import org.springframework.beans.BeanWrapper -import org.springframework.beans.BeanUtils -import org.springframework.beans.factory.BeanFactory -import org.springframework.beans.factory.config.AbstractFactoryBean -import org.springframework.context.{ApplicationContext,ApplicationContextAware} -import org.springframework.util.ReflectionUtils -import org.springframework.util.StringUtils - -import se.scalablesolutions.akka.actor.{ActiveObjectConfiguration, ActiveObject} -import se.scalablesolutions.akka.config.ScalaConfig.{ShutdownCallback, RestartCallbacks} -import se.scalablesolutions.akka.dispatch.MessageDispatcher -import se.scalablesolutions.akka.util.Logging - -/** - * Factory bean for active objects. - * - * @author michaelkober - * @author Johan Rask - * @author Martin Krasser - */ -class ActiveObjectFactoryBean extends AbstractFactoryBean[AnyRef] with Logging with ApplicationContextAware { - import StringReflect._ - import AkkaSpringConfigurationTags._ - - @BeanProperty var target: String = "" - @BeanProperty var timeout: Long = _ - @BeanProperty var interface: String = "" - @BeanProperty var transactional: Boolean = false - @BeanProperty var pre: String = "" - @BeanProperty var post: String = "" - @BeanProperty var shutdown: String = "" - @BeanProperty var host: String = "" - @BeanProperty var port: Int = _ - @BeanProperty var lifecycle: String = "" - @BeanProperty var dispatcher: DispatcherProperties = _ - @BeanProperty var scope:String = VAL_SCOPE_SINGLETON - @BeanProperty var property:PropertyEntries = _ - @BeanProperty var applicationContext:ApplicationContext = _ - - // Holds info about if deps has been set or not. Depends on - // if interface is specified or not. We must set deps on - // target instance if interface is specified - var hasSetDependecies = false - - - override def isSingleton:Boolean = { - if(scope.equals(VAL_SCOPE_SINGLETON)) { - true - } else { - false - } - } - - /* - * @see org.springframework.beans.factory.FactoryBean#getObjectType() - */ - def getObjectType: Class[AnyRef] = try { - target.toClass - } catch { - // required by contract to return null - case e: ClassNotFoundException => null - } - - /* - * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance() - */ - def createInstance: AnyRef = { - var argumentList = "" - if (isRemote) argumentList += "r" - if (hasInterface) argumentList += "i" - if (hasDispatcher) argumentList += "d" - - postConstruct( - setProperties( - create(argumentList))) - - } - - /** - * Stop the active object if it is a singleton. - */ - override def destroyInstance(instance:AnyRef) { - ActiveObject.stop(instance) - } - - /** - * Invokes any method annotated with @PostConstruct - * When interfaces are specified, this method is invoked both on the - * target instance and on the active object, so a developer is free do decide - * where the annotation should be. If no interface is specified it is only invoked - * on the active object - */ - private def postConstruct(ref:AnyRef) : AnyRef = { - // Invoke postConstruct method if any - for(method <- ref.getClass.getMethods) { - if(method.isAnnotationPresent(classOf[PostConstruct])) { - method.invoke(ref) - } - } - ref - } - - - private def setProperties(ref:AnyRef) : AnyRef = { - if(hasSetDependecies) { - return ref - } - - log.debug("Processing properties and dependencies for target class %s",target) - val beanWrapper = new BeanWrapperImpl(ref); - if(ref.isInstanceOf[ApplicationContextAware]) { - log.debug("Setting application context") - beanWrapper.setPropertyValue("applicationContext",applicationContext) - } - for(entry <- property.entryList) { - val propertyDescriptor = BeanUtils.getPropertyDescriptor(ref.getClass,entry.name) - val method = propertyDescriptor.getWriteMethod(); - - if(StringUtils.hasText(entry.ref)) { - log.debug("Setting property %s with bean ref %s using method %s", - entry.name,entry.ref,method.getName) - method.invoke(ref,getBeanFactory().getBean(entry.ref)) - } else if(StringUtils.hasText(entry.value)) { - log.debug("Setting property %s with value %s using method %s", - entry.name,entry.value,method.getName) - beanWrapper.setPropertyValue(entry.name,entry.value) - } else { - throw new AkkaBeansException("Either property@ref or property@value must be set on property element") - } - } - ref - } - - private[akka] def create(argList : String) : AnyRef = { - if (argList == "r") { - ActiveObject.newInstance(target.toClass, createConfig.makeRemote(host, port)) - } else if (argList == "ri" ) { - ActiveObject.newInstance(interface.toClass, aNewInstance(target.toClass), createConfig.makeRemote(host, port)) - } else if (argList == "rd") { - ActiveObject.newInstance(target.toClass, createConfig.makeRemote(host, port).dispatcher(dispatcherInstance)) - } else if (argList == "rid") { - ActiveObject.newInstance(interface.toClass, aNewInstance(target.toClass), createConfig.makeRemote(host, port).dispatcher(dispatcherInstance)) - } else if (argList == "i") { - ActiveObject.newInstance(interface.toClass, aNewInstance(target.toClass), createConfig) - } else if (argList == "id") { - ActiveObject.newInstance(interface.toClass, aNewInstance(target.toClass), createConfig.dispatcher(dispatcherInstance)) - } else if (argList == "d") { - ActiveObject.newInstance(target.toClass, createConfig.dispatcher(dispatcherInstance)) - } else { - ActiveObject.newInstance(target.toClass, createConfig) - } - } - - - - private[akka] def createConfig: ActiveObjectConfiguration = { - val config = new ActiveObjectConfiguration().timeout(timeout) - if (hasRestartCallbacks) config.restartCallbacks(pre, post) - if (hasShutdownCallback) config.shutdownCallback(shutdown) - if (transactional) config.makeTransactionRequired - config - } - def aNewInstance[T <: AnyRef](clazz: Class[T]) : T = { - var ref = clazz.newInstance().asInstanceOf[T] - postConstruct( - setProperties(ref)) - hasSetDependecies = true - ref - } - - private[akka] def isRemote = (host != null) && (!host.isEmpty) - - private[akka] def hasInterface = (interface != null) && (!interface.isEmpty) - - private[akka] def hasRestartCallbacks = ((pre != null) && !pre.isEmpty) || ((post != null) && !post.isEmpty) - - private[akka] def hasShutdownCallback = ((shutdown != null) && !shutdown.isEmpty) - - private[akka] def hasDispatcher = (dispatcher != null) && (dispatcher.dispatcherType != null) && (!dispatcher.dispatcherType.isEmpty) - - private[akka] def dispatcherInstance : MessageDispatcher = { - import DispatcherFactoryBean._ - createNewInstance(dispatcher) - } -} diff --git a/akka-spring/src/main/scala/ActorFactoryBean.scala b/akka-spring/src/main/scala/ActorFactoryBean.scala new file mode 100644 index 0000000000..11d5274a70 --- /dev/null +++ b/akka-spring/src/main/scala/ActorFactoryBean.scala @@ -0,0 +1,193 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.spring + +import java.beans.PropertyDescriptor +import java.lang.reflect.Method +import javax.annotation.PreDestroy +import javax.annotation.PostConstruct + +import org.springframework.beans.{BeanUtils,BeansException,BeanWrapper,BeanWrapperImpl} +import org.springframework.beans.factory.BeanFactory +import org.springframework.beans.factory.config.AbstractFactoryBean +import org.springframework.context.{ApplicationContext,ApplicationContextAware} +import org.springframework.util.ReflectionUtils +import org.springframework.util.StringUtils + +import se.scalablesolutions.akka.actor.{ActorRef, AspectInitRegistry, TypedActorConfiguration, TypedActor,Actor} +import se.scalablesolutions.akka.dispatch.MessageDispatcher +import se.scalablesolutions.akka.util.{Logging, Duration} +import scala.reflect.BeanProperty + +/** + * Exception to use when something goes wrong during bean creation. + * + * @author Johan Rask + */ +class AkkaBeansException(message: String, cause:Throwable) extends BeansException(message, cause) { + def this(message: String) = this(message, null) +} + +/** + * Factory bean for typed and untyped actors. + * + * @author michaelkober + * @author Johan Rask + * @author Martin Krasser + * @author Jonas Bonér + */ +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 = _ + @BeanProperty var transactional: Boolean = false + @BeanProperty var host: String = "" + @BeanProperty var port: Int = _ + @BeanProperty var lifecycle: String = "" + @BeanProperty var dispatcher: DispatcherProperties = _ + @BeanProperty var scope: String = VAL_SCOPE_SINGLETON + @BeanProperty var property: PropertyEntries = _ + @BeanProperty var applicationContext: ApplicationContext = _ + + // Holds info about if deps has been set or not. Depends on + // if interface is specified or not. We must set deps on + // target instance if interface is specified + var hasSetDependecies = false + + override def isSingleton = scope.equals(VAL_SCOPE_SINGLETON) + + /* + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + def getObjectType: Class[AnyRef] = try { + implementation.toClass + } catch { + // required by contract to return null + case e: IllegalArgumentException => null + } + + /* + * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance() + */ + def createInstance: AnyRef = { + val ref = typed match { + case TYPED_ACTOR_TAG => val typedActor = createTypedInstance() + setProperties(AspectInitRegistry.initFor(typedActor).targetInstance) + typedActor + case UNTYPED_ACTOR_TAG => val untypedActor = createUntypedInstance() + setProperties(untypedActor.actor) + untypedActor + case _ => throw new IllegalArgumentException("Unknown actor type") + } + ref + } + + 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") + + TypedActor.newInstance(interface.toClass, implementation.toClass, createConfig) + } + + /** + * Create an UntypedActor. + */ + private[akka] def createUntypedInstance() : ActorRef = { + 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 = Actor.actorOf(implementation.toClass) + if (timeout > 0) { + actorRef.setTimeout(timeout) + } + if (transactional) { + actorRef.makeTransactionRequired + } + if (isRemote) { + actorRef.makeRemote(host, port) + } + if (hasDispatcher) { + if (dispatcher.dispatcherType != THREAD_BASED){ + actorRef.setDispatcher(dispatcherInstance()) + } else { + actorRef.setDispatcher(dispatcherInstance(Some(actorRef))) + } + } + actorRef + } + + /** + * Stop the typed actor if it is a singleton. + */ + override def destroyInstance(instance: AnyRef) { + typed match { + case TYPED_ACTOR_TAG => TypedActor.stop(instance) + case UNTYPED_ACTOR_TAG => instance.asInstanceOf[ActorRef].stop + } + } + + private def setProperties(ref: AnyRef): AnyRef = { + if (hasSetDependecies) return ref + log.debug("Processing properties and dependencies for implementation class\n\t[%s]", implementation) + val beanWrapper = new BeanWrapperImpl(ref); + if (ref.isInstanceOf[ApplicationContextAware]) { + log.debug("Setting application context") + beanWrapper.setPropertyValue("applicationContext", applicationContext) + } + for (entry <- property.entryList) { + val propertyDescriptor = BeanUtils.getPropertyDescriptor(ref.getClass, entry.name) + val method = propertyDescriptor.getWriteMethod + if (StringUtils.hasText(entry.ref)) { + log.debug("Setting property %s with bean ref %s using method %s", entry.name, entry.ref, method.getName) + method.invoke(ref,getBeanFactory().getBean(entry.ref)) + } else if(StringUtils.hasText(entry.value)) { + log.debug("Setting property %s with value %s using method %s", entry.name, entry.value, method.getName) + beanWrapper.setPropertyValue(entry.name,entry.value) + } else throw new AkkaBeansException("Either property@ref or property@value must be set on property element") + } + ref + } + + + 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 + } + + private[akka] def isRemote = (host != null) && (!host.isEmpty) + + private[akka] def hasDispatcher = + (dispatcher != null) && + (dispatcher.dispatcherType != null) && + (!dispatcher.dispatcherType.isEmpty) + + /** + * Create dispatcher instance with dispatcher properties. + * @param actorRef actorRef for thread based dispatcher + * @return new dispatcher instance + */ + private[akka] def dispatcherInstance(actorRef: Option[ActorRef] = None) : MessageDispatcher = { + import DispatcherFactoryBean._ + if (dispatcher.dispatcherType != THREAD_BASED) { + createNewInstance(dispatcher) + } else { + createNewInstance(dispatcher, actorRef) + } + } +} diff --git a/akka-spring/src/main/scala/ActiveObjectParser.scala b/akka-spring/src/main/scala/ActorParser.scala similarity index 53% rename from akka-spring/src/main/scala/ActiveObjectParser.scala rename to akka-spring/src/main/scala/ActorParser.scala index 8838360a44..69073bd52f 100644 --- a/akka-spring/src/main/scala/ActiveObjectParser.scala +++ b/akka-spring/src/main/scala/ActorParser.scala @@ -10,24 +10,22 @@ import scala.collection.JavaConversions._ import se.scalablesolutions.akka.actor.IllegalActorStateException /** - * Parser trait for custom namespace configuration for active-object. + * Parser trait for custom namespace configuration for typed-actor. * @author michaelkober * @author Johan Rask * @author Martin Krasser */ -trait ActiveObjectParser extends BeanParser with DispatcherParser { +trait ActorParser extends BeanParser with DispatcherParser { import AkkaSpringConfigurationTags._ /** - * Parses the given element and returns a ActiveObjectProperties. + * Parses the given element and returns a TypedActorProperties. * @param element dom element to parse - * @return configuration for the active object + * @return configuration for the typed actor */ - def parseActiveObject(element: Element): ActiveObjectProperties = { - val objectProperties = new ActiveObjectProperties() + def parseActor(element: Element): ActorProperties = { + val objectProperties = new ActorProperties() val remoteElement = DomUtils.getChildElementByTagName(element, REMOTE_TAG); - val restartCallbacksElement = DomUtils.getChildElementByTagName(element, RESTART_CALLBACKS_TAG); - val shutdownCallbackElement = DomUtils.getChildElementByTagName(element, SHUTDOWN_CALLBACK_TAG); val dispatcherElement = DomUtils.getChildElementByTagName(element, DISPATCHER_TAG) val propertyEntries = DomUtils.getChildElementsByTagName(element,PROPERTYENTRY_TAG) @@ -36,40 +34,29 @@ trait ActiveObjectParser extends BeanParser with DispatcherParser { objectProperties.port = mandatory(remoteElement, PORT).toInt } - if (restartCallbacksElement != null) { - objectProperties.preRestart = restartCallbacksElement.getAttribute(PRE_RESTART) - objectProperties.postRestart = restartCallbacksElement.getAttribute(POST_RESTART) - if ((objectProperties.preRestart.isEmpty) && (objectProperties.preRestart.isEmpty)) { - throw new IllegalActorStateException("At least one of pre or post must be defined.") - } - } - - if (shutdownCallbackElement != null) { - objectProperties.shutdown = shutdownCallbackElement.getAttribute("method") - } - if (dispatcherElement != null) { val dispatcherProperties = parseDispatcher(dispatcherElement) objectProperties.dispatcher = dispatcherProperties } - for(element <- propertyEntries) { - val entry = new PropertyEntry() - entry.name = element.getAttribute("name"); - entry.value = element.getAttribute("value") - entry.ref = element.getAttribute("ref") - objectProperties.propertyEntries.add(entry) + for (element <- propertyEntries) { + val entry = new PropertyEntry + entry.name = element.getAttribute("name"); + entry.value = element.getAttribute("value") + entry.ref = element.getAttribute("ref") + objectProperties.propertyEntries.add(entry) } 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)) throw nfe } - objectProperties.target = mandatory(element, TARGET) + objectProperties.target = mandatory(element, IMPLEMENTATION) objectProperties.transactional = if (element.getAttribute(TRANSACTIONAL).isEmpty) false else element.getAttribute(TRANSACTIONAL).toBoolean if (!element.getAttribute(INTERFACE).isEmpty) { diff --git a/akka-spring/src/main/scala/ActiveObjectProperties.scala b/akka-spring/src/main/scala/ActorProperties.scala similarity index 76% rename from akka-spring/src/main/scala/ActiveObjectProperties.scala rename to akka-spring/src/main/scala/ActorProperties.scala index 0f4b09d559..15c7e61fe0 100644 --- a/akka-spring/src/main/scala/ActiveObjectProperties.scala +++ b/akka-spring/src/main/scala/ActorProperties.scala @@ -8,18 +8,16 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder import AkkaSpringConfigurationTags._ /** - * Data container for active object configuration data. + * Data container for typed actor configuration data. * @author michaelkober * @author Martin Krasser */ -class ActiveObjectProperties { +class ActorProperties { + var typed: String = "" var target: String = "" var timeout: Long = _ var interface: String = "" var transactional: Boolean = false - var preRestart: String = "" - var postRestart: String = "" - var shutdown: String = "" var host: String = "" var port: Int = _ var lifecycle: String = "" @@ -33,13 +31,11 @@ class ActiveObjectProperties { * @param builder bean definition builder */ def setAsProperties(builder: BeanDefinitionBuilder) { + builder.addPropertyValue("typed", typed) builder.addPropertyValue(HOST, host) builder.addPropertyValue(PORT, port) - builder.addPropertyValue(PRE_RESTART, preRestart) - builder.addPropertyValue(POST_RESTART, postRestart) - builder.addPropertyValue(SHUTDOWN, shutdown) builder.addPropertyValue(TIMEOUT, timeout) - builder.addPropertyValue(TARGET, target) + builder.addPropertyValue(IMPLEMENTATION, target) builder.addPropertyValue(INTERFACE, interface) builder.addPropertyValue(TRANSACTIONAL, transactional) builder.addPropertyValue(LIFECYCLE, lifecycle) diff --git a/akka-spring/src/main/scala/AkkaBeansException.scala b/akka-spring/src/main/scala/AkkaBeansException.scala deleted file mode 100644 index 8cbffa86f7..0000000000 --- a/akka-spring/src/main/scala/AkkaBeansException.scala +++ /dev/null @@ -1,14 +0,0 @@ -package se.scalablesolutions.akka.spring - -import org.springframework.beans.BeansException - -/** -* Exception to use when something goes wrong during bean creation -@author Johan Rask -*/ -class AkkaBeansException(errorMsg:String,t:Throwable) extends BeansException(errorMsg,t) { - - def this(errorMsg:String) = { - this(errorMsg,null) - } -} diff --git a/akka-spring/src/main/scala/AkkaNamespaceHandler.scala b/akka-spring/src/main/scala/AkkaNamespaceHandler.scala index 466dbeca30..a478b7b262 100644 --- a/akka-spring/src/main/scala/AkkaNamespaceHandler.scala +++ b/akka-spring/src/main/scala/AkkaNamespaceHandler.scala @@ -12,7 +12,8 @@ import AkkaSpringConfigurationTags._ */ class AkkaNamespaceHandler extends NamespaceHandlerSupport { def init = { - registerBeanDefinitionParser(ACTIVE_OBJECT_TAG, new ActiveObjectBeanDefinitionParser()); + 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 80a9f2e8d0..518727bd4c 100644 --- a/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala +++ b/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala @@ -13,19 +13,19 @@ object AkkaSpringConfigurationTags { // --- TAGS // // top level tags - val ACTIVE_OBJECT_TAG = "active-object" + 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" - // active-object sub tags - val RESTART_CALLBACKS_TAG = "restart-callbacks" - val SHUTDOWN_CALLBACK_TAG = "shutdown-callback" + // actor sub tags val REMOTE_TAG = "remote" // superivision sub tags - val ACTIVE_OBJECTS_TAG = "active-objects" + 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" @@ -38,16 +38,13 @@ object AkkaSpringConfigurationTags { // --- ATTRIBUTES // - // active object attributes + // actor attributes val TIMEOUT = "timeout" - val TARGET = "target" + val IMPLEMENTATION = "implementation" val INTERFACE = "interface" val TRANSACTIONAL = "transactional" val HOST = "host" val PORT = "port" - val PRE_RESTART = "pre" - val POST_RESTART = "post" - val SHUTDOWN = "shutdown" val LIFECYCLE = "lifecycle" val SCOPE = "scope" @@ -60,6 +57,7 @@ object AkkaSpringConfigurationTags { val NAME = "name" val REF = "ref" val TYPE = "type" + val AGGREGATE = "aggregate" // HawtDispatcher // thread pool attributes val QUEUE = "queue" @@ -98,8 +96,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/CamelServiceFactoryBean.scala b/akka-spring/src/main/scala/CamelServiceFactoryBean.scala index 50a3bd748c..b491a38072 100644 --- a/akka-spring/src/main/scala/CamelServiceFactoryBean.scala +++ b/akka-spring/src/main/scala/CamelServiceFactoryBean.scala @@ -5,7 +5,8 @@ package se.scalablesolutions.akka.spring import org.apache.camel.CamelContext import org.springframework.beans.factory.{DisposableBean, InitializingBean, FactoryBean} -import se.scalablesolutions.akka.camel.{CamelContextManager, CamelService} + +import se.scalablesolutions.akka.camel.{CamelContextManager, CamelService, CamelServiceFactory} /** * Factory bean for a {@link CamelService}. @@ -31,14 +32,14 @@ class CamelServiceFactoryBean extends FactoryBean[CamelService] with Initializin if (camelContext ne null) { CamelContextManager.init(camelContext) } - instance = CamelService.newInstance - instance.load + instance = CamelServiceFactory.createCamelService + instance.start } /** * Stops the {@link CamelService} singleton. */ def destroy = { - instance.unload + instance.stop } } diff --git a/akka-spring/src/main/scala/DispatcherBeanDefinitionParser.scala b/akka-spring/src/main/scala/DispatcherBeanDefinitionParser.scala index 826125fcfc..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 ActiveObjectParser 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/DispatcherFactoryBean.scala b/akka-spring/src/main/scala/DispatcherFactoryBean.scala index ac4172a696..06c9994c7f 100644 --- a/akka-spring/src/main/scala/DispatcherFactoryBean.scala +++ b/akka-spring/src/main/scala/DispatcherFactoryBean.scala @@ -7,6 +7,7 @@ import org.springframework.beans.factory.config.AbstractFactoryBean import se.scalablesolutions.akka.config.JavaConfig._ import AkkaSpringConfigurationTags._ import reflect.BeanProperty +import se.scalablesolutions.akka.actor.ActorRef import se.scalablesolutions.akka.dispatch.{ThreadPoolBuilder, Dispatchers, MessageDispatcher} import java.util.concurrent.RejectedExecutionHandler import java.util.concurrent.ThreadPoolExecutor.{DiscardPolicy, DiscardOldestPolicy, CallerRunsPolicy, AbortPolicy} @@ -15,17 +16,30 @@ import java.util.concurrent.ThreadPoolExecutor.{DiscardPolicy, DiscardOldestPoli * Reusable factory method for dispatchers. */ object DispatcherFactoryBean { - def createNewInstance(properties: DispatcherProperties): MessageDispatcher = { + + /** + * factory method for dispatchers + * @param properties dispatcher properties + * @param actorRef actorRef needed for thread based dispatcher + */ + def createNewInstance(properties: DispatcherProperties, actorRef: Option[ActorRef] = None): 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 THREAD_BASED => if (!actorRef.isDefined) { + throw new IllegalArgumentException("Need an ActorRef to create a thread based dispatcher.") + } else { + Dispatchers.newThreadBasedDispatcher(actorRef.get) + } + case HAWT => Dispatchers.newHawtDispatcher(properties.aggregate) case _ => throw new IllegalArgumentException("unknown dispatcher type") } - if ((properties.threadPool != null) && (properties.threadPool.queue != null)) { - var threadPoolBuilder = dispatcher.asInstanceOf[ThreadPoolBuilder] - threadPoolBuilder = properties.threadPool.queue match { + // build threadpool + if ((properties.threadPool != null) && (properties.threadPool.queue != null)) { + var threadPoolBuilder = dispatcher.asInstanceOf[ThreadPoolBuilder] + threadPoolBuilder = properties.threadPool.queue match { case VAL_BOUNDED_ARRAY_BLOCKING_QUEUE => threadPoolBuilder.withNewThreadPoolWithArrayBlockingQueueWithCapacityAndFairness(properties.threadPool.capacity, properties.threadPool.fairness) case VAL_UNBOUNDED_LINKED_BLOCKING_QUEUE => if (properties.threadPool.capacity > -1) threadPoolBuilder.withNewThreadPoolWithLinkedBlockingQueueWithCapacity(properties.threadPool.capacity) diff --git a/akka-spring/src/main/scala/DispatcherParser.scala b/akka-spring/src/main/scala/DispatcherParser.scala index dc156c3d58..c4257230f7 100644 --- a/akka-spring/src/main/scala/DispatcherParser.scala +++ b/akka-spring/src/main/scala/DispatcherParser.scala @@ -28,13 +28,24 @@ 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 != "active-object") { - throw new IllegalArgumentException("Thread based dispatcher must be nested in active-object element!") + val allowedParentNodes = "akka:typed-actor" :: "akka:untyped-actor" :: "typed-actor" :: "untyped-actor" :: Nil + if (!allowedParentNodes.contains(dispatcherElement.getParentNode.getNodeName)) { + throw new IllegalArgumentException("Thread based dispatcher must be nested in 'typed-actor' or 'untyped-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 || @@ -45,7 +56,7 @@ trait DispatcherParser extends BeanParser { properties.threadPool = threadPoolProperties } properties -} + } /** * Parses the given element and returns a ThreadPoolProperties. 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/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/StringReflect.scala b/akka-spring/src/main/scala/StringReflect.scala index 7dda9dba08..9e8cab8172 100644 --- a/akka-spring/src/main/scala/StringReflect.scala +++ b/akka-spring/src/main/scala/StringReflect.scala @@ -1,9 +1,11 @@ /** * Copyright (C) 2009-2010 Scalable Solutions AB */ + package se.scalablesolutions.akka.spring object StringReflect { + /** * Implicit conversion from String to StringReflect. */ @@ -15,10 +17,9 @@ object StringReflect { * @author michaelkober */ class StringReflect(val self: String) { + if (self == null || self == "") throw new IllegalArgumentException("Class name can't be null or empty string [" + self + "]") def toClass[T <: AnyRef]: Class[T] = { val clazz = Class.forName(self) clazz.asInstanceOf[Class[T]] } } - - diff --git a/akka-spring/src/main/scala/SupervisionBeanDefinitionParser.scala b/akka-spring/src/main/scala/SupervisionBeanDefinitionParser.scala index dde14f3cb2..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 ActiveObjectParser { +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 activeObjectsElement = mandatoryElement(element, ACTIVE_OBJECTS_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) - parseActiveObjectList(activeObjectsElement, 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) { @@ -46,10 +56,16 @@ class SupervisionBeanDefinitionParser extends AbstractSingleBeanDefinitionParser builder.addPropertyValue("restartStrategy", restartStrategy) } - private[akka] def parseActiveObjectList(element: Element, builder: BeanDefinitionBuilder) { - val activeObjects = DomUtils.getChildElementsByTagName(element, ACTIVE_OBJECT_TAG).toArray.toList.asInstanceOf[List[Element]] - val activeObjectProperties = activeObjects.map(parseActiveObject(_)) - builder.addPropertyValue("supervised", activeObjectProperties) + private[akka] def parseTypedActorList(element: Element, builder: BeanDefinitionBuilder) { + val typedActors = DomUtils.getChildElementsByTagName(element, TYPED_ACTOR_TAG).toArray.toList.asInstanceOf[List[Element]] + 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 d8c44c3502..39d927a16c 100644 --- a/akka-spring/src/main/scala/SupervisionFactoryBean.scala +++ b/akka-spring/src/main/scala/SupervisionFactoryBean.scala @@ -4,8 +4,10 @@ package se.scalablesolutions.akka.spring import org.springframework.beans.factory.config.AbstractFactoryBean -import se.scalablesolutions.akka.config.ActiveObjectConfigurator +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, Actor} import AkkaSpringConfigurationTags._ import reflect.BeanProperty @@ -14,31 +16,45 @@ import reflect.BeanProperty * Factory bean for supervisor configuration. * @author michaelkober */ -class SupervisionFactoryBean extends AbstractFactoryBean[ActiveObjectConfigurator] { +class SupervisionFactoryBean extends AbstractFactoryBean[AnyRef] { @BeanProperty var restartStrategy: RestartStrategy = _ - @BeanProperty var supervised: List[ActiveObjectProperties] = _ + @BeanProperty var supervised: List[ActorProperties] = _ + @BeanProperty var typed: String = "" /* * @see org.springframework.beans.factory.FactoryBean#getObjectType() */ - def getObjectType: Class[ActiveObjectConfigurator] = classOf[ActiveObjectConfigurator] + def getObjectType: Class[AnyRef] = classOf[AnyRef] /* * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance() */ - def createInstance: ActiveObjectConfigurator = { - val configurator = new ActiveObjectConfigurator() + 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 ActiveObject + * Create configuration for TypedActor */ - private[akka] def createComponent(props: ActiveObjectProperties): 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[ActiveObjectConfigurato } } } + + /** + * 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 actorRef = Actor.actorOf(props.target.toClass) + if (props.timeout > 0) { + actorRef.setTimeout(props.timeout) + } + if (props.transactional) { + actorRef.makeTransactionRequired + } + + val supervise = if (isRemote) { + val remote = new SRemoteAddress(props.host, props.port) + Supervise(actorRef, lifeCycle.transform, remote) + } else { + Supervise(actorRef, lifeCycle.transform) + } + supervise + } } diff --git a/akka-spring/src/main/scala/ActiveObjectBeanDefinitionParser.scala b/akka-spring/src/main/scala/TypedActorBeanDefinitionParser.scala similarity index 77% rename from akka-spring/src/main/scala/ActiveObjectBeanDefinitionParser.scala rename to akka-spring/src/main/scala/TypedActorBeanDefinitionParser.scala index 0189147994..e8e0cef7d4 100644 --- a/akka-spring/src/main/scala/ActiveObjectBeanDefinitionParser.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 ActiveObjectBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with ActiveObjectParser { +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 activeObjectConf = parseActiveObject(element) - activeObjectConf.setAsProperties(builder) + 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[ActiveObjectFactoryBean] + 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/Pojo.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/Pojo.java index 04995b75c8..f588fc777a 100644 --- a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/Pojo.java +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/Pojo.java @@ -2,38 +2,50 @@ package se.scalablesolutions.akka.spring; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; + import javax.annotation.PreDestroy; import javax.annotation.PostConstruct; -public class Pojo implements PojoInf,ApplicationContextAware { +import se.scalablesolutions.akka.actor.*; - private String string; +public class Pojo extends TypedActor implements PojoInf, ApplicationContextAware { - private boolean gotApplicationContext = false; - private boolean postConstructInvoked = false; - - public boolean gotApplicationContext() { - return gotApplicationContext; - } - public void setApplicationContext(ApplicationContext context) { - gotApplicationContext = true; - } + private String stringFromVal; + private String stringFromRef; - public void setString(String s) { - string = s; - } + private boolean gotApplicationContext = false; + private boolean initInvoked = false; + + public boolean gotApplicationContext() { + return gotApplicationContext; + } + + public void setApplicationContext(ApplicationContext context) { + gotApplicationContext = true; + } - public String getString() { - return string; - } - - @PostConstruct - public void create() { - postConstructInvoked = true; - } + public String getStringFromVal() { + return stringFromVal; + } - public boolean isPostConstructInvoked() { - return postConstructInvoked; + public void setStringFromVal(String s) { + stringFromVal = s; + } + + public String getStringFromRef() { + return stringFromRef; + } + + public void setStringFromRef(String s) { + stringFromRef = s; + } + + @Override + public void init() { + initInvoked = true; + } + + public boolean isInitInvoked() { + return initInvoked; + } } - - } diff --git a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/PojoInf.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/PojoInf.java index 70d64245db..9ebf80e89b 100644 --- a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/PojoInf.java +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/PojoInf.java @@ -5,10 +5,9 @@ import javax.annotation.PostConstruct; public interface PojoInf { - public String getString(); - public boolean gotApplicationContext(); - public boolean isPostConstructInvoked(); - - @PostConstruct - public void create(); - } + public String getStringFromVal(); + public String getStringFromRef(); + public boolean gotApplicationContext(); + public boolean isInitInvoked(); + +} diff --git a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/SampleBean.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/SampleBean.java index e8adaa38e7..2828c42bcb 100644 --- a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/SampleBean.java +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/SampleBean.java @@ -1,22 +1,25 @@ package se.scalablesolutions.akka.spring; -import se.scalablesolutions.akka.actor.annotation.shutdown; +import se.scalablesolutions.akka.actor.*; -public class SampleBean { +public class SampleBean extends TypedActor implements SampleBeanIntf { - public boolean down; + private boolean down; public SampleBean() { down = false; } + public boolean down() { + return down; + } + public String foo(String s) { return "hello " + s; } - @shutdown + @Override public void shutdown() { down = true; } - } diff --git a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/SampleBeanIntf.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/SampleBeanIntf.java new file mode 100644 index 0000000000..ec189ecd5f --- /dev/null +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/SampleBeanIntf.java @@ -0,0 +1,6 @@ +package se.scalablesolutions.akka.spring; + +public interface SampleBeanIntf { + public boolean down(); + public String foo(String s); + } diff --git a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/SampleRoute.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/SampleRoute.java index 3865ea1615..5e7e5ea126 100644 --- a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/SampleRoute.java +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/SampleRoute.java @@ -6,7 +6,6 @@ public class SampleRoute extends RouteBuilder { @Override public void configure() throws Exception { - from("direct:test").to("active-object:sample?method=foo"); + from("direct:test").to("typed-actor:sample?method=foo"); } - } 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/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..e447b26a28 --- /dev/null +++ b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/foo/PingActor.java @@ -0,0 +1,69 @@ +package se.scalablesolutions.akka.spring.foo; + +import se.scalablesolutions.akka.actor.UntypedActor; +import se.scalablesolutions.akka.actor.ActorRef; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + + +/** + * test class + */ +public class PingActor extends UntypedActor implements ApplicationContextAware { + + private String stringFromVal; + private String stringFromRef; + + private boolean gotApplicationContext = false; + + + public void setApplicationContext(ApplicationContext context) { + gotApplicationContext = true; + } + + public boolean gotApplicationContext() { + return gotApplicationContext; + } + + public String getStringFromVal() { + return stringFromVal; + } + + public void setStringFromVal(String s) { + stringFromVal = s; + } + + public String getStringFromRef() { + return stringFromRef; + } + + public void setStringFromRef(String s) { + stringFromRef = s; + } + + + 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"); + ActorRef 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/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/src/test/resources/appContext.xml b/akka-spring/src/test/resources/appContext.xml index e9a651b735..02ea1c6733 100644 --- a/akka-spring/src/test/resources/appContext.xml +++ b/akka-spring/src/test/resources/appContext.xml @@ -7,27 +7,37 @@ http://www.akkasource.org/schema/akka http://scalablesolutions.se/akka/akka-0.10.xsd"> - - - + + + - - - + + + + + + + + + - + - - - - diff --git a/akka-spring/src/test/resources/appContextCamelServiceCustom.xml b/akka-spring/src/test/resources/appContextCamelServiceCustom.xml index ba18a325d0..84f442ebf6 100644 --- a/akka-spring/src/test/resources/appContextCamelServiceCustom.xml +++ b/akka-spring/src/test/resources/appContextCamelServiceCustom.xml @@ -20,6 +20,8 @@ http://camel.apache.org/schema/spring/camel-spring.xsd"> - - + 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 53% 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 688d04f377..c02c5b4d14 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,28 @@ http://scalablesolutions.se/akka/akka-0.10.xsd"> + + + + + + + + + + + + + + + + + @@ -69,14 +95,21 @@ http://scalablesolutions.se/akka/akka-0.10.xsd"> java.lang.NullPointerException - - - - - - - + + + + + + diff --git a/akka-spring/src/test/resources/failing-appContext.xml b/akka-spring/src/test/resources/failing-appContext.xml index 1acab0e830..66382056bd 100644 --- a/akka-spring/src/test/resources/failing-appContext.xml +++ b/akka-spring/src/test/resources/failing-appContext.xml @@ -7,15 +7,15 @@ http://www.akkasource.org/schema/akka classpath:se/scalablesolutions/akka/spring/akka-0.10.xsd"> - - - - - - - + + + + + + + \ No newline at end of file diff --git a/akka-spring/src/test/resources/supervisor-config.xml b/akka-spring/src/test/resources/supervisor-config.xml new file mode 100644 index 0000000000..698581d903 --- /dev/null +++ b/akka-spring/src/test/resources/supervisor-config.xml @@ -0,0 +1,120 @@ + + + + + + + java.io.IOException + java.lang.NullPointerException + + + + + + + + + + + + + + java.io.IOException + java.lang.NullPointerException + + + + + + + + + + + + + + + + + java.lang.Exception + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/akka-spring/src/test/resources/typed-actor-config.xml b/akka-spring/src/test/resources/typed-actor-config.xml new file mode 100644 index 0000000000..dfa40f99ad --- /dev/null +++ b/akka-spring/src/test/resources/typed-actor-config.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + java.io.IOException + java.lang.NullPointerException + + + + + + + + + + + \ No newline at end of file diff --git a/akka-spring/src/test/resources/untyped-actor-config.xml b/akka-spring/src/test/resources/untyped-actor-config.xml new file mode 100644 index 0000000000..aea5e86d44 --- /dev/null +++ b/akka-spring/src/test/resources/untyped-actor-config.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/akka-spring/src/test/scala/ActiveObjectFactoryBeanTest.scala b/akka-spring/src/test/scala/ActiveObjectFactoryBeanTest.scala deleted file mode 100644 index 68dac8e97c..0000000000 --- a/akka-spring/src/test/scala/ActiveObjectFactoryBeanTest.scala +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ -package se.scalablesolutions.akka.spring - -import org.scalatest.Spec -import org.scalatest.matchers.ShouldMatchers -import org.scalatest.junit.JUnitRunner -import org.junit.runner.RunWith -import org.springframework.core.io.ResourceEditor -import org.springframework.context.support.ClassPathXmlApplicationContext; - -/** - * Test for ActiveObjectFactoryBean - * @author michaelkober - */ -@RunWith(classOf[JUnitRunner]) -class ActiveObjectFactoryBeanTest extends Spec with ShouldMatchers { - - describe("A ActiveObjectFactoryBean") { - val bean = new ActiveObjectFactoryBean - it("should have java getters and setters for all properties") { - bean.setTarget("java.lang.String") - assert(bean.getTarget == "java.lang.String") - bean.setTimeout(1000) - assert(bean.getTimeout == 1000) - } - - it("should create a remote active object when a host is set") { - bean.setHost("some.host.com"); - assert(bean.isRemote) - } - - it("should create object that implements the given interface") { - bean.setInterface("com.biz.IPojo"); - assert(bean.hasInterface) - } - - it("should create an active object with dispatcher if dispatcher is set") { - val props = new DispatcherProperties() - props.dispatcherType = "executor-based-event-driven" - bean.setDispatcher(props); - assert(bean.hasDispatcher) - } - - it("should return the object type") { - bean.setTarget("java.lang.String") - assert(bean.getObjectType == classOf[String]) - } - - it("should create a proxy of type ResourceEditor") { - val bean = new ActiveObjectFactoryBean() - // we must have a java class here - bean.setTarget("org.springframework.core.io.ResourceEditor") - val entries = new PropertyEntries() - val entry = new PropertyEntry() - entry.name = "source" - entry.value = "sourceBeanIsAString" - entries.add(entry) - bean.setProperty(entries) - assert(bean.getObjectType == classOf[ResourceEditor]) - - // Check that we have injected the depencency correctly - val target:ResourceEditor = bean.createInstance.asInstanceOf[ResourceEditor] - assert(target.getSource === entry.value) - } - - it("should create an application context and verify dependency injection") { - var ctx = new ClassPathXmlApplicationContext("appContext.xml"); - val target:ResourceEditor = ctx.getBean("bean").asInstanceOf[ResourceEditor] - assert(target.getSource === "someString") - - val pojoInf = ctx.getBean("pojoInf").asInstanceOf[PojoInf]; - println("pojoInf = " + pojoInf.getString) - Thread.sleep(200) - assert(pojoInf.isPostConstructInvoked) - assert(pojoInf.getString == "akka rocks") - assert(pojoInf.gotApplicationContext) - } - - it("should stop the created active object when scope is singleton and the context is closed") { - var ctx = new ClassPathXmlApplicationContext("appContext.xml"); - val target = ctx.getBean("bean-singleton").asInstanceOf[SampleBean] - assert(!target.down) - ctx.close - assert(target.down) - } - - it("should not stop the created active object when scope is prototype and the context is closed") { - var ctx = new ClassPathXmlApplicationContext("appContext.xml"); - val target = ctx.getBean("bean-prototype").asInstanceOf[SampleBean] - assert(!target.down) - ctx.close - assert(!target.down) - } - } -} diff --git a/akka-spring/src/test/scala/ActorFactoryBeanTest.scala b/akka-spring/src/test/scala/ActorFactoryBeanTest.scala new file mode 100644 index 0000000000..f765cc3307 --- /dev/null +++ b/akka-spring/src/test/scala/ActorFactoryBeanTest.scala @@ -0,0 +1,113 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ +package se.scalablesolutions.akka.spring + +import se.scalablesolutions.akka.actor.{ActorRegistry, ActorRef} +import se.scalablesolutions.akka.spring.foo.PingActor + +import org.junit.runner.RunWith +import org.springframework.context.support.ClassPathXmlApplicationContext +import org.scalatest.junit.JUnitRunner +import org.scalatest.{BeforeAndAfterAll, Spec} +import org.scalatest.matchers.ShouldMatchers + +/** + * Test for TypedActorFactoryBean + * @author michaelkober + */ +@RunWith(classOf[JUnitRunner]) +class ActorFactoryBeanTest extends Spec with ShouldMatchers with BeforeAndAfterAll { + override protected def afterAll = ActorRegistry.shutdownAll + + describe("A ActorFactoryBean") { + val bean = new ActorFactoryBean + it("should have java getters and setters for all properties") { + bean.setImplementation("java.lang.String") + assert(bean.getImplementation == "java.lang.String") + bean.setTimeout(1000) + assert(bean.getTimeout == 1000) + } + + it("should create a remote typed actor when a host is set") { + bean.setHost("some.host.com"); + assert(bean.isRemote) + } + + it("should create an typed actor with dispatcher if dispatcher is set") { + val props = new DispatcherProperties() + props.dispatcherType = "executor-based-event-driven" + bean.setDispatcher(props); + assert(bean.hasDispatcher) + } + + it("should return the object type") { + bean.setImplementation("java.lang.String") + assert(bean.getObjectType == classOf[String]) + } + + it("should create a proxy of type PojoInf") { + val bean = new ActorFactoryBean() + bean.setInterface("se.scalablesolutions.akka.spring.PojoInf") + bean.setImplementation("se.scalablesolutions.akka.spring.Pojo") + bean.timeout = 1000 + bean.typed = AkkaSpringConfigurationTags.TYPED_ACTOR_TAG + val entries = new PropertyEntries() + val entry = new PropertyEntry() + entry.name = "stringFromVal" + entry.value = "tests rock" + entries.add(entry) + bean.setProperty(entries) + assert(classOf[PojoInf].isAssignableFrom(bean.getObjectType)) + + // Check that we have injected the depencency correctly + val target = bean.createInstance.asInstanceOf[PojoInf] + assert(target.getStringFromVal === entry.value) + } + + it("should create an application context and verify dependency injection for tryped") { + var ctx = new ClassPathXmlApplicationContext("appContext.xml"); + val ta = ctx.getBean("typedActor").asInstanceOf[PojoInf]; + assert(ta.isInitInvoked) + assert(ta.getStringFromVal === "akka rocks") + assert(ta.getStringFromRef === "spring rocks") + assert(ta.gotApplicationContext) + ctx.close + } + + it("should create an application context and verify dependency injection for untyped actors") { + var ctx = new ClassPathXmlApplicationContext("appContext.xml") + val uta = ctx.getBean("untypedActor").asInstanceOf[ActorRef] + val ping = uta.actor.asInstanceOf[PingActor] + assert(ping.getStringFromVal === "akka rocks") + assert(ping.getStringFromRef === "spring rocks") + assert(ping.gotApplicationContext) + ctx.close + } + + it("should stop the created typed actor when scope is singleton and the context is closed") { + var ctx = new ClassPathXmlApplicationContext("appContext.xml"); + val target = ctx.getBean("untypedActor").asInstanceOf[ActorRef] + target.start + assert(target.isRunning) + ctx.close + assert(!target.isRunning) + } + + it("should stop the created untyped actor when scope is singleton and the context is closed") { + var ctx = new ClassPathXmlApplicationContext("appContext.xml"); + val target = ctx.getBean("bean-singleton").asInstanceOf[SampleBeanIntf] + assert(!target.down) + ctx.close + assert(target.down) + } + + it("should not stop the created typed actor when scope is prototype and the context is closed") { + var ctx = new ClassPathXmlApplicationContext("appContext.xml"); + val target = ctx.getBean("bean-prototype").asInstanceOf[SampleBeanIntf] + assert(!target.down) + ctx.close + assert(!target.down) + } + } +} diff --git a/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala b/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala index d260afa44f..e8b0d727c3 100644 --- a/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala +++ b/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala @@ -6,7 +6,7 @@ import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, FeatureSpec} import org.springframework.context.support.ClassPathXmlApplicationContext import se.scalablesolutions.akka.camel.CamelContextManager -import se.scalablesolutions.akka.actor.{ActiveObject, ActorRegistry} +import se.scalablesolutions.akka.actor.{TypedActor, ActorRegistry} class CamelServiceSpringFeatureTest extends FeatureSpec with BeforeAndAfterEach with BeforeAndAfterAll { override protected def beforeAll = { @@ -19,24 +19,23 @@ class CamelServiceSpringFeatureTest extends FeatureSpec with BeforeAndAfterEach feature("start CamelService from Spring application context") { import CamelContextManager._ - - scenario("with a custom CamelContext and access a registered active object") { + scenario("with a custom CamelContext and access a registered typed actor") { val appctx = new ClassPathXmlApplicationContext("/appContextCamelServiceCustom.xml") assert(context.isInstanceOf[SpringCamelContext]) assert("hello sample" === template.requestBody("direct:test", "sample")) appctx.close } - scenario("with a default CamelContext and access a registered active object") { + scenario("with a default CamelContext and access a registered typed actor") { val appctx = new ClassPathXmlApplicationContext("/appContextCamelServiceDefault.xml") // create a custom registry val registry = new SimpleRegistry - registry.put("custom", ActiveObject.newInstance(classOf[SampleBean])) + registry.put("custom", TypedActor.newInstance(classOf[SampleBeanIntf], classOf[SampleBean])) // set custom registry in DefaultCamelContext assert(context.isInstanceOf[DefaultCamelContext]) context.asInstanceOf[DefaultCamelContext].setRegistry(registry) - // access registered active object - assert("hello sample" === template.requestBody("active-object:custom?method=foo", "sample")) + // access registered typed actor + assert("hello sample" === template.requestBody("typed-actor:custom?method=foo", "sample")) appctx.close } } diff --git a/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala b/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala index bd5490a1b0..83c179e29a 100644 --- a/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala +++ b/akka-spring/src/test/scala/DispatcherBeanDefinitionParserTest.scala @@ -15,28 +15,35 @@ 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 = - val props = parser.parseDispatcher(dom(xml).getDocumentElement); + type="executor-based-event-driven" + name="myDispatcher"/> + 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") { 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") @@ -50,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") @@ -69,29 +76,40 @@ 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 ActiveObject") { - 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 = + var props = parser.parseDispatcher(dom(xml).getDocumentElement); + assert(props != null) + assert(props.dispatcherType === "hawt") + assert(props.aggregate === false) } } } diff --git a/akka-spring/src/test/scala/DispatcherSpringFeatureTest.scala b/akka-spring/src/test/scala/DispatcherSpringFeatureTest.scala new file mode 100644 index 0000000000..9fbc2c800e --- /dev/null +++ b/akka-spring/src/test/scala/DispatcherSpringFeatureTest.scala @@ -0,0 +1,144 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ +package se.scalablesolutions.akka.spring + + +import foo.{IMyPojo, MyPojo, PingActor} +import se.scalablesolutions.akka.dispatch._ +import se.scalablesolutions.akka.actor.ActorRef + +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") + assert(dispatcher.aggregate === false) + } + + 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[ActorRef] + assert(actorRef.getActorClassName() === "se.scalablesolutions.akka.spring.foo.PingActor") + actorRef.start() + actorRef.sendOneWay("Hello") + assert(actorRef.getDispatcher.isInstanceOf[ThreadBasedDispatcher]) + } + } + + /** + * 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/SupervisionBeanDefinitionParserTest.scala b/akka-spring/src/test/scala/SupervisionBeanDefinitionParserTest.scala index ffc1f7a95d..fd9ad3e3bd 100644 --- a/akka-spring/src/test/scala/SupervisionBeanDefinitionParserTest.scala +++ b/akka-spring/src/test/scala/SupervisionBeanDefinitionParserTest.scala @@ -26,8 +26,8 @@ class SupervisionBeanDefinitionParserTest extends Spec with ShouldMatchers { val parser = new Parser() val builder = BeanDefinitionBuilder.genericBeanDefinition("foo.bar.Foo") - it("should be able to parse active object configuration") { - val props = parser.parseActiveObject(createActiveObjectElement); + it("should be able to parse typed actor configuration") { + val props = parser.parseActor(createTypedActorElement); assert(props != null) assert(props.timeout == 1000) assert(props.target == "foo.bar.MyPojo") @@ -45,9 +45,9 @@ class SupervisionBeanDefinitionParserTest extends Spec with ShouldMatchers { expect(1000) { strategy.withinTimeRange } } - it("should parse the supervised active objects") { + it("should parse the supervised typed actors") { parser.parseSupervisor(createSupervisorElement, builder); - val supervised = builder.getBeanDefinition.getPropertyValues.getPropertyValue("supervised").getValue.asInstanceOf[List[ActiveObjectProperties]] + val supervised = builder.getBeanDefinition.getPropertyValues.getPropertyValue("supervised").getValue.asInstanceOf[List[ActorProperties]] assert(supervised != null) expect(4) { supervised.length } val iterator = supervised.iterator @@ -59,9 +59,6 @@ class SupervisionBeanDefinitionParserTest extends Spec with ShouldMatchers { expect("foo.bar.Bar") { prop2.target } expect("foo.bar.MyPojo") { prop3.target } expect("foo.bar.MyPojo") { prop4.target } - expect("preRestart") { prop3.preRestart } - expect("postRestart") { prop3.postRestart } - expect("shutdown") { prop4.shutdown } expect("permanent") { prop1.lifecycle } expect("temporary") { prop4.lifecycle } } @@ -75,9 +72,9 @@ class SupervisionBeanDefinitionParserTest extends Spec with ShouldMatchers { } } - private def createActiveObjectElement : Element = { - val xml = dom(xml).getDocumentElement @@ -91,16 +88,16 @@ class SupervisionBeanDefinitionParserTest extends Spec with ShouldMatchers { java.lang.NullPointerException - - - - + + + + - - + + - - + + dom(xml).getDocumentElement } @@ -113,9 +110,9 @@ class SupervisionBeanDefinitionParserTest extends Spec with ShouldMatchers { java.io.IOException - - - + + + dom(xml).getDocumentElement } @@ -124,10 +121,10 @@ class SupervisionBeanDefinitionParserTest extends Spec with ShouldMatchers { val xml = - - - - + + + + dom(xml).getDocumentElement } diff --git a/akka-spring/src/test/scala/SupervisionFactoryBeanTest.scala b/akka-spring/src/test/scala/SupervisionFactoryBeanTest.scala index 7313725202..2d7baf2b3e 100644 --- a/akka-spring/src/test/scala/SupervisionFactoryBeanTest.scala +++ b/akka-spring/src/test/scala/SupervisionFactoryBeanTest.scala @@ -8,7 +8,7 @@ import org.scalatest.matchers.ShouldMatchers import org.scalatest.junit.JUnitRunner import org.junit.runner.RunWith import se.scalablesolutions.akka.config.JavaConfig._ -import se.scalablesolutions.akka.config.ActiveObjectConfigurator +import se.scalablesolutions.akka.config.TypedActorConfigurator private[akka] class Foo @@ -16,10 +16,10 @@ private[akka] class Foo class SupervisionFactoryBeanTest extends Spec with ShouldMatchers { val restartStrategy = new RestartStrategy(new AllForOne(), 3, 1000, Array(classOf[Throwable])) - val activeObjects = List(createActiveObjectProperties("se.scalablesolutions.akka.spring.Foo", 1000L)) + val typedActors = List(createTypedActorProperties("se.scalablesolutions.akka.spring.Foo", 1000L)) - def createActiveObjectProperties(target: String, timeout: Long) : ActiveObjectProperties = { - val properties = new ActiveObjectProperties() + def createTypedActorProperties(target: String, timeout: Long) : ActorProperties = { + val properties = new ActorProperties() properties.target = target properties.timeout = timeout properties @@ -30,12 +30,12 @@ class SupervisionFactoryBeanTest extends Spec with ShouldMatchers { it("should have java getters and setters for all properties") { bean.setRestartStrategy(restartStrategy) assert(bean.getRestartStrategy == restartStrategy) - bean.setSupervised(activeObjects) - assert(bean.getSupervised == activeObjects) + bean.setSupervised(typedActors) + assert(bean.getSupervised == typedActors) } - it("should return the object type ActiveObjectConfigurator") { - assert(bean.getObjectType == classOf[ActiveObjectConfigurator]) + it("should return the object type AnyRef") { + assert(bean.getObjectType == classOf[AnyRef]) } } } diff --git a/akka-spring/src/test/scala/SupervisorSpringFeatureTest.scala b/akka-spring/src/test/scala/SupervisorSpringFeatureTest.scala new file mode 100644 index 0000000000..30c3710b79 --- /dev/null +++ b/akka-spring/src/test/scala/SupervisorSpringFeatureTest.scala @@ -0,0 +1,57 @@ +/** + * 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 se.scalablesolutions.akka.actor.Supervisor + +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 for typed actors 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 for untyped actors from context") { + val context = new ClassPathXmlApplicationContext("/supervisor-config.xml") + val supervisor = context.getBean("supervision-untyped-actors").asInstanceOf[Supervisor] + supervisor.children + } + + 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) + } + } +} \ No newline at end of file diff --git a/akka-spring/src/test/scala/ActiveObjectBeanDefinitionParserTest.scala b/akka-spring/src/test/scala/TypedActorBeanDefinitionParserTest.scala similarity index 56% rename from akka-spring/src/test/scala/ActiveObjectBeanDefinitionParserTest.scala rename to akka-spring/src/test/scala/TypedActorBeanDefinitionParserTest.scala index dc48ecc4b1..27a42f3d6c 100644 --- a/akka-spring/src/test/scala/ActiveObjectBeanDefinitionParserTest.scala +++ b/akka-spring/src/test/scala/TypedActorBeanDefinitionParserTest.scala @@ -12,25 +12,25 @@ import ScalaDom._ import org.w3c.dom.Element /** - * Test for ActiveObjectParser + * Test for TypedActorParser * @author michaelkober */ @RunWith(classOf[JUnitRunner]) -class ActiveObjectBeanDefinitionParserTest extends Spec with ShouldMatchers { - private class Parser extends ActiveObjectParser +class TypedActorBeanDefinitionParserTest extends Spec with ShouldMatchers { + private class Parser extends ActorParser - describe("An ActiveObjectParser") { + describe("An TypedActorParser") { val parser = new Parser() - it("should parse the active object configuration") { - val xml = - + - val props = parser.parseActiveObject(dom(xml).getDocumentElement); + val props = parser.parseActor(dom(xml).getDocumentElement); assert(props != null) assert(props.timeout === 1000) assert(props.target === "foo.bar.MyPojo") @@ -40,29 +40,29 @@ class ActiveObjectBeanDefinitionParserTest extends Spec with ShouldMatchers { } it("should throw IllegalArgumentException on missing mandatory attributes") { - val xml = - evaluating { parser.parseActiveObject(dom(xml).getDocumentElement) } should produce [IllegalArgumentException] + evaluating { parser.parseActor(dom(xml).getDocumentElement) } should produce [IllegalArgumentException] } - it("should parse ActiveObjects configuration with dispatcher") { - val xml = - - val props = parser.parseActiveObject(dom(xml).getDocumentElement); + + val props = parser.parseActor(dom(xml).getDocumentElement); assert(props != null) assert(props.dispatcher.dispatcherType === "thread-based") } - it("should parse remote ActiveObjects configuration") { - val xml = - - val props = parser.parseActiveObject(dom(xml).getDocumentElement); + + val props = parser.parseActor(dom(xml).getDocumentElement); assert(props != null) assert(props.host === "com.some.host") assert(props.port === 9999) 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") + } + } + +} + diff --git a/akka-spring/src/test/scala/UntypedActorSpringFeatureTest.scala b/akka-spring/src/test/scala/UntypedActorSpringFeatureTest.scala new file mode 100644 index 0000000000..cf7d8d9805 --- /dev/null +++ b/akka-spring/src/test/scala/UntypedActorSpringFeatureTest.scala @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ +package se.scalablesolutions.akka.spring + + +import foo.PingActor +import se.scalablesolutions.akka.dispatch.ExecutorBasedEventDrivenWorkStealingDispatcher +import se.scalablesolutions.akka.remote.RemoteNode +import se.scalablesolutions.akka.actor.ActorRef +import org.scalatest.FeatureSpec +import org.scalatest.matchers.ShouldMatchers +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith +import org.springframework.context.ApplicationContext +import org.springframework.context.support.ClassPathXmlApplicationContext + + +/** + * Tests for spring configuration of typed actors. + * @author michaelkober + */ +@RunWith(classOf[JUnitRunner]) +class UntypedActorSpringFeatureTest extends FeatureSpec with ShouldMatchers { + feature("parse Spring application context") { + + scenario("get a untyped actor") { + val context = new ClassPathXmlApplicationContext("/untyped-actor-config.xml") + val myactor = context.getBean("simple-untyped-actor").asInstanceOf[ActorRef] + assert(myactor.getActorClassName() === "se.scalablesolutions.akka.spring.foo.PingActor") + myactor.start() + myactor.sendOneWay("Hello") + assert(myactor.isDefinedAt("some string message")) + } + + scenario("untyped-actor with timeout") { + val context = new ClassPathXmlApplicationContext("/untyped-actor-config.xml") + val myactor = context.getBean("simple-untyped-actor-long-timeout").asInstanceOf[ActorRef] + assert(myactor.getActorClassName() === "se.scalablesolutions.akka.spring.foo.PingActor") + myactor.start() + myactor.sendOneWay("Hello") + assert(myactor.getTimeout() === 10000) + } + + scenario("transactional untyped-actor") { + val context = new ClassPathXmlApplicationContext("/untyped-actor-config.xml") + val myactor = context.getBean("transactional-untyped-actor").asInstanceOf[ActorRef] + assert(myactor.getActorClassName() === "se.scalablesolutions.akka.spring.foo.PingActor") + myactor.start() + myactor.sendOneWay("Hello") + assert(myactor.isDefinedAt("some string message")) + } + + scenario("get a remote typed-actor") { + RemoteNode.start + Thread.sleep(1000) + val context = new ClassPathXmlApplicationContext("/untyped-actor-config.xml") + val myactor = context.getBean("remote-untyped-actor").asInstanceOf[ActorRef] + assert(myactor.getActorClassName() === "se.scalablesolutions.akka.spring.foo.PingActor") + myactor.start() + myactor.sendOneWay("Hello") + assert(myactor.isDefinedAt("some string message")) + assert(myactor.getRemoteAddress().isDefined) + assert(myactor.getRemoteAddress().get.getHostName() === "localhost") + assert(myactor.getRemoteAddress().get.getPort() === 9999) + } + + scenario("untyped-actor with custom dispatcher") { + val context = new ClassPathXmlApplicationContext("/untyped-actor-config.xml") + val myactor = context.getBean("untyped-actor-with-dispatcher").asInstanceOf[ActorRef] + assert(myactor.getActorClassName() === "se.scalablesolutions.akka.spring.foo.PingActor") + myactor.start() + myactor.sendOneWay("Hello") + assert(myactor.getTimeout() === 1000) + assert(myactor.getDispatcher.isInstanceOf[ExecutorBasedEventDrivenWorkStealingDispatcher]) + } + } +} + diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 23412db3d3..827ec7b5ad 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -5,24 +5,12 @@ # This file has all the default settings, so all these could be removed with no visible effect. # Modify as needed. -log { - filename = "./logs/akka.log" - roll = "daily" # Options: never, hourly, daily, sunday/monday/... - level = "debug" # Options: fatal, critical, error, warning, info, debug, trace - console = on - # syslog_host = "" - # syslog_server_name = "" - - akka { - node = "se.scalablesolutions.akka" - level = "info" - } -} - akka { version = "0.10" - # FQN (Fully Qualified Name) to the class doing initial active object/actor + time-unit = "seconds" # default timeout time unit for all timeout properties throughout the config + + # FQN (Fully Qualified Name) to the class doing initial typed actor/actor # supervisor bootstrap, should be defined in default constructor boot = ["sample.camel.Boot", "sample.rest.java.Boot", @@ -30,9 +18,23 @@ akka { "sample.security.Boot"] actor { - timeout = 5000 # default timeout for future based invocations - serialize-messages = off # does a deep clone of (non-primitive) messages to ensure immutability - throughput = 5 # default throughput for ExecutorBasedEventDrivenDispatcher + timeout = 5 # default timeout for future based invocations + serialize-messages = off # does a deep clone of (non-primitive) messages to ensure immutability + throughput = 5 # Default throughput for all ExecutorBasedEventDrivenDispatcher + default-dispatcher { + type = "GlobalExecutorBasedEventDriven" # Must be one of the following, all "Global*" are non-configurable + # ReactorBasedSingleThreadEventDriven, (ExecutorBasedEventDrivenWorkStealing), ExecutorBasedEventDriven, + # ReactorBasedThreadPoolEventDriven, Hawt, GlobalReactorBasedSingleThreadEventDriven, + # GlobalReactorBasedThreadPoolEventDriven, GlobalExecutorBasedEventDriven, GlobalHawt + keep-alive-ms = 60000 # Keep alive time for threads + core-pool-size-factor = 1.0 # No of core threads ... ceil(available processors * factor) + max-pool-size-factor = 4.0 # Max no of threads ... ceil(available processors * factor) + executor-bounds = -1 # Makes the Executor bounded, -1 is unbounded + allow-core-timeout = on # Allow core threads to time out + rejection-policy = "caller-runs" # abort, caller-runs, discard-oldest, discard + throughput = 5 # Throughput for ExecutorBasedEventDrivenDispatcher + aggregate = off # Aggregate on/off for HawtDispatchers + } } stm { @@ -41,15 +43,13 @@ akka { # begin (or join), commit or rollback the JTA transaction. Default is 'off'. timeout = 5 # default timeout for blocking transactions and transaction set (in unit defined by # the time-unit property) - # FIXME: use 'time-unit' for all timeouts - time-unit = "seconds" # default timeout time unit } jta { provider = "from-jndi" # Options: "from-jndi" (means that Akka will try to detect a TransactionManager in the JNDI) # "atomikos" (means that Akka will use the Atomikos based JTA impl in 'akka-jta', # e.g. you need the akka-jta JARs on classpath). - timeout = 60000 + timeout = 60 } rest { @@ -57,8 +57,15 @@ akka { hostname = "localhost" port = 9998 filters = ["se.scalablesolutions.akka.security.AkkaSecurityFilterFactory"] # List with all jersey filters to use - resource_packages = ["sample.rest.scala","sample.rest.java","sample.security"] # List with all resource packages for your Jersey services + resource_packages = ["sample.rest.scala", + "sample.rest.java", + "sample.security"] # List with all resource packages for your Jersey services authenticator = "sample.security.BasicAuthenticationService" # The authentication service to use. Need to be overridden (uses sample now) + + comet-dispatcher { + #type = "Hawt" //uses the default dispatcher is commented out + } + #maxInactiveActivity = 60000 #Atmosphere CometSupport maxInactiveActivity #IF you are using a KerberosAuthenticationActor # kerberos { @@ -73,6 +80,23 @@ akka { compression-scheme = "zlib" # Options: "zlib" (lzf to come), leave out for no compression zlib-compression-level = 6 # Options: 0-9 (1 being fastest and 9 being the most compressed), default is 6 + ssl { #This feature is not deemed production ready and is not possible to turn on yet + service = off + + #You can either use java command-line options or use the settings below + + #key-store-type = "pkcs12" #Same as -Djavax.net.ssl.keyStoreType=pkcs12 + #key-store = "yourcertificate.p12" #Same as -Djavax.net.ssl.keyStore=yourcertificate.p12 + #key-store-pass = "$PASS" #Same as -Djavax.net.ssl.keyStorePassword=$PASS + + #trust-store-type = "jks" #Same as -Djavax.net.ssl.trustStoreType=jks + #trust-store = "your.keystore" #Same as -Djavax.net.ssl.trustStore=your.keystore + #trust-store-pass = "$PASS" #Same as -Djavax.net.ssl.trustStorePassword=$PASS + + #This can be useful for debugging + debug = off #if on, very verbose debug, same as -Djavax.net.debug=ssl + } + cluster { service = on name = "default" # The name of the cluster @@ -83,12 +107,12 @@ akka { service = on hostname = "localhost" port = 9999 - connection-timeout = 1000 # in millis (1 sec default) + connection-timeout = 1 } client { - reconnect-delay = 5000 # in millis (5 sec default) - read-timeout = 10000 # in millis (10 sec default) + reconnect-delay = 5 + read-timeout = 10 } } diff --git a/config/akka.conf b/config/akka.conf index 71670837d3..84b9bfbbcf 100644 --- a/config/akka.conf +++ b/config/akka.conf @@ -3,10 +3,3 @@ include "akka-reference.conf" # In this file you can override any option defined in the 'akka-reference.conf' file. # Copy in all or parts of the 'akka-reference.conf' file and modify as you please. -# -# -# -# cluster = ["localhost:6379", "localhost:6380", "localhost:6381"] -# -# -# diff --git a/config/log4j.properties b/config/log4j.properties deleted file mode 100755 index 76d3661163..0000000000 --- a/config/log4j.properties +++ /dev/null @@ -1,20 +0,0 @@ -# for production, you should probably set the root to INFO -# and the pattern to %c instead of %l. (%l is slower.) - -# output messages into a rolling log file as well as stdout -log4j.rootLogger=INFO,stdout,R - -# stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout - -# rolling log file ("system.log -log4j.appender.R=org.apache.log4j.DailyRollingFileAppender -log4j.appender.R.DatePattern='.'yyyy-MM-dd-HH -log4j.appender.R.layout=org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern=%5p [%t] %d{ISO8601} %F (line %L) %m%n - -# Edit the next line to point to your logs directory -log4j.appender.R.File=./logs/akka.log - -log4j.logger.org.atmosphere=DEBUG diff --git a/config/logback-test.xml b/config/logback-test.xml new file mode 100755 index 0000000000..fdb912d463 --- /dev/null +++ b/config/logback-test.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + [%4p] [%d{ISO8601}] [%t] %c{1}: %m%n + + + + + + + diff --git a/config/logback.xml b/config/logback.xml new file mode 100755 index 0000000000..1ace0bfd8f --- /dev/null +++ b/config/logback.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + [%t] [%4p] [%d{ISO8601}] %c{1}: %m%n + + + + ./logs/akka.log + + [%t] [%4p] [%d{ISO8601}] %c{1}: %m%n + + + ./logs/akka.log.%d{yyyy-MM-dd-HH} + + + + + + + + diff --git a/embedded-repo/com/redis/redisclient/2.8.0-1.4/redisclient-2.8.0-1.4.jar b/embedded-repo/com/redis/redisclient/2.8.0-1.4/redisclient-2.8.0-1.4.jar index a5c824b19e..b811e6ab92 100644 Binary files a/embedded-repo/com/redis/redisclient/2.8.0-1.4/redisclient-2.8.0-1.4.jar and b/embedded-repo/com/redis/redisclient/2.8.0-1.4/redisclient-2.8.0-1.4.jar differ diff --git a/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1-sources.jar b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1-sources.jar new file mode 100644 index 0000000000..7aa1393153 Binary files /dev/null and b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1-sources.jar differ diff --git a/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1-sources.jar.md5 b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1-sources.jar.md5 new file mode 100644 index 0000000000..46500d76fc --- /dev/null +++ b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1-sources.jar.md5 @@ -0,0 +1 @@ +607f775c6b2ec1954fe60717875aefea \ No newline at end of file diff --git a/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1-sources.jar.sha1 b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1-sources.jar.sha1 new file mode 100644 index 0000000000..3eb85256e2 --- /dev/null +++ b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1-sources.jar.sha1 @@ -0,0 +1 @@ +ee377a85bf07b2afb3a98157f926ebdb47a5e88c \ No newline at end of file diff --git a/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.jar b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.jar new file mode 100644 index 0000000000..7222c09136 Binary files /dev/null and b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.jar differ diff --git a/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.jar.md5 b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.jar.md5 new file mode 100644 index 0000000000..be8f3065c7 --- /dev/null +++ b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.jar.md5 @@ -0,0 +1 @@ +ba1be87b58c03e8ae6f890ca87c74b5b \ No newline at end of file diff --git a/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.jar.sha1 b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.jar.sha1 new file mode 100644 index 0000000000..aa86a31839 --- /dev/null +++ b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.jar.sha1 @@ -0,0 +1 @@ +aaa96009d7e151f89703b4d932fc73ebcf9bc973 \ No newline at end of file diff --git a/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.pom b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.pom new file mode 100644 index 0000000000..e3cdbce60d --- /dev/null +++ b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.pom @@ -0,0 +1,144 @@ + + + + + 4.0.0 + + + org.apache.camel + camel-parent + 2.4.0 + + + camel-jetty + bundle + Camel :: Jetty + Camel Jetty support + 2.4.0.1 + + + org.apache.camel.component.jetty.* + + + + + + org.apache.camel + camel-core + 2.4.0 + + + org.apache.camel + camel-http + 2.4.0 + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-security + + + org.eclipse.jetty + jetty-servlet + + + org.eclipse.jetty + jetty-servlets + ${jetty-version} + + + org.eclipse.jetty + jetty-client + + + org.eclipse.jetty + jetty-jmx + ${jetty-version} + + + + org.apache.camel + camel-test + 2.4.0 + test + + + org.apache.camel + camel-spring + 2.4.0 + test + + + javax.mail + mail + ${javax-mail-version} + test + + + + org.springframework + spring-context + true + test + + + org.springframework + spring-aop + true + test + + + org.springframework + spring-test + true + test + + + + junit + junit + test + + + log4j + log4j + test + + + + + + + + maven-surefire-plugin + + pertest + + + + **/*XXXTest.* + + + + + + + diff --git a/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.pom.md5 b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.pom.md5 new file mode 100644 index 0000000000..295aae7a23 --- /dev/null +++ b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.pom.md5 @@ -0,0 +1 @@ +fba57baa166195ac2b2a013c3cc6d3f1 \ No newline at end of file diff --git a/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.pom.sha1 b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.pom.sha1 new file mode 100644 index 0000000000..7fb0b3347f --- /dev/null +++ b/embedded-repo/org/apache/camel/camel-jetty/2.4.0.1/camel-jetty-2.4.0.1.pom.sha1 @@ -0,0 +1 @@ +c41ff483ac35754c1d41b1823561935849b362ed \ No newline at end of file diff --git a/embedded-repo/org/apache/camel/camel-jetty/maven-metadata.xml b/embedded-repo/org/apache/camel/camel-jetty/maven-metadata.xml new file mode 100644 index 0000000000..0e25837b64 --- /dev/null +++ b/embedded-repo/org/apache/camel/camel-jetty/maven-metadata.xml @@ -0,0 +1,11 @@ + + org.apache.camel + camel-jetty + 2.4.0.1 + + + 2.4.0.1 + + 20100723102939 + + \ No newline at end of file diff --git a/embedded-repo/org/apache/camel/camel-jetty/maven-metadata.xml.md5 b/embedded-repo/org/apache/camel/camel-jetty/maven-metadata.xml.md5 new file mode 100644 index 0000000000..fcff48b3ce --- /dev/null +++ b/embedded-repo/org/apache/camel/camel-jetty/maven-metadata.xml.md5 @@ -0,0 +1 @@ +34f1efbcb11f7251390994d8f81598b2 \ No newline at end of file diff --git a/embedded-repo/org/apache/camel/camel-jetty/maven-metadata.xml.sha1 b/embedded-repo/org/apache/camel/camel-jetty/maven-metadata.xml.sha1 new file mode 100644 index 0000000000..64ef58e71f --- /dev/null +++ b/embedded-repo/org/apache/camel/camel-jetty/maven-metadata.xml.sha1 @@ -0,0 +1 @@ +2e1bb47c5a8c19f98b70e5e6af450861933deacc \ No newline at end of file diff --git a/embedded-repo/shoal-jxta/jxta/1.1-20090818/LICENSE.txt b/embedded-repo/shoal-jxta/jxta/1.1-20090818/LICENSE.txt deleted file mode 100644 index 4aea53745d..0000000000 --- a/embedded-repo/shoal-jxta/jxta/1.1-20090818/LICENSE.txt +++ /dev/null @@ -1,263 +0,0 @@ -COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 - -1. Definitions. - - 1.1. Contributor. means each individual or entity that creates or contributes to the creation of Modifications. - - 1.2. Contributor Version. means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor. - - 1.3. Covered Software. means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof. - - 1.4. Executable. means the Covered Software in any form other than Source Code. - - 1.5. Initial Developer. means the individual or entity that first makes Original Software available under this License. - - 1.6. Larger Work. means a work which combines Covered Software or portions thereof with code not governed by the terms of this License. - - 1.7. License. means this document. - - 1.8. Licensable. means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. - - 1.9. Modifications. means the Source Code and Executable form of any of the following: - - A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications; - - B. Any new file that contains any part of the Original Software or previous Modification; or - - C. Any new file that is contributed or otherwise made available under the terms of this License. - - 1.10. Original Software. means the Source Code and Executable form of computer software code that is originally released under this License. - - 1.11. Patent Claims. means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. - - 1.12. Source Code. means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code. - - 1.13. You. (or .Your.) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, .You. includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, .control. means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. - -2. License Grants. - - 2.1. The Initial Developer Grant. - - Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license: - - (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and - - (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof). - - (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License. - - (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices. - - 2.2. Contributor Grant. - - Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: - - (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and - - (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). - - (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party. - - (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor. - -3. Distribution Obligations. - - 3.1. Availability of Source Code. - Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange. - - 3.2. Modifications. - The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License. - - 3.3. Required Notices. - You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer. - - 3.4. Application of Additional Terms. - You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients. rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. - - 3.5. Distribution of Executable Versions. - You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient.s rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. - - 3.6. Larger Works. - You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software. - -4. Versions of the License. - - 4.1. New Versions. - Sun Microsystems, Inc. is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License. - - 4.2. Effect of New Versions. - You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward. - - 4.3. Modified Versions. - When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License. - -5. DISCLAIMER OF WARRANTY. - - COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN .AS IS. BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. - -6. TERMINATION. - - 6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. - - 6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as .Participant.) alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant. - - 6.3. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination. - -7. LIMITATION OF LIABILITY. - - UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY.S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. - -8. U.S. GOVERNMENT END USERS. - - The Covered Software is a .commercial item,. as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of .commercial computer software. (as that term is defined at 48 C.F.R. º 252.227-7014(a)(1)) and .commercial computer software documentation. as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License. - -9. MISCELLANEOUS. - - This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction.s conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys. fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software. - -10. RESPONSIBILITY FOR CLAIMS. - - As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. - - NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) - - The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California. - - -The GNU General Public License (GPL) Version 2, June 1991 - - -Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - -Preamble - -The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. - -When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. - -To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. - -For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. - -We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. - -Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. - -Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. - -The precise terms and conditions for copying, distribution and modification follow. - - -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. - -1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. - -You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - -2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. - - c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. - -3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. - -If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - -4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. - -5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. - -6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. - -7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. - -This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - -8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. - -9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. - -10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - -NO WARRANTY - -11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -END OF TERMS AND CONDITIONS - - -How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. - -To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - - One line to give the program's name and a brief idea of what it does. - - Copyright (C) - - This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. - - signature of Ty Coon, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. - - -"CLASSPATH" EXCEPTION TO THE GPL VERSION 2 - -Certain source files distributed by Sun Microsystems, Inc. are subject to the following clarification and special exception to the GPL Version 2, but only where Sun has expressly included in the particular source file's header the words - -"Sun designates this particular file as subject to the "Classpath" exception as provided by Sun in the License file that accompanied this code." - -Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License Version 2 cover the whole combination. - -As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module.? An independent module is a module which is not derived from or based on this library.? If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so.? If you do not wish to do so, delete this exception statement from your version. diff --git a/embedded-repo/shoal-jxta/jxta/1.1-20090818/jxta-1.1-20090818.jar b/embedded-repo/shoal-jxta/jxta/1.1-20090818/jxta-1.1-20090818.jar deleted file mode 100644 index 6b539cce4a..0000000000 Binary files a/embedded-repo/shoal-jxta/jxta/1.1-20090818/jxta-1.1-20090818.jar and /dev/null differ diff --git a/embedded-repo/shoal-jxta/jxta/1.1-20090818/jxta-1.1-20090818.pom b/embedded-repo/shoal-jxta/jxta/1.1-20090818/jxta-1.1-20090818.pom deleted file mode 100755 index c0f3c68341..0000000000 --- a/embedded-repo/shoal-jxta/jxta/1.1-20090818/jxta-1.1-20090818.pom +++ /dev/null @@ -1,8 +0,0 @@ - - - 4.0.0 - shoal-jxta - jxta - 1.1-20090818 - jar - \ No newline at end of file diff --git a/embedded-repo/shoal-jxta/shoal/1.1-20090818/LICENSE.txt b/embedded-repo/shoal-jxta/shoal/1.1-20090818/LICENSE.txt deleted file mode 100644 index 4aea53745d..0000000000 --- a/embedded-repo/shoal-jxta/shoal/1.1-20090818/LICENSE.txt +++ /dev/null @@ -1,263 +0,0 @@ -COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 - -1. Definitions. - - 1.1. Contributor. means each individual or entity that creates or contributes to the creation of Modifications. - - 1.2. Contributor Version. means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor. - - 1.3. Covered Software. means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof. - - 1.4. Executable. means the Covered Software in any form other than Source Code. - - 1.5. Initial Developer. means the individual or entity that first makes Original Software available under this License. - - 1.6. Larger Work. means a work which combines Covered Software or portions thereof with code not governed by the terms of this License. - - 1.7. License. means this document. - - 1.8. Licensable. means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. - - 1.9. Modifications. means the Source Code and Executable form of any of the following: - - A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications; - - B. Any new file that contains any part of the Original Software or previous Modification; or - - C. Any new file that is contributed or otherwise made available under the terms of this License. - - 1.10. Original Software. means the Source Code and Executable form of computer software code that is originally released under this License. - - 1.11. Patent Claims. means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. - - 1.12. Source Code. means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code. - - 1.13. You. (or .Your.) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, .You. includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, .control. means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. - -2. License Grants. - - 2.1. The Initial Developer Grant. - - Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license: - - (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and - - (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof). - - (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License. - - (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices. - - 2.2. Contributor Grant. - - Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: - - (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and - - (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). - - (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party. - - (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor. - -3. Distribution Obligations. - - 3.1. Availability of Source Code. - Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange. - - 3.2. Modifications. - The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License. - - 3.3. Required Notices. - You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer. - - 3.4. Application of Additional Terms. - You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients. rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. - - 3.5. Distribution of Executable Versions. - You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient.s rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. - - 3.6. Larger Works. - You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software. - -4. Versions of the License. - - 4.1. New Versions. - Sun Microsystems, Inc. is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License. - - 4.2. Effect of New Versions. - You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward. - - 4.3. Modified Versions. - When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License. - -5. DISCLAIMER OF WARRANTY. - - COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN .AS IS. BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. - -6. TERMINATION. - - 6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. - - 6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as .Participant.) alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant. - - 6.3. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination. - -7. LIMITATION OF LIABILITY. - - UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY.S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. - -8. U.S. GOVERNMENT END USERS. - - The Covered Software is a .commercial item,. as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of .commercial computer software. (as that term is defined at 48 C.F.R. º 252.227-7014(a)(1)) and .commercial computer software documentation. as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License. - -9. MISCELLANEOUS. - - This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction.s conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys. fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software. - -10. RESPONSIBILITY FOR CLAIMS. - - As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. - - NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) - - The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California. - - -The GNU General Public License (GPL) Version 2, June 1991 - - -Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - -Preamble - -The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. - -When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. - -To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. - -For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. - -We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. - -Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. - -Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. - -The precise terms and conditions for copying, distribution and modification follow. - - -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. - -1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. - -You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - -2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. - - c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. - -3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. - -If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - -4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. - -5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. - -6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. - -7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. - -This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - -8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. - -9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. - -10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - -NO WARRANTY - -11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -END OF TERMS AND CONDITIONS - - -How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. - -To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - - One line to give the program's name and a brief idea of what it does. - - Copyright (C) - - This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. - - signature of Ty Coon, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. - - -"CLASSPATH" EXCEPTION TO THE GPL VERSION 2 - -Certain source files distributed by Sun Microsystems, Inc. are subject to the following clarification and special exception to the GPL Version 2, but only where Sun has expressly included in the particular source file's header the words - -"Sun designates this particular file as subject to the "Classpath" exception as provided by Sun in the License file that accompanied this code." - -Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License Version 2 cover the whole combination. - -As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module.? An independent module is a module which is not derived from or based on this library.? If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so.? If you do not wish to do so, delete this exception statement from your version. diff --git a/embedded-repo/shoal-jxta/shoal/1.1-20090818/shoal-1.1-20090818.jar b/embedded-repo/shoal-jxta/shoal/1.1-20090818/shoal-1.1-20090818.jar deleted file mode 100644 index 3e13c87681..0000000000 Binary files a/embedded-repo/shoal-jxta/shoal/1.1-20090818/shoal-1.1-20090818.jar and /dev/null differ diff --git a/embedded-repo/shoal-jxta/shoal/1.1-20090818/shoal-1.1-20090818.pom b/embedded-repo/shoal-jxta/shoal/1.1-20090818/shoal-1.1-20090818.pom deleted file mode 100755 index 3174de1e4a..0000000000 --- a/embedded-repo/shoal-jxta/shoal/1.1-20090818/shoal-1.1-20090818.pom +++ /dev/null @@ -1,8 +0,0 @@ - - - 4.0.0 - shoal-jxta - shoal - 1.1-20090818 - jar - \ No newline at end of file diff --git a/embedded-repo/sjson/json/sjson/0.7-2.8.0/sjson-0.7-2.8.0.jar b/embedded-repo/sjson/json/sjson/0.7-2.8.0/sjson-0.7-2.8.0.jar new file mode 100644 index 0000000000..e8700f11d3 Binary files /dev/null and b/embedded-repo/sjson/json/sjson/0.7-2.8.0/sjson-0.7-2.8.0.jar differ diff --git a/embedded-repo/sjson/json/sjson/0.7-2.8.0/sjson-0.7-2.8.0.pom b/embedded-repo/sjson/json/sjson/0.7-2.8.0/sjson-0.7-2.8.0.pom new file mode 100644 index 0000000000..dfc9017dcf --- /dev/null +++ b/embedded-repo/sjson/json/sjson/0.7-2.8.0/sjson-0.7-2.8.0.pom @@ -0,0 +1,9 @@ + + + 4.0.0 + sjson.json + sjson + 0.7-2.8.0 + POM was created from install:install-file + diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 8020b848af..21a1e3c217 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -2,32 +2,19 @@ | Copyright (C) 2009-2010 Scalable Solutions AB | \---------------------------------------------------------------------------*/ -import sbt._ -import sbt.CompileOrder._ - -import spde._ -import de.tuxed.codefellow.plugin.CodeFellowPlugin - +import com.weiglewilczek.bnd4sbt.BNDPlugin +import java.io.File import java.util.jar.Attributes import java.util.jar.Attributes.Name._ -import java.io.File +import sbt._ +import sbt.CompileOrder._ +import spde._ -class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { +class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { - // ------------------------------------------------------------ - // project versions - val JERSEY_VERSION = "1.2" - val ATMO_VERSION = "0.6" - val CAMEL_VERSION = "2.4.0" - val SPRING_VERSION = "3.0.3.RELEASE" - val CASSANDRA_VERSION = "0.6.1" - val LIFT_VERSION = "2.0-scala280-SNAPSHOT" - val SCALATEST_VERSION = "1.2-for-scala-2.8.0.final-SNAPSHOT" - val MULTIVERSE_VERSION = "0.6-SNAPSHOT" - - // ------------------------------------------------------------ - lazy val deployPath = info.projectPath / "deploy" - lazy val distPath = info.projectPath / "dist" + // ------------------------------------------------------------------------------------------------------------------- + // Compile settings + // ------------------------------------------------------------------------------------------------------------------- override def compileOptions = super.compileOptions ++ Seq("-deprecation", @@ -37,58 +24,214 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { "-Xwarninit", "-encoding", "utf8") .map(x => CompileOption(x)) - override def javaCompileOptions = JavaCompileOption("-Xlint:unchecked") :: super.javaCompileOptions.toList - def distName = "%s_%s-%s.zip".format(name, buildScalaVersion, version) + // ------------------------------------------------------------------------------------------------------------------- + // Deploy/dist settings + // ------------------------------------------------------------------------------------------------------------------- + lazy val deployPath = info.projectPath / "deploy" + lazy val distPath = info.projectPath / "dist" + def distName = "%s_%s-%s.zip".format(name, buildScalaVersion, version) lazy val dist = zipTask(allArtifacts, "dist", distName) dependsOn (`package`) describedAs("Zips up the distribution.") // ------------------------------------------------------------------------------------------------------------------- - // Repositories + // All repositories *must* go here! See ModuleConigurations below. + // ------------------------------------------------------------------------------------------------------------------- + + 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 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") + } + + // ------------------------------------------------------------------------------------------------------------------- + // ModuleConfigurations // Every dependency that cannot be resolved from the built-in repositories (Maven Central and Scala Tools Releases) // must be resolved from a ModuleConfiguration. This will result in a significant acceleration of the update action. // Therefore, if repositories are defined, this must happen as def, not as val. // ------------------------------------------------------------------------------------------------------------------- - val embeddedRepo = "Embedded Repo" at (info.projectPath / "embedded-repo").asURL.toString - val scalaTestModuleConfig = ModuleConfiguration("org.scalatest", ScalaToolsSnapshots) - def guiceyFruitRepo = "GuiceyFruit Repo" at "http://guiceyfruit.googlecode.com/svn/repo/releases/" - val guiceyFruitModuleConfig = ModuleConfiguration("org.guiceyfruit", guiceyFruitRepo) - def jbossRepo = "JBoss Repo" at "https://repository.jboss.org/nexus/content/groups/public/" - val jbossModuleConfig = ModuleConfiguration("org.jboss", jbossRepo) - val nettyModuleConfig = ModuleConfiguration("org.jboss.netty", jbossRepo) - val jgroupsModuleConfig = ModuleConfiguration("jgroups", jbossRepo) - def sunjdmkRepo = "Sun JDMK Repo" at "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo" - val jmsModuleConfig = ModuleConfiguration("javax.jms", sunjdmkRepo) - val jdmkModuleConfig = ModuleConfiguration("com.sun.jdmk", sunjdmkRepo) - val jmxModuleConfig = ModuleConfiguration("com.sun.jmx", sunjdmkRepo) - def javaNetRepo = "java.net Repo" at "http://download.java.net/maven/2" - def sonatypeSnapshotRepo = "Sonatype OSS Repo" at "http://oss.sonatype.org/content/repositories/releases" - val jerseyModuleConfig = ModuleConfiguration("com.sun.jersey", javaNetRepo) - val jerseyContrModuleConfig = ModuleConfiguration("com.sun.jersey.contribs", javaNetRepo) - val grizzlyModuleConfig = ModuleConfiguration("com.sun.grizzly", javaNetRepo) - val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", sonatypeSnapshotRepo) - val liftModuleConfig = ModuleConfiguration("net.liftweb", ScalaToolsSnapshots) - def codehausSnapshotRepo = "Codehaus Snapshots" at "http://snapshots.repository.codehaus.org" - val multiverseModuleConfig = ModuleConfiguration("org.multiverse", codehausSnapshotRepo) - // ------------------------------------------------------------ - // project defintions - lazy val akka_core = project("akka-core", "akka-core", new AkkaCoreProject(_)) - lazy val akka_amqp = project("akka-amqp", "akka-amqp", new AkkaAMQPProject(_), akka_core) - lazy val akka_http = project("akka-http", "akka-http", new AkkaHttpProject(_), akka_core, akka_camel) - lazy val akka_camel = project("akka-camel", "akka-camel", new AkkaCamelProject(_), akka_core) + 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 jmsModuleConfig = ModuleConfiguration("javax.jms", SunJDMKRepo) + lazy val jmxModuleConfig = ModuleConfiguration("com.sun.jmx", 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 liftModuleConfig = ModuleConfiguration("net.liftweb", ScalaToolsReleases) + lazy val multiverseModuleConfig = ModuleConfiguration("org.multiverse", CodehausSnapshotRepo) + lazy val nettyModuleConfig = ModuleConfiguration("org.jboss.netty", JBossRepo) + lazy val scalaTestModuleConfig = ModuleConfiguration("org.scalatest", ScalaToolsSnapshots) + lazy val logbackModuleConfig = ModuleConfiguration("ch.qos.logback",sbt.DefaultMavenRepository) + lazy val embeddedRepo = EmbeddedRepo // This is the only exception, because the embedded repo is fast! + + // ------------------------------------------------------------------------------------------------------------------- + // Versions + // ------------------------------------------------------------------------------------------------------------------- + + lazy val ATMO_VERSION = "0.6.1" + lazy val CAMEL_VERSION = "2.4.0" + lazy val CASSANDRA_VERSION = "0.6.1" + lazy val DispatchVersion = "0.7.4" + lazy val HAWTDISPATCH_VERSION = "1.0" + lazy val JacksonVersion = "1.2.1" + lazy val JERSEY_VERSION = "1.2" + lazy val LIFT_VERSION = "2.1-M1" + lazy val MULTIVERSE_VERSION = "0.6-SNAPSHOT" + lazy val SCALATEST_VERSION = "1.2-for-scala-2.8.0.final-SNAPSHOT" + lazy val LOGBACK_VERSION = "0.9.24" + lazy val SLF4J_VERSION = "1.6.0" + lazy val SPRING_VERSION = "3.0.3.RELEASE" + lazy val WerkzVersion = "2.2.1" + + // ------------------------------------------------------------------------------------------------------------------- + // Dependencies + // ------------------------------------------------------------------------------------------------------------------- + + object Dependencies { + + // Compile + + lazy val annotation = "javax.annotation" % "jsr250-api" % "1.0" % "compile" + + lazy val aopalliance = "aopalliance" % "aopalliance" % "1.0" % "compile" + + lazy val atmo = "org.atmosphere" % "atmosphere-annotations" % ATMO_VERSION % "compile" + lazy val atmo_jbossweb = "org.atmosphere" % "atmosphere-compat-jbossweb" % ATMO_VERSION % "compile" + lazy val atmo_jersey = "org.atmosphere" % "atmosphere-jersey" % ATMO_VERSION % "compile" + lazy val atmo_runtime = "org.atmosphere" % "atmosphere-runtime" % ATMO_VERSION % "compile" + lazy val atmo_tomcat = "org.atmosphere" % "atmosphere-compat-tomcat" % ATMO_VERSION % "compile" + lazy val atmo_weblogic = "org.atmosphere" % "atmosphere-compat-weblogic" % ATMO_VERSION % "compile" + + lazy val atomikos_transactions = "com.atomikos" % "transactions" % "3.2.3" % "compile" + lazy val atomikos_transactions_api = "com.atomikos" % "transactions-api" % "3.2.3" % "compile" + lazy val atomikos_transactions_jta = "com.atomikos" % "transactions-jta" % "3.2.3" % "compile" + + lazy val camel_core = "org.apache.camel" % "camel-core" % CAMEL_VERSION % "compile" + + lazy val cassandra = "org.apache.cassandra" % "cassandra" % CASSANDRA_VERSION % "compile" + + lazy val commons_codec = "commons-codec" % "commons-codec" % "1.4" % "compile" + + lazy val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" + + lazy val commons_pool = "commons-pool" % "commons-pool" % "1.5.4" % "compile" + + lazy val configgy = "net.lag" % "configgy" % "2.8.0-1.5.5" % "compile" + + lazy val dispatch_http = "net.databinder" % "dispatch-http_2.8.0" % DispatchVersion % "compile" + lazy val dispatch_json = "net.databinder" % "dispatch-json_2.8.0" % DispatchVersion % "compile" + + lazy val grizzly = "com.sun.grizzly" % "grizzly-comet-webserver" % "1.9.18-i" % "compile" + + lazy val guicey = "org.guiceyfruit" % "guice-all" % "2.0" % "compile" + + lazy val h2_lzf = "voldemort.store.compress" % "h2-lzf" % "1.0" % "compile" + + lazy val hawtdispatch = "org.fusesource.hawtdispatch" % "hawtdispatch-scala" % HAWTDISPATCH_VERSION % "compile" + + lazy val jackson = "org.codehaus.jackson" % "jackson-mapper-asl" % JacksonVersion % "compile" + lazy val jackson_core = "org.codehaus.jackson" % "jackson-core-asl" % JacksonVersion % "compile" + lazy val jackson_core_asl = "org.codehaus.jackson" % "jackson-core-asl" % JacksonVersion % "compile" + + lazy val jersey = "com.sun.jersey" % "jersey-core" % JERSEY_VERSION % "compile" + lazy val jersey_json = "com.sun.jersey" % "jersey-json" % JERSEY_VERSION % "compile" + lazy val jersey_server = "com.sun.jersey" % "jersey-server" % JERSEY_VERSION % "compile" + lazy val jersey_contrib = "com.sun.jersey.contribs" % "jersey-scala" % JERSEY_VERSION % "compile" + + lazy val jgroups = "jgroups" % "jgroups" % "2.9.0.GA" % "compile" + + lazy val jsr166x = "jsr166x" % "jsr166x" % "1.0" % "compile" + + lazy val jsr250 = "javax.annotation" % "jsr250-api" % "1.0" % "compile" + + lazy val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" + + lazy val jta_1_1 = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile" intransitive + + lazy val lift_util = "net.liftweb" % "lift-util_2.8.0" % LIFT_VERSION % "compile" + lazy val lift_webkit = "net.liftweb" % "lift-webkit_2.8.0" % LIFT_VERSION % "compile" + + lazy val mongo = "org.mongodb" % "mongo-java-driver" % "2.0" % "compile" + + lazy val multiverse = "org.multiverse" % "multiverse-alpha" % MULTIVERSE_VERSION % "compile" intransitive + + lazy val netty = "org.jboss.netty" % "netty" % "3.2.1.Final" % "compile" + + lazy val protobuf = "com.google.protobuf" % "protobuf-java" % "2.3.0" % "compile" + + lazy val osgi_core = "org.osgi" % "org.osgi.core" % "4.2.0" + + lazy val rabbit = "com.rabbitmq" % "amqp-client" % "1.8.1" % "compile" + + lazy val redis = "com.redis" % "redisclient" % "2.8.0-1.4" % "compile" + + lazy val sbinary = "sbinary" % "sbinary" % "2.8.0-0.3.1" % "compile" + + lazy val servlet = "javax.servlet" % "servlet-api" % "2.5" % "compile" + + lazy val sjson = "sjson.json" % "sjson" % "0.7-2.8.0" % "compile" + + lazy val slf4j = "org.slf4j" % "slf4j-api" % SLF4J_VERSION % "compile" + + lazy val logback = "ch.qos.logback" % "logback-classic" % LOGBACK_VERSION % "compile" + lazy val logback_core = "ch.qos.logback" % "logback-core" % LOGBACK_VERSION % "compile" + + lazy val spring_beans = "org.springframework" % "spring-beans" % SPRING_VERSION % "compile" + lazy val spring_context = "org.springframework" % "spring-context" % SPRING_VERSION % "compile" + + lazy val stax_api = "javax.xml.stream" % "stax-api" % "1.0-2" % "compile" + + lazy val thrift = "com.facebook" % "thrift" % "r917130" % "compile" + + lazy val werkz = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % WerkzVersion % "compile" + lazy val werkz_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % WerkzVersion % "compile" + + // Test + + lazy val camel_spring = "org.apache.camel" % "camel-spring" % CAMEL_VERSION % "test" + lazy val cassandra_clhm = "org.apache.cassandra" % "clhm-production" % CASSANDRA_VERSION % "test" + lazy val commons_coll = "commons-collections" % "commons-collections" % "3.2.1" % "test" + lazy val google_coll = "com.google.collections" % "google-collections" % "1.0" % "test" + lazy val high_scale = "org.apache.cassandra" % "high-scale-lib" % CASSANDRA_VERSION % "test" + lazy val jettyServer = "org.mortbay.jetty" % "jetty" % "6.1.22" % "test" + lazy val junit = "junit" % "junit" % "4.5" % "test" + lazy val mockito = "org.mockito" % "mockito-all" % "1.8.1" % "test" + lazy val scalatest = "org.scalatest" % "scalatest" % SCALATEST_VERSION % "test" + } + + // ------------------------------------------------------------------------------------------------------------------- + // Subprojects + // ------------------------------------------------------------------------------------------------------------------- + + lazy val akka_core = project("akka-core", "akka-core", new AkkaCoreProject(_)) + lazy val akka_amqp = project("akka-amqp", "akka-amqp", new AkkaAMQPProject(_), akka_core) + lazy val akka_http = project("akka-http", "akka-http", new AkkaHttpProject(_), akka_core, akka_camel) + lazy val akka_camel = project("akka-camel", "akka-camel", new AkkaCamelProject(_), akka_core) lazy val akka_persistence = project("akka-persistence", "akka-persistence", new AkkaPersistenceParentProject(_)) - lazy val akka_spring = project("akka-spring", "akka-spring", new AkkaSpringProject(_), akka_core, akka_camel) - lazy val akka_jta = project("akka-jta", "akka-jta", new AkkaJTAProject(_), akka_core) - lazy val akka_kernel = project("akka-kernel", "akka-kernel", new AkkaKernelProject(_), - akka_core, akka_http, akka_spring, akka_camel, akka_persistence, akka_amqp) + lazy val akka_spring = project("akka-spring", "akka-spring", new AkkaSpringProject(_), akka_core, akka_camel) + lazy val akka_jta = project("akka-jta", "akka-jta", new AkkaJTAProject(_), akka_core) + lazy val akka_kernel = project("akka-kernel", "akka-kernel", new AkkaKernelProject(_), + akka_core, akka_http, akka_spring, akka_camel, akka_persistence, akka_amqp) + lazy val akka_osgi = project("akka-osgi", "akka-osgi", new AkkaOSGiParentProject(_)) + lazy val akka_samples = project("akka-samples", "akka-samples", new AkkaSamplesParentProject(_)) - // examples - lazy val akka_samples = project("akka-samples", "akka-samples", new AkkaSamplesParentProject(_)) + // ------------------------------------------------------------------------------------------------------------------- + // Miscellaneous + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------ - // Run Akka microkernel using 'sbt run' + use for packaging executable JAR override def mainClass = Some("se.scalablesolutions.akka.kernel.Main") override def packageOptions = @@ -121,8 +264,17 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { " dist/akka-jta_%s-%s.jar".format(buildScalaVersion, version) ) - // Exclude slf4j1.5.11 from the classpath, it's conflicting... - override def runClasspath = super.runClasspath --- (super.runClasspath ** "slf4j*1.5.11.jar") + //Exclude slf4j1.5.11 from the classpath, it's conflicting... + override def fullClasspath(config: Configuration): PathFinder = { + super.fullClasspath(config) --- + (super.fullClasspath(config) ** "slf4j*1.5.11.jar") + } + + override def mainResources = super.mainResources +++ + descendents(info.projectPath / "config", "*") --- + (super.mainResources ** "logback-test.xml") + + override def testResources = super.testResources --- (super.testResources ** "logback-test.xml") // ------------------------------------------------------------ // publishing @@ -170,105 +322,100 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { " -Dpackaging=jar -DgeneratePom=true" command ! log } - None + None } dependsOn(dist) describedAs("Run mvn install for artifacts in dist.") - // ------------------------------------------------------------ - // subprojects - class AkkaCoreProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { - val netty = "org.jboss.netty" % "netty" % "3.2.1.Final" % "compile" - val commons_codec = "commons-codec" % "commons-codec" % "1.4" % "compile" - val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" - val dispatch_json = "net.databinder" % "dispatch-json_2.8.0" % "0.7.4" % "compile" - val dispatch_http = "net.databinder" % "dispatch-http_2.8.0" % "0.7.4" % "compile" - val sjson = "sjson.json" % "sjson" % "0.7-SNAPSHOT-2.8.0" % "compile" - val sbinary = "sbinary" % "sbinary" % "2.8.0-0.3.1" % "compile" - val jackson = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.2.1" % "compile" - val jackson_core = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile" - val h2_lzf = "voldemort.store.compress" % "h2-lzf" % "1.0" % "compile" - val jsr166x = "jsr166x" % "jsr166x" % "1.0" % "compile" - val jta_1_1 = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile" intransitive() - val werkz = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.2.1" % "compile" - val werkz_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.2.1" % "compile" - val configgy = "net.lag" % "configgy" % "2.8.0-1.5.5" % "compile" - val guicey = "org.guiceyfruit" % "guice-all" % "2.0" % "compile" - val aopalliance = "aopalliance" % "aopalliance" % "1.0" % "compile" - val protobuf = "com.google.protobuf" % "protobuf-java" % "2.3.0" % "compile" - val multiverse = "org.multiverse" % "multiverse-alpha" % MULTIVERSE_VERSION % "compile" intransitive() - val jgroups = "jgroups" % "jgroups" % "2.9.0.GA" % "compile" + // ------------------------------------------------------------------------------------------------------------------- + // akka-core subproject + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaCoreProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + val aopalliance = Dependencies.aopalliance + val commons_codec = Dependencies.commons_codec + val commons_io = Dependencies.commons_io + val configgy = Dependencies.configgy + val dispatch_http = Dependencies.dispatch_http + val dispatch_json = Dependencies.dispatch_json + val guicey = Dependencies.guicey + val h2_lzf = Dependencies.h2_lzf + val hawtdispatch = Dependencies.hawtdispatch + val jackson = Dependencies.jackson + val jackson_core = Dependencies.jackson_core + val jgroups = Dependencies.jgroups + val jsr166x = Dependencies.jsr166x + val jta_1_1 = Dependencies.jta_1_1 + val multiverse = Dependencies.multiverse + val netty = Dependencies.netty + val protobuf = Dependencies.protobuf + val sbinary = Dependencies.sbinary + val sjson = Dependencies.sjson + val werkz = Dependencies.werkz + val werkz_core = Dependencies.werkz_core + val slf4j = Dependencies.slf4j + val logback = Dependencies.logback + val logback_core = Dependencies.logback_core // testing - val scalatest = "org.scalatest" % "scalatest" % SCALATEST_VERSION % "test" - val junit = "junit" % "junit" % "4.5" % "test" + val junit = Dependencies.junit + val scalatest = Dependencies.scalatest + + override def bndImportPackage = "javax.transaction;version=1.1" :: super.bndImportPackage.toList } - class AkkaAMQPProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { - val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" - val rabbit = "com.rabbitmq" % "amqp-client" % "1.8.1" % "compile" + // ------------------------------------------------------------------------------------------------------------------- + // akka-amqp subproject + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaAMQPProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + val commons_io = Dependencies.commons_io + val rabbit = Dependencies.rabbit + val protobuf = Dependencies.protobuf // testing - val multiverse = "org.multiverse" % "multiverse-alpha" % MULTIVERSE_VERSION % "test" intransitive() - val scalatest = "org.scalatest" % "scalatest" % SCALATEST_VERSION % "test" - val junit = "junit" % "junit" % "4.5" % "test" + val junit = Dependencies.junit + val multiverse = Dependencies.multiverse + val scalatest = Dependencies.scalatest } - class AkkaHttpProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { - val jackson_core_asl = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile" - val stax_api = "javax.xml.stream" % "stax-api" % "1.0-2" % "compile" - val servlet = "javax.servlet" % "servlet-api" % "2.5" % "compile" - val jersey = "com.sun.jersey" % "jersey-core" % JERSEY_VERSION % "compile" - val jersey_server = "com.sun.jersey" % "jersey-server" % JERSEY_VERSION % "compile" - val jersey_json = "com.sun.jersey" % "jersey-json" % JERSEY_VERSION % "compile" - val jersey_contrib = "com.sun.jersey.contribs" % "jersey-scala" % JERSEY_VERSION % "compile" - val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" - val grizzly = "com.sun.grizzly" % "grizzly-comet-webserver" % "1.9.18-i" % "compile" - val atmo = "org.atmosphere" % "atmosphere-annotations" % ATMO_VERSION % "compile" - val atmo_jersey = "org.atmosphere" % "atmosphere-jersey" % ATMO_VERSION % "compile" - val atmo_runtime = "org.atmosphere" % "atmosphere-runtime" % ATMO_VERSION % "compile" - val atmo_tomcat = "org.atmosphere" % "atmosphere-compat-tomcat" % ATMO_VERSION % "compile" - val atmo_weblogic = "org.atmosphere" % "atmosphere-compat-weblogic" % ATMO_VERSION % "compile" - val atmo_jbossweb = "org.atmosphere" % "atmosphere-compat-jbossweb" % ATMO_VERSION % "compile" - val commons_logging = "commons-logging" % "commons-logging" % "1.1.1" % "compile" - val annotation = "javax.annotation" % "jsr250-api" % "1.0" % "compile" + // ------------------------------------------------------------------------------------------------------------------- + // akka-http subproject + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaHttpProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + val annotation = Dependencies.annotation + val atmo = Dependencies.atmo + val atmo_jbossweb = Dependencies.atmo_jbossweb + val atmo_jersey = Dependencies.atmo_jersey + val atmo_runtime = Dependencies.atmo_runtime + val atmo_tomcat = Dependencies.atmo_tomcat + val atmo_weblogic = Dependencies.atmo_weblogic + val grizzly = Dependencies.grizzly + val jackson_core_asl = Dependencies.jackson_core_asl + val jersey = Dependencies.jersey + val jersey_contrib = Dependencies.jersey_contrib + val jersey_json = Dependencies.jersey_json + val jersey_server = Dependencies.jersey_server + val jsr311 = Dependencies.jsr311 + val servlet = Dependencies.servlet + val stax_api = Dependencies.stax_api // testing - val scalatest = "org.scalatest" % "scalatest" % SCALATEST_VERSION % "test" - val junit = "junit" % "junit" % "4.5" % "test" - val mockito = "org.mockito" % "mockito-all" % "1.8.1" % "test" + val junit = Dependencies.junit + val mockito = Dependencies.mockito + val scalatest = Dependencies.scalatest } - class AkkaCamelProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { - val camel_core = "org.apache.camel" % "camel-core" % CAMEL_VERSION % "compile" + // ------------------------------------------------------------------------------------------------------------------- + // akka-camel subproject + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaCamelProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + val camel_core = Dependencies.camel_core } - class AkkaPersistenceCommonProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { - val thrift = "com.facebook" % "thrift" % "r917130" % "compile" - val commons_pool = "commons-pool" % "commons-pool" % "1.5.4" % "compile" - } - - class AkkaRedisProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { - val redis = "com.redis" % "redisclient" % "2.8.0-1.4" % "compile" - val commons_codec = "commons-codec" % "commons-codec" % "1.4" % "compile" - override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil - } - - class AkkaMongoProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { - val mongo = "org.mongodb" % "mongo-java-driver" % "1.4" % "compile" - override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil - } - - class AkkaCassandraProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { - val cassandra = "org.apache.cassandra" % "cassandra" % CASSANDRA_VERSION % "compile" - val slf4j = "org.slf4j" % "slf4j-api" % "1.6.0" % "compile" - val slf4j_log4j = "org.slf4j" % "slf4j-log4j12" % "1.6.0" % "compile" - val log4j = "log4j" % "log4j" % "1.2.15" % "compile" - // testing - val high_scale = "org.apache.cassandra" % "high-scale-lib" % CASSANDRA_VERSION % "test" - val cassandra_clhm = "org.apache.cassandra" % "clhm-production" % CASSANDRA_VERSION % "test" - val commons_coll = "commons-collections" % "commons-collections" % "3.2.1" % "test" - val google_coll = "com.google.collections" % "google-collections" % "1.0" % "test" - override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil - } + // ------------------------------------------------------------------------------------------------------------------- + // akka-persistence subproject + // ------------------------------------------------------------------------------------------------------------------- class AkkaPersistenceParentProject(info: ProjectInfo) extends ParentProject(info) { lazy val akka_persistence_common = project("akka-persistence-common", "akka-persistence-common", @@ -281,61 +428,230 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { new AkkaCassandraProject(_), akka_persistence_common) } - class AkkaKernelProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) + // ------------------------------------------------------------------------------------------------------------------- + // akka-persistence-common subproject + // ------------------------------------------------------------------------------------------------------------------- - class AkkaSpringProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { - val spring_beans = "org.springframework" % "spring-beans" % SPRING_VERSION % "compile" - val spring_context = "org.springframework" % "spring-context" % SPRING_VERSION % "compile" + class AkkaPersistenceCommonProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + val commons_pool = Dependencies.commons_pool + val thrift = Dependencies.thrift + } + + // ------------------------------------------------------------------------------------------------------------------- + // akka-persistence-redis subproject + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaRedisProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + val commons_codec = Dependencies.commons_codec + val redis = Dependencies.redis + + override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil + } + + // ------------------------------------------------------------------------------------------------------------------- + // akka-persistence-mongo subproject + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaMongoProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + val mongo = Dependencies.mongo + + override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil + } + + // ------------------------------------------------------------------------------------------------------------------- + // akka-persistence-cassandra subproject + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaCassandraProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + val cassandra = Dependencies.cassandra // testing - val camel_spring = "org.apache.camel" % "camel-spring" % CAMEL_VERSION % "test" - val scalatest = "org.scalatest" % "scalatest" % SCALATEST_VERSION % "test" - val junit = "junit" % "junit" % "4.5" % "test" + val cassandra_clhm = Dependencies.cassandra_clhm + val commons_coll = Dependencies.commons_coll + val google_coll = Dependencies.google_coll + val high_scale = Dependencies.high_scale + + override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil } - class AkkaJTAProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { - val atomikos_transactions = "com.atomikos" % "transactions" % "3.2.3" % "compile" - val atomikos_transactions_jta = "com.atomikos" % "transactions-jta" % "3.2.3" % "compile" - val atomikos_transactions_api = "com.atomikos" % "transactions-api" % "3.2.3" % "compile" + // ------------------------------------------------------------------------------------------------------------------- + // akka-kernel subproject + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaKernelProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) + + // ------------------------------------------------------------------------------------------------------------------- + // akka-spring subproject + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaSpringProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + val spring_beans = Dependencies.spring_beans + val spring_context = Dependencies.spring_context + + // testing + val camel_spring = Dependencies.camel_spring + val junit = Dependencies.junit + val scalatest = Dependencies.scalatest + } + + // ------------------------------------------------------------------------------------------------------------------- + // akka-jta subproject + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaJTAProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + val atomikos_transactions = Dependencies.atomikos_transactions + val atomikos_transactions_api = Dependencies.atomikos_transactions_api + val atomikos_transactions_jta = Dependencies.atomikos_transactions_jta + val jta_1_1 = Dependencies.jta_1_1 //val atomikos_transactions_util = "com.atomikos" % "transactions-util" % "3.2.3" % "compile" - val jta_spec = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile" intransitive() } - // ================= EXAMPLES ================== - class AkkaSampleAntsProject(info: ProjectInfo) extends DefaultSpdeProject(info) with CodeFellowPlugin { - val scalaToolsSnapshots = ScalaToolsSnapshots + // ------------------------------------------------------------------------------------------------------------------- + // OSGi stuff + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaOSGiParentProject(info: ProjectInfo) extends ParentProject(info) { + lazy val akka_osgi_dependencies_bundle = project("akka-osgi-dependencies-bundle", "akka-osgi-dependencies-bundle", + new AkkaOSGiDependenciesBundleProject(_), akka_kernel, akka_jta) // akka_kernel does not depend on akka_jta (why?) therefore we list akka_jta here + lazy val akka_osgi_assembly = project("akka-osgi-assembly", "akka-osgi-assembly", + new AkkaOSGiAssemblyProject(_), akka_osgi_dependencies_bundle, akka_core, akka_amqp, akka_http, + akka_camel, akka_spring, akka_jta, akka_persistence.akka_persistence_common, + akka_persistence.akka_persistence_redis, akka_persistence.akka_persistence_mongo, + akka_persistence.akka_persistence_cassandra) + } + + class AkkaOSGiDependenciesBundleProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with BNDPlugin { + override def bndClasspath = compileClasspath + override def bndPrivatePackage = Seq("") + override def bndImportPackage = Seq("*;resolution:=optional") + override def bndExportPackage = Seq( + "org.aopalliance.*;version=1.0.0", + + // Provided by other bundles + "!se.scalablesolutions.akka.*", + "!net.liftweb.*", + "!com.google.inject.*", + "!javax.transaction.*", + "!javax.ws.rs.*", + "!javax.jms.*", + "!javax.transaction,*", + "!org.apache.commons.io.*", + "!org.apache.commons.pool.*", + "!org.codehaus.jackson.*", + "!org.jboss.netty.*", + "!org.springframework.*", + "!org.apache.camel.*", + "!org.fusesource.commons.management.*", + + "*;version=0.0.0") + } + + class AkkaOSGiAssemblyProject(info: ProjectInfo) extends DefaultProject(info) { + + // Scala bundle + val scala_bundle = "com.weiglewilczek.scala-lang-osgi" % "scala-library" % buildScalaVersion % "compile" intransitive + + // Lift bundles +// val lift_util = Dependencies.lift_util.intransitive +// val lift_actor = "net.liftweb" % "lift-actor" % LIFT_VERSION % "compile" intransitive +// val lift_common = "net.liftweb" % "lift-common" % LIFT_VERSION % "compile" intransitive +// val lift_json = "net.liftweb" % "lift-json" % LIFT_VERSION % "compile" intransitive + + // Camel bundles + val camel_core = Dependencies.camel_core.intransitive + val fusesource_commonman = "org.fusesource.commonman" % "commons-management" % "1.0" intransitive + + // Spring bundles + val spring_beans = Dependencies.spring_beans.intransitive + val spring_context = Dependencies.spring_context.intransitive + val spring_aop = "org.springframework" % "spring-aop" % SPRING_VERSION % "compile" intransitive + val spring_asm = "org.springframework" % "spring-asm" % SPRING_VERSION % "compile" intransitive + val spring_core = "org.springframework" % "spring-core" % SPRING_VERSION % "compile" intransitive + val spring_expression = "org.springframework" % "spring-expression" % SPRING_VERSION % "compile" intransitive + val spring_jms = "org.springframework" % "spring-jms" % SPRING_VERSION % "compile" intransitive + val spring_tx = "org.springframework" % "spring-tx" % SPRING_VERSION % "compile" intransitive + + val commons_codec = Dependencies.commons_codec.intransitive + val commons_io = Dependencies.commons_io.intransitive + val commons_pool = Dependencies.commons_pool.intransitive + val guicey = Dependencies.guicey.intransitive + val jackson = Dependencies.jackson.intransitive + val jackson_core = Dependencies.jackson_core.intransitive + val jsr311 = Dependencies.jsr311.intransitive + val jta_1_1 = Dependencies.jta_1_1.intransitive + val netty = Dependencies.netty.intransitive + val commons_fileupload = "commons-fileupload" % "commons-fileupload" % "1.2.1" % "compile" intransitive + val jms_1_1 = "org.apache.geronimo.specs" % "geronimo-jms_1.1_spec" % "1.1.1" % "compile" intransitive + val joda = "joda-time" % "joda-time" % "1.6" intransitive + + override def packageAction = + task { + val libs: Seq[Path] = managedClasspath(config("compile")).get.toSeq + val prjs: Seq[Path] = info.dependencies.toSeq.asInstanceOf[Seq[DefaultProject]] map { _.jarPath } + val all = libs ++ prjs + val destination = outputPath / "bundles" + FileUtilities.copyFlat(all, destination, log) + log info "Copied %s bundles to %s".format(all.size, destination) + None + } + + override def artifacts = Set.empty + } + + // ------------------------------------------------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaTypedActorTestProject(info: ProjectInfo) extends DefaultProject(info) { + // testing + val junit = "junit" % "junit" % "4.5" % "test" + val jmock = "org.jmock" % "jmock" % "2.4.0" % "test" + } + + // ------------------------------------------------------------------------------------------------------------------- + // Examples + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaSampleAntsProject(info: ProjectInfo) extends DefaultSpdeProject(info) { + //val scalaToolsSnapshots = ScalaToolsSnapshots override def spdeSourcePath = mainSourcePath / "spde" } - class AkkaSampleChatProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) with CodeFellowPlugin - class AkkaSamplePubSubProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) with CodeFellowPlugin + class AkkaSampleChatProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) + class AkkaSamplePubSubProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) + class AkkaSampleFSMProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) + + class AkkaSampleLiftProject(info: ProjectInfo) extends DefaultWebProject(info) with DeployProject { + //val commons_logging = Dependencies.commons_logging + val lift_util = Dependencies.lift_util + val lift_webkit = Dependencies.lift_webkit + val servlet = Dependencies.servlet - class AkkaSampleLiftProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) with CodeFellowPlugin { - val commons_logging = "commons-logging" % "commons-logging" % "1.1.1" % "compile" - val lift = "net.liftweb" % "lift-webkit" % LIFT_VERSION % "compile" - val lift_util = "net.liftweb" % "lift-util" % LIFT_VERSION % "compile" - val servlet = "javax.servlet" % "servlet-api" % "2.5" % "compile" // testing - val jetty = "org.mortbay.jetty" % "jetty" % "6.1.22" % "test" - val junit = "junit" % "junit" % "4.5" % "test" + val jettyServer = Dependencies.jettyServer + val junit = Dependencies.junit + + def deployPath = AkkaParentProject.this.deployPath + override def jarPath = warPath } - class AkkaSampleRestJavaProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) with CodeFellowPlugin + class AkkaSampleRestJavaProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) - class AkkaSampleRemoteProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) with CodeFellowPlugin + class AkkaSampleRemoteProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) - class AkkaSampleRestScalaProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) with CodeFellowPlugin { - val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1.1" % "compile" + class AkkaSampleRestScalaProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) { + val jsr311 = Dependencies.jsr311 } - class AkkaSampleCamelProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) with CodeFellowPlugin { + class AkkaSampleCamelProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) { + //Must be like this to be able to exclude the geronimo-servlet_2.4_spec which is a too old Servlet spec override def ivyXML = - + @@ -345,10 +661,16 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { } - class AkkaSampleSecurityProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) with CodeFellowPlugin { - val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1.1" % "compile" - val jsr250 = "javax.annotation" % "jsr250-api" % "1.0" % "compile" - val commons_codec = "commons-codec" % "commons-codec" % "1.4" % "compile" + class AkkaSampleSecurityProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) { + val commons_codec = Dependencies.commons_codec + val jsr250 = Dependencies.jsr250 + val jsr311 = Dependencies.jsr311 + } + + class AkkaSampleOSGiProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with BNDPlugin { + val osgi_core = Dependencies.osgi_core + override lazy val bndBundleActivator = Some("se.scalablesolutions.akka.sample.osgi.Activator") + override lazy val bndExportPackage = Nil // Necessary because of mixing-in AkkaDefaultProject which exports all ...akka.* packages! } class AkkaSamplesParentProject(info: ProjectInfo) extends ParentProject(info) { @@ -358,6 +680,8 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { new AkkaSampleChatProject(_), akka_kernel) lazy val akka_sample_pubsub = project("akka-sample-pubsub", "akka-sample-pubsub", new AkkaSamplePubSubProject(_), akka_kernel) + lazy val akka_sample_fsm = project("akka-sample-fsm", "akka-sample-fsm", + new AkkaSampleFSMProject(_), akka_kernel) lazy val akka_sample_lift = project("akka-sample-lift", "akka-sample-lift", new AkkaSampleLiftProject(_), akka_kernel) lazy val akka_sample_rest_java = project("akka-sample-rest-java", "akka-sample-rest-java", @@ -370,10 +694,14 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { new AkkaSampleSecurityProject(_), akka_kernel) lazy val akka_sample_remote = project("akka-sample-remote", "akka-sample-remote", new AkkaSampleRemoteProject(_), akka_kernel) + lazy val akka_sample_osgi = project("akka-sample-osgi", "akka-sample-osgi", + new AkkaSampleOSGiProject(_), akka_core) } - // ------------------------------------------------------------ - // helper functions + // ------------------------------------------------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------------------------------------------------- + def removeDupEntries(paths: PathFinder) = Path.lazyPathFinder { val mapped = paths.get map { p => (p.relativePath, p) } @@ -386,8 +714,8 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { ((outputPath ##) / defaultJarName) +++ mainResources +++ mainDependencies.scalaJars +++ - descendents(info.projectPath, "*.conf") +++ descendents(info.projectPath / "scripts", "run_akka.sh") +++ + descendents(info.projectPath / "scripts", "akka-init-script.sh") +++ descendents(info.projectPath / "dist", "*.jar") +++ descendents(info.projectPath / "deploy", "*.jar") +++ descendents(path("lib") ##, "*.jar") +++ @@ -401,25 +729,29 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { def akkaArtifacts = descendents(info.projectPath / "dist", "*" + buildScalaVersion + "-" + version + ".jar") // ------------------------------------------------------------ - class AkkaDefaultProject(info: ProjectInfo, val deployPath: Path) extends DefaultProject(info) with DeployProject + class AkkaDefaultProject(info: ProjectInfo, val deployPath: Path) extends DefaultProject(info) with DeployProject with OSGiProject +} - trait DeployProject extends DefaultProject { - // defines where the deployTask copies jars to - def deployPath: Path +trait DeployProject { self: BasicScalaProject => + // defines where the deployTask copies jars to + def deployPath: Path - lazy val dist = distAction - def distAction = deployTask(jarPath, packageDocsJar, packageSrcJar, deployPath, true, true, true) dependsOn( - `package`, packageDocs, packageSrc) describedAs("Deploying") - def deployTask(jar: Path, docs: Path, src: Path, toDir: Path, - genJar: Boolean, genDocs: Boolean, genSource: Boolean) = task { - gen(jar, toDir, genJar, "Deploying bits") orElse - gen(docs, toDir, genDocs, "Deploying docs") orElse - gen(src, toDir, genSource, "Deploying sources") - } - private def gen(jar: Path, toDir: Path, flag: Boolean, msg: String): Option[String] = - if (flag) { - log.info(msg + " " + jar) - FileUtilities.copyFile(jar, toDir / jar.name, log) - } else None + lazy val dist = deployTask(jarPath, packageDocsJar, packageSrcJar, deployPath, true, true, true) dependsOn( + `package`, packageDocs, packageSrc) describedAs("Deploying") + def deployTask(jar: Path, docs: Path, src: Path, toDir: Path, + genJar: Boolean, genDocs: Boolean, genSource: Boolean) = task { + def gen(jar: Path, toDir: Path, flag: Boolean, msg: String): Option[String] = + if (flag) { + log.info(msg + " " + jar) + FileUtilities.copyFile(jar, toDir / jar.name, log) + } else None + + gen(jar, toDir, genJar, "Deploying bits") orElse + gen(docs, toDir, genDocs, "Deploying docs") orElse + gen(src, toDir, genSource, "Deploying sources") } } + +trait OSGiProject extends BNDPlugin { self: DefaultProject => + override def bndExportPackage = Seq("se.scalablesolutions.akka.*;version=%s".format(projectVersion.value)) +} diff --git a/project/plugins/Plugins.scala b/project/plugins/Plugins.scala index 92b9943998..4f4ec09479 100644 --- a/project/plugins/Plugins.scala +++ b/project/plugins/Plugins.scala @@ -1,10 +1,29 @@ import sbt._ class Plugins(info: ProjectInfo) extends PluginDefinition(info) { - val databinderRepo = "Databinder Repository" at "http://databinder.net/repo" - val embeddedRepo = "Embedded Repo" at (info.projectPath / "embedded-repo").asURL.toString - val spdeSbt = "us.technically.spde" % "spde-sbt-plugin" % "0.4.1" - val codeFellow = "de.tuxed" % "codefellow-plugin" % "0.3" // for code completion and more in VIM -// val repo = "GH-pages repo" at "http://mpeltonen.github.com/maven/" -// val idea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.1-SNAPSHOT" + + // ------------------------------------------------------------------------------------------------------------------- + // All repositories *must* go here! See ModuleConigurations below. + // ------------------------------------------------------------------------------------------------------------------- + object Repositories { + lazy val AquteRepo = "aQute Maven Repository" at "http://www.aqute.biz/repo" + lazy val DatabinderRepo = "Databinder Repository" at "http://databinder.net/repo" + lazy val EmbeddedRepo = "Embedded Repo" at (info.projectPath / "embedded-repo").asURL.toString + } + + // ------------------------------------------------------------------------------------------------------------------- + // ModuleConfigurations + // Every dependency that cannot be resolved from the built-in repositories (Maven Central and Scala Tools Releases) + // must be resolved from a ModuleConfiguration. This will result in a significant acceleration of the update action. + // Therefore, if repositories are defined, this must happen as def, not as val. + // ------------------------------------------------------------------------------------------------------------------- + import Repositories._ + lazy val aquteModuleConfig = ModuleConfiguration("biz.aQute", AquteRepo) + lazy val spdeModuleConfig = ModuleConfiguration("us.technically.spde", DatabinderRepo) + + // ------------------------------------------------------------------------------------------------------------------- + // Dependencies + // ------------------------------------------------------------------------------------------------------------------- + lazy val bnd4sbt = "com.weiglewilczek.bnd4sbt" % "bnd4sbt" % "1.0.0.RC4" + lazy val spdeSbt = "us.technically.spde" % "spde-sbt-plugin" % "0.4.1" } diff --git a/scripts/akka-init-script.sh b/scripts/akka-init-script.sh new file mode 100644 index 0000000000..752d675b55 --- /dev/null +++ b/scripts/akka-init-script.sh @@ -0,0 +1,96 @@ +#! /bin/bash + +#Original /etc/init.d/skeleton modified for http://mydebian.blogdns.org + +# PATH should only include /usr/* if it runs after the mountnfs.sh +script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="my cool akka app" +NAME="cool" +DAEMON=/usr/bin/java +export AKKA_HOME=/var/.../servers/akka +AKKA_JAR=$AKKA_HOME/akka.jar +LOGBACK=$AKKA_HOME/config/logback.xml +JVMFLAGS="-Xms512M -Xmx3072M -XX:+UseConcMarkSweepGC - +Dlogback.configurationFile="$LOGBACK +DAEMON_ARGS=$JVMFLAGS" -jar "$AKKA_JAR +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME +#the user that will run the script +USER=cool-user +VERBOSE=1 + +# NO NEED TO MODIFY THE LINES BELOW + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + start-stop-daemon --start --quiet -b -m -p $PIDFILE --exec $DAEMON -- +$DAEMON_ARGS \ + || return 2 +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE + RETVAL="$?" + rm -f $PIDFILE + return "$RETVAL" +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + restart) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2 + exit 3 + ;; +esac \ No newline at end of file diff --git a/scripts/run_akka.sh b/scripts/run_akka.sh index 2d87a08148..f2ededd90f 100755 --- a/scripts/run_akka.sh +++ b/scripts/run_akka.sh @@ -1,6 +1,6 @@ #!/bin/bash cd $AKKA_HOME -VERSION=akka_2.8.0.RC3-0.10 +VERSION=akka_2.8.0-0.10 TARGET_DIR=dist/$VERSION/$1 shift 1 VMARGS=$@