From f4b6fb45196f39fdcf067c447c148e700839adb0 Mon Sep 17 00:00:00 2001 From: momania Date: Thu, 23 Sep 2010 17:07:51 +0200 Subject: [PATCH 01/32] - Adding java api to AMQP module - Reorg of params, especially declaration attributes and exhange name/params --- .../se/scalablesolutions/akka/amqp/AMQP.scala | 306 ++++++++++++++---- .../akka/amqp/ConsumerActor.scala | 65 ++-- .../akka/amqp/ExampleSession.scala | 48 +-- .../akka/amqp/FaultTolerantChannelActor.scala | 23 +- .../amqp/FaultTolerantConnectionActor.scala | 2 +- .../akka/amqp/ProducerActor.scala | 11 +- .../scalablesolutions/akka/amqp/rpc/RPC.scala | 43 ++- .../akka/amqp/rpc/RpcClientActor.scala | 2 +- project/build/AkkaProject.scala | 68 ++-- 9 files changed, 372 insertions(+), 196 deletions(-) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala index cd73d27e03..be53297cd3 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala @@ -8,6 +8,7 @@ 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 ConnectionFactory._ import com.rabbitmq.client.AMQP.BasicProperties import java.lang.{String, IllegalArgumentException} @@ -19,56 +20,208 @@ import java.lang.{String, IllegalArgumentException} * @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) + /** + * Parameters used to make the connection to the amqp broker. Uses the rabbitmq defaults. + */ + case class ConnectionParameters( + host: String = DEFAULT_HOST, + port: Int = DEFAULT_AMQP_PORT, + username: String = DEFAULT_USER, + password: String = DEFAULT_PASS, + virtualHost: String = DEFAULT_VHOST, + initReconnectDelay: Long = 5000, + connectionCallback: Option[ActorRef] = None) { + + // Needed for Java API usage + def this() = this (DEFAULT_HOST, DEFAULT_AMQP_PORT, DEFAULT_USER, DEFAULT_PASS, DEFAULT_VHOST, 5000, None) + + // Needed for Java API usage + def this(host: String, port: Int, username: String, password: String, virtualHost: String) = + this (host, port, username, password, virtualHost, 5000, None) + + // Needed for Java API usage + def this(host: String, port: Int, username: String, password: String, virtualHost: String, initReconnectDelay: Long, connectionCallback: ActorRef) = + this (host, port, username, password, virtualHost, initReconnectDelay, Some(connectionCallback)) + + // Needed for Java API usage + def this(connectionCallback: ActorRef) = + this (DEFAULT_HOST, DEFAULT_AMQP_PORT, DEFAULT_USER, DEFAULT_PASS, DEFAULT_VHOST, 5000, Some(connectionCallback)) + + } + + /** + * Additional parameters for the channel + */ case class ChannelParameters( shutdownListener: Option[ShutdownListener] = None, - channelCallback: Option[ActorRef] = None) + channelCallback: Option[ActorRef] = None) { + // Needed for Java API usage + def this() = this (None, None) + + // Needed for Java API usage + def this(channelCallback: ActorRef) = this (None, Some(channelCallback)) + + // Needed for Java API usage + def this(shutdownListener: ShutdownListener, channelCallback: ActorRef) = + this (Some(shutdownListener), Some(channelCallback)) + } + + /** + * Declaration type used for either exchange or queue declaration + */ + sealed trait Declaration + case object NoActionDeclaration extends Declaration + case object PassiveDeclaration extends Declaration + case class ActiveDeclaration(durable: Boolean = false, autoDelete: Boolean = true, exclusive: Boolean = false) extends Declaration { + + // Needed for Java API usage + def this() = this (false, true, false) + + // Needed for Java API usage + def this(durable: Boolean, autoDelete: Boolean) = this (durable, autoDelete, false) + } + + /** + * Exchange specific parameters + */ case class ExchangeParameters( exchangeName: String, - exchangeType: ExchangeType, - exchangeDurable: Boolean = false, - exchangeAutoDelete: Boolean = true, - exchangePassive: Boolean = false, - configurationArguments: Map[String, AnyRef] = Map()) + exchangeType: ExchangeType = ExchangeType.Topic, + exchangeDeclaration: Declaration = new ActiveDeclaration(), + configurationArguments: Map[String, AnyRef] = Map.empty()) { + // Needed for Java API usage + def this(exchangeName: String) = + this (exchangeName, ExchangeType.Topic, new ActiveDeclaration(), Map.empty()) + + // Needed for Java API usage + def this(exchangeName: String, exchangeType: ExchangeType) = + this (exchangeName, exchangeType, new ActiveDeclaration(), Map.empty()) + + // Needed for Java API usage + def this(exchangeName: String, exchangeType: ExchangeType, exchangeDeclaration: Declaration) = + this (exchangeName, exchangeType, exchangeDeclaration, Map.empty()) + } + + /** + * Producer specific parameters + */ case class ProducerParameters( - exchangeParameters: ExchangeParameters, + exchangeParameters: Option[ExchangeParameters] = None, producerId: Option[String] = None, returnListener: Option[ReturnListener] = None, - channelParameters: Option[ChannelParameters] = None) + channelParameters: Option[ChannelParameters] = None) { + def this() = this(None, None, None, None) + + // Needed for Java API usage + def this(exchangeParameters: ExchangeParameters) = this (Some(exchangeParameters), None, None, None) + + // Needed for Java API usage + def this(exchangeParameters: ExchangeParameters, producerId: String) = + this (Some(exchangeParameters), Some(producerId), None, None) + + // Needed for Java API usage + def this(exchangeParameters: ExchangeParameters, returnListener: ReturnListener) = + this (Some(exchangeParameters), None, Some(returnListener), None) + + // Needed for Java API usage + def this(exchangeParameters: ExchangeParameters, channelParameters: ChannelParameters) = + this (Some(exchangeParameters), None, None, Some(channelParameters)) + + // Needed for Java API usage + def this(exchangeParameters: ExchangeParameters, producerId: String, returnListener: ReturnListener, channelParameters: ChannelParameters) = + this (Some(exchangeParameters), Some(producerId), Some(returnListener), Some(channelParameters)) + } + + /** + * Consumer specific parameters + */ 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, + exchangeParameters: Option[ExchangeParameters], + queueDeclaration: Declaration = new ActiveDeclaration(), selfAcknowledging: Boolean = true, channelParameters: Option[ChannelParameters] = None) { - if (queueDurable && queueName.isEmpty) { - throw new IllegalArgumentException("A queue name is required when requesting a durable queue.") + + if (queueName.isEmpty) { + queueDeclaration match { + case ActiveDeclaration(true, _, _) => + throw new IllegalArgumentException("A queue name is required when requesting a durable queue.") + case PassiveDeclaration => + throw new IllegalArgumentException("A queue name is required when requesting passive declaration.") + case NoActionDeclaration => () // ignore + } } + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef) = + this (routingKey, deliveryHandler, None, None, new ActiveDeclaration(), true, None) + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, channelParameters: ChannelParameters) = + this (routingKey, deliveryHandler, None, None, new ActiveDeclaration(), true, Some(channelParameters)) + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, selfAcknowledging: Boolean) = + this (routingKey, deliveryHandler, None, None, new ActiveDeclaration(), selfAcknowledging, None) + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, selfAcknowledging: Boolean, channelParameters: ChannelParameters) = + this (routingKey, deliveryHandler, None, None, new ActiveDeclaration(), selfAcknowledging, Some(channelParameters)) + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, queueName: String) = + this (routingKey, deliveryHandler, Some(queueName), None, new ActiveDeclaration(), true, None) + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, queueName: String, queueDeclaration: Declaration, selfAcknowledging: Boolean, channelParameters: ChannelParameters) = + this (routingKey, deliveryHandler, Some(queueName), None, queueDeclaration, selfAcknowledging, Some(channelParameters)) + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, exchangeParameters: ExchangeParameters) = + this (routingKey, deliveryHandler, None, Some(exchangeParameters), new ActiveDeclaration(), true, None) + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, exchangeParameters: ExchangeParameters, selfAcknowledging: Boolean) = + this (routingKey, deliveryHandler, None, Some(exchangeParameters), new ActiveDeclaration(), selfAcknowledging, None) + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, queueName: String, exchangeParameters: ExchangeParameters) = + this (routingKey, deliveryHandler, Some(queueName), Some(exchangeParameters), new ActiveDeclaration(), true, None) + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, queueName: String, exchangeParameters: ExchangeParameters, queueDeclaration: Declaration) = + this (routingKey, deliveryHandler, Some(queueName), Some(exchangeParameters), queueDeclaration, true, None) + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, queueName: String, exchangeParameters: ExchangeParameters, queueDeclaration: Declaration, selfAcknowledging: Boolean) = + this (routingKey, deliveryHandler, Some(queueName), Some(exchangeParameters), queueDeclaration, selfAcknowledging, None) + + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, queueName: String, exchangeParameters: ExchangeParameters, queueDeclaration: Declaration, selfAcknowledging: Boolean, channelParameters: ChannelParameters) = + this (routingKey, deliveryHandler, Some(queueName), Some(exchangeParameters), queueDeclaration, selfAcknowledging, Some(channelParameters)) + + // How about that for some overloading... huh? :P (yes, I know, there are still posibilities left...sue me!) + // Who said java is easy :( } - def newConnection(connectionParameters: ConnectionParameters = new ConnectionParameters): ActorRef = { + def newConnection(connectionParameters: ConnectionParameters = new ConnectionParameters()): ActorRef = { val connection = actorOf(new FaultTolerantConnectionActor(connectionParameters)) supervisor.startLink(connection) connection ! Connect connection } + // Needed for Java API usage + def newConnection(): ActorRef = { + newConnection(new ConnectionParameters()) + } + def newProducer(connection: ActorRef, producerParameters: ProducerParameters): ActorRef = { val producer: ActorRef = Actor.actorOf(new ProducerActor(producerParameters)) connection.startLink(producer) @@ -86,7 +239,7 @@ object AMQP { } /** - * Convenience + * Convenience */ class ProducerClient[O](client: ActorRef, routingKey: String, toBinary: ToBinary[O]) { def send(request: O, replyTo: Option[String] = None) = { @@ -95,20 +248,19 @@ object AMQP { client ! Message(toBinary.toBinary(request), routingKey, false, false, Some(basicProperties)) } - def stop = client.stop + 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] = { + exchangeName: Option[String], + routingKey: Option[String] = None, + producerId: Option[String] = None): ProducerClient[String] = { - val exchangeParameters = ExchangeParameters(exchange, ExchangeType.Topic, - exchangeDurable = durable, exchangeAutoDelete = autoDelete) - val rKey = routingKey.getOrElse("%s.request".format(exchange)) + if (exchangeName.isEmpty && routingKey.isEmpty) { + throw new IllegalArgumentException("Either exchange name or routing key is mandatory") + } + val exchangeParameters = exchangeName.flatMap(name => Some(ExchangeParameters(name))) + val rKey = routingKey.getOrElse("%s.request".format(exchangeName.get)) val producerRef = newProducer(connection, ProducerParameters(exchangeParameters, producerId)) val toBinary = new ToBinary[String] { @@ -117,37 +269,57 @@ object AMQP { new ProducerClient(producerRef, rKey, toBinary) } + // Needed for Java API usage + def newStringProducer(connection: ActorRef): ProducerClient[String] = { + newStringProducer(connection, None, None, None) + } + // Needed for Java API usage + def newStringProducer(connection: ActorRef, exchangeName: String): ProducerClient[String] = { + newStringProducer(connection, Some(exchangeName), None, None) + } + + // Needed for Java API usage + def newStringProducer(connection: ActorRef, exchangeName: String, routingKey: String): ProducerClient[String] = { + newStringProducer(connection, Some(exchangeName), Some(routingKey), None) + } + + // Needed for Java API usage + def newStringProducer(connection: ActorRef, exchangeName: String, routingKey: String, producerId: String): ProducerClient[String] = { + newStringProducer(connection, Some(exchangeName), Some(routingKey), Some(producerId)) + } + + def newStringConsumer(connection: ActorRef, - exchange: String, - handler: String => Unit, - routingKey: Option[String] = None, - queueName: Option[String] = None, - durable: Boolean = false, - autoDelete: Boolean = true): ActorRef = { + handler: String => Unit, + exchangeName: Option[String], + routingKey: Option[String] = None, + queueName: Option[String] = None): ActorRef = { + + if (exchangeName.isEmpty && routingKey.isEmpty) { + throw new IllegalArgumentException("Either exchange name or routing key is mandatory") + } 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 exchangeParameters = exchangeName.flatMap(name => Some(ExchangeParameters(name))) + val rKey = routingKey.getOrElse("%s.request".format(exchangeName.get)) val qName = queueName.getOrElse("%s.in".format(rKey)) - newConsumer(connection, ConsumerParameters(exchangeParameters, rKey, deliveryHandler, Some(qName), durable, autoDelete)) + newConsumer(connection, ConsumerParameters(rKey, deliveryHandler, Some(qName), exchangeParameters)) } 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] = { + exchangeName: Option[String], + routingKey: Option[String] = None, + producerId: Option[String] = None): ProducerClient[O] = { - val exchangeParameters = ExchangeParameters(exchange, ExchangeType.Topic, - exchangeDurable = durable, exchangeAutoDelete = autoDelete) - val rKey = routingKey.getOrElse("%s.request".format(exchange)) + if (exchangeName.isEmpty && routingKey.isEmpty) { + throw new IllegalArgumentException("Either exchange name or routing key is mandatory") + } + val exchangeParameters = exchangeName.flatMap(name => Some(ExchangeParameters(name))) + val rKey = routingKey.getOrElse("%s.request".format(exchangeName.get)) val producerRef = newProducer(connection, ProducerParameters(exchangeParameters, producerId)) new ProducerClient(producerRef, rKey, new ToBinary[O] { @@ -156,12 +328,14 @@ object AMQP { } 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 = { + handler: I => Unit, + exchangeName: Option[String], + routingKey: Option[String] = None, + queueName: Option[String] = None)(implicit manifest: Manifest[I]): ActorRef = { + + if (exchangeName.isEmpty && routingKey.isEmpty) { + throw new IllegalArgumentException("Either exchange name or routing key is mandatory") + } val deliveryHandler = actor { case Delivery(payload, _, _, _, _) => { @@ -169,22 +343,20 @@ object AMQP { } } - val exchangeParameters = ExchangeParameters(exchange, ExchangeType.Topic, - exchangeDurable = durable, exchangeAutoDelete = autoDelete) - val rKey = routingKey.getOrElse("%s.request".format(exchange)) + val exchangeParameters = exchangeName.flatMap(name => Some(ExchangeParameters(name))) + val rKey = routingKey.getOrElse("%s.request".format(exchangeName.get)) val qName = queueName.getOrElse("%s.in".format(rKey)) - newConsumer(connection, ConsumerParameters(exchangeParameters, rKey, deliveryHandler, Some(qName), durable, autoDelete)) + newConsumer(connection, ConsumerParameters(rKey, deliveryHandler, Some(qName), exchangeParameters)) } /** * Main supervisor */ - class AMQPSupervisorActor extends Actor { import self._ - faultHandler = Some(OneForOneStrategy(5, 5000)) + faultHandler = Some(OneForOneStrategy(None, None)) // never die trapExit = List(classOf[Throwable]) def receive = { @@ -194,7 +366,7 @@ object AMQP { private val supervisor = actorOf(new AMQPSupervisorActor).start - def shutdownAll = { + def shutdownAll() = { supervisor.shutdownLinkedActors } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala index 0ca9046093..277500e136 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala @@ -6,20 +6,16 @@ package se.scalablesolutions.akka.amqp import collection.JavaConversions -import se.scalablesolutions.akka.amqp.AMQP.ConsumerParameters import se.scalablesolutions.akka.util.Logging -import se.scalablesolutions.akka.AkkaException -import com.rabbitmq.client.AMQP.Queue.DeclareOk import com.rabbitmq.client.AMQP.BasicProperties import com.rabbitmq.client.{Channel, Envelope, DefaultConsumer} +import se.scalablesolutions.akka.amqp.AMQP.{NoActionDeclaration, ActiveDeclaration, PassiveDeclaration, ConsumerParameters} private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) - extends FaultTolerantChannelActor( - consumerParameters.exchangeParameters, consumerParameters.channelParameters) { - + extends FaultTolerantChannelActor( + consumerParameters.exchangeParameters, consumerParameters.channelParameters) { import consumerParameters._ - import exchangeParameters._ var listenerTag: Option[String] = None @@ -34,15 +30,21 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) protected def setupChannel(ch: Channel) = { - val queueDeclare: DeclareOk = { + val queueDeclare: com.rabbitmq.client.AMQP.Queue.DeclareOk = { 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)) + queueDeclaration match { + case PassiveDeclaration => + log.debug("Passively declaring new queue [%s] for %s", name, toString) + ch.queueDeclarePassive(name) + case ActiveDeclaration(durable, autoDelete, exclusive) => + log.debug("Actively declaring new queue [%s] for %s", name, toString) + val configurationArguments = exchangeParameters match { + case Some(params) => params.configurationArguments + case _ => Map.empty() + } + ch.queueDeclare(name, durable, exclusive, autoDelete, JavaConversions.asMap(configurationArguments)) + case NoActionDeclaration => new com.rabbitmq.client.impl.AMQImpl.Queue.DeclareOk(name, 0, 0) // do nothing here } case None => log.debug("Declaring new generated queue for %s", toString) @@ -50,8 +52,11 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) } } - log.debug("Binding new queue [%s] for %s", queueDeclare.getQueue, toString) - ch.queueBind(queueDeclare.getQueue, exchangeName, routingKey) + exchangeParameters.foreach { + params => + log.debug("Binding new queue [%s] for %s", queueDeclare.getQueue, toString) + ch.queueBind(queueDeclare.getQueue, params.exchangeName, routingKey) + } val tag = ch.basicConsume(queueDeclare.getQueue, false, new DefaultConsumer(ch) with Logging { override def handleDelivery(tag: String, envelope: Envelope, properties: BasicProperties, payload: Array[Byte]) { @@ -77,11 +82,12 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) private def acknowledgeDeliveryTag(deliveryTag: Long, remoteAcknowledgement: Boolean) = { log.debug("Acking message with delivery tag [%s]", deliveryTag) - channel.foreach{ch => - ch.basicAck(deliveryTag, false) - if (remoteAcknowledgement) { - deliveryHandler ! Acknowledged(deliveryTag) - } + channel.foreach { + ch => + ch.basicAck(deliveryTag, false) + if (remoteAcknowledgement) { + deliveryHandler ! Acknowledged(deliveryTag) + } } } @@ -90,10 +96,11 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) // 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) - } + channel.foreach { + ch => + if (remoteAcknowledgement) { + deliveryHandler ! Rejected(deliveryTag) + } } throw new RejectionException(deliveryTag) } @@ -115,10 +122,8 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) } override def toString = - "AMQP.Consumer[id= "+ self.id + - ", exchange=" + exchangeName + - ", exchangeType=" + exchangeType + - ", durable=" + exchangeDurable + - ", autoDelete=" + exchangeAutoDelete + "]" + "AMQP.Consumer[id= " + self.id + + ", exchangeParameters=" + exchangeParameters + + ", queueDeclaration=" + queueDeclaration + "]" } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala index ecb3029444..8468d75919 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala @@ -69,11 +69,11 @@ object ExampleSession { val exchangeParameters = ExchangeParameters("my_direct_exchange", ExchangeType.Direct) - val consumer = AMQP.newConsumer(connection, ConsumerParameters(exchangeParameters, "some.routing", actor { + val consumer = AMQP.newConsumer(connection, ConsumerParameters("some.routing", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) - })) + }, None, Some(exchangeParameters))) - val producer = AMQP.newProducer(connection, ProducerParameters(exchangeParameters)) + val producer = AMQP.newProducer(connection, ProducerParameters(Some(exchangeParameters))) producer ! Message("@jonas_boner: You sucked!!".getBytes, "some.routing") } @@ -84,15 +84,15 @@ object ExampleSession { val exchangeParameters = ExchangeParameters("my_fanout_exchange", ExchangeType.Fanout) - val bushConsumer = AMQP.newConsumer(connection, ConsumerParameters(exchangeParameters, "@george_bush", actor { + val bushConsumer = AMQP.newConsumer(connection, ConsumerParameters("@george_bush", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) - })) + }, None, Some(exchangeParameters))) - val obamaConsumer = AMQP.newConsumer(connection, ConsumerParameters(exchangeParameters, "@barack_obama", actor { + val obamaConsumer = AMQP.newConsumer(connection, ConsumerParameters("@barack_obama", actor { case Delivery(payload, _, _, _, _) => log.info("@barack_obama received message from: %s", new String(payload)) - })) + }, None, Some(exchangeParameters))) - val producer = AMQP.newProducer(connection, ProducerParameters(exchangeParameters)) + val producer = AMQP.newProducer(connection, ProducerParameters(Some(exchangeParameters))) producer ! Message("@jonas_boner: I'm going surfing".getBytes, "") } @@ -103,15 +103,15 @@ object ExampleSession { val exchangeParameters = ExchangeParameters("my_topic_exchange", ExchangeType.Topic) - val bushConsumer = AMQP.newConsumer(connection, ConsumerParameters(exchangeParameters, "@george_bush", actor { + val bushConsumer = AMQP.newConsumer(connection, ConsumerParameters("@george_bush", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) - })) + }, None, Some(exchangeParameters))) - val obamaConsumer = AMQP.newConsumer(connection, ConsumerParameters(exchangeParameters, "@barack_obama", actor { + val obamaConsumer = AMQP.newConsumer(connection, ConsumerParameters("@barack_obama", actor { case Delivery(payload, _, _, _, _) => log.info("@barack_obama received message from: %s", new String(payload)) - })) + }, None, Some(exchangeParameters))) - val producer = AMQP.newProducer(connection, ProducerParameters(exchangeParameters)) + val producer = AMQP.newProducer(connection, ProducerParameters(Some(exchangeParameters))) producer ! Message("@jonas_boner: You still suck!!".getBytes, "@george_bush") producer ! Message("@jonas_boner: Yes I can!".getBytes, "@barack_obama") } @@ -138,11 +138,11 @@ object ExampleSession { val exchangeParameters = ExchangeParameters("my_callback_exchange", ExchangeType.Direct) val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) - val consumer = AMQP.newConsumer(connection, ConsumerParameters(exchangeParameters, "callback.routing", actor { + val consumer = AMQP.newConsumer(connection, ConsumerParameters("callback.routing", actor { case _ => () // not used - }, channelParameters = Some(channelParameters))) + }, None, Some(exchangeParameters), channelParameters = Some(channelParameters))) - val producer = AMQP.newProducer(connection, ProducerParameters(exchangeParameters)) + val producer = AMQP.newProducer(connection, ProducerParameters(Some(exchangeParameters))) // Wait until both channels (producer & consumer) are started before stopping the connection channelCountdown.await(2, TimeUnit.SECONDS) @@ -155,10 +155,10 @@ object ExampleSession { val exchangeName = "easy.string" // listen by default to: - // exchange = exchangeName - // routingKey = .request + // exchange = optional exchangeName + // routingKey = provided routingKey or .request // queueName = .in - AMQP.newStringConsumer(connection, exchangeName, message => println("Received message: "+message)) + AMQP.newStringConsumer(connection, message => println("Received message: "+message), Some(exchangeName)) // send by default to: // exchange = exchangeName @@ -177,9 +177,9 @@ object ExampleSession { log.info("Received "+message) } - AMQP.newProtobufConsumer(connection, exchangeName, protobufMessageHandler) + AMQP.newProtobufConsumer(connection, protobufMessageHandler, Some(exchangeName)) - val producerClient = AMQP.newProtobufProducer[AddressProtocol](connection, exchangeName) + val producerClient = AMQP.newProtobufProducer[AddressProtocol](connection, Some(exchangeName)) producerClient.send(AddressProtocol.newBuilder.setHostname("akkarocks.com").setPort(1234).build) } @@ -187,7 +187,7 @@ object ExampleSession { val connection = AMQP.newConnection() - val exchangeParameters = ExchangeParameters("my_rpc_exchange", ExchangeType.Topic) + val exchangeName = "my_rpc_exchange" /** Server */ val serverFromBinary = new FromBinary[String] { @@ -200,7 +200,7 @@ object ExampleSession { def requestHandler(request: String) = 3 - val rpcServer = RPC.newRpcServer[String,Int](connection, exchangeParameters, "rpc.in.key", rpcServerSerializer, + val rpcServer = RPC.newRpcServer[String,Int](connection, exchangeName, "rpc.in.key", rpcServerSerializer, requestHandler, queueName = Some("rpc.in.key.queue")) @@ -213,7 +213,7 @@ object ExampleSession { } val rpcClientSerializer = new RpcClientSerializer[String, Int](clientToBinary, clientFromBinary) - val rpcClient = RPC.newRpcClient[String,Int](connection, exchangeParameters, "rpc.in.key", rpcClientSerializer) + val rpcClient = RPC.newRpcClient[String,Int](connection, exchangeName, "rpc.in.key", rpcClientSerializer) val response = (rpcClient !! "rpc_request") log.info("Response: " + response) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala index 4d642df554..6617c62a44 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala @@ -10,13 +10,10 @@ import se.scalablesolutions.akka.actor.Actor import Actor._ import com.rabbitmq.client.{ShutdownSignalException, Channel, ShutdownListener} import scala.PartialFunction -import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ChannelParameters} +import se.scalablesolutions.akka.amqp.AMQP._ abstract private[amqp] class FaultTolerantChannelActor( - exchangeParameters: ExchangeParameters, channelParameters: Option[ChannelParameters]) extends Actor { - - import exchangeParameters._ - + exchangeParameters: Option[ExchangeParameters], channelParameters: Option[ChannelParameters]) extends Actor { protected[amqp] var channel: Option[Channel] = None log.info("%s is started", toString) @@ -64,12 +61,16 @@ abstract private[amqp] class FaultTolerantChannelActor( protected def setupChannel(ch: Channel) private def setupChannelInternal(ch: Channel) = if (channel.isEmpty) { - if (exchangeName != "") { - if (exchangePassive) { - ch.exchangeDeclarePassive(exchangeName) - } else { - ch.exchangeDeclare(exchangeName, exchangeType.toString, exchangeDurable, exchangeAutoDelete, JavaConversions.asMap(configurationArguments)) - } + + exchangeParameters.foreach { + params => + import params._ + exchangeDeclaration match { + case PassiveDeclaration => ch.exchangeDeclarePassive(exchangeName) + case ActiveDeclaration(durable, autoDelete, _) => + ch.exchangeDeclare(exchangeName, exchangeType.toString, durable, autoDelete, JavaConversions.asMap(configurationArguments)) + case NoActionDeclaration => // ignore + } } ch.addShutdownListener(new ShutdownListener { def shutdownCompleted(cause: ShutdownSignalException) = { diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala index 0fd3f715b5..72a897dccf 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala @@ -19,7 +19,7 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio self.lifeCycle = Some(LifeCycle(Permanent)) self.trapExit = List(classOf[Throwable]) - self.faultHandler = Some(OneForOneStrategy(5, 5000)) + self.faultHandler = Some(OneForOneStrategy(None, None)) // never die val reconnectionTimer = new Timer("%s-timer".format(self.id)) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala index 3551ffa276..84c80d683b 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala @@ -7,14 +7,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) { import producerParameters._ - import exchangeParameters._ + + val exchangeName = exchangeParameters.flatMap(params => Some(params.exchangeName)) producerId.foreach(id => self.id = id) @@ -22,7 +22,7 @@ private[amqp] class ProducerActor(producerParameters: ProducerParameters) case message@Message(payload, routingKey, mandatory, immediate, properties) if channel.isDefined => { log.debug("Sending message [%s]", message) - channel.foreach(_.basicPublish(exchangeName, routingKey, mandatory, immediate, properties.getOrElse(null), payload)) + channel.foreach(_.basicPublish(exchangeName.getOrElse(null), routingKey, mandatory, immediate, properties.getOrElse(null), payload)) } case message@Message(payload, routingKey, mandatory, immediate, properties) => { log.warning("Unable to send message [%s]", message) @@ -55,9 +55,6 @@ private[amqp] class ProducerActor(producerParameters: ProducerParameters) override def toString = "AMQP.Poducer[id= "+ self.id + - ", exchange=" + exchangeName + - ", exchangeType=" + exchangeType + - ", durable=" + exchangeDurable + - ", autoDelete=" + exchangeAutoDelete + "]" + ", exchangeParameters=" + exchangeParameters + "]" } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala index 5c50fd4670..394315a68b 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala @@ -9,29 +9,30 @@ import se.scalablesolutions.akka.amqp._ object RPC { def newRpcClient[O, I](connection: ActorRef, - exchangeParameters: ExchangeParameters, + exchangeName: String, routingKey: String, serializer: RpcClientSerializer[O, I], channelParameters: Option[ChannelParameters] = None): ActorRef = { val rpcActor: ActorRef = actorOf(new RpcClientActor[O, I]( - exchangeParameters, routingKey, serializer, channelParameters)) + ExchangeParameters(exchangeName), routingKey, serializer, channelParameters)) connection.startLink(rpcActor) rpcActor ! Start rpcActor } def newRpcServer[I, O](connection: ActorRef, - exchangeParameters: ExchangeParameters, + exchangeName: String, 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 producer = newProducer(connection, ProducerParameters(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)) + val consumer = newConsumer(connection, ConsumerParameters(routingKey, rpcServer, + exchangeParameters = Some(ExchangeParameters(exchangeName)), channelParameters = channelParameters, + selfAcknowledging = false, queueName = queueName)) RpcServerHandle(producer, consumer) } @@ -66,7 +67,7 @@ object RPC { def newProtobufRpcServer[I <: Message, O <: Message]( connection: ActorRef, - exchange: String, + exchangeName: String, requestHandler: I => O, routingKey: Option[String] = None, queueName: Option[String] = None, @@ -82,12 +83,12 @@ object RPC { def toBinary(t: O) = t.toByteArray }) - startServer(connection, exchange, requestHandler, routingKey, queueName, durable, autoDelete, serializer) + startServer(connection, exchangeName, requestHandler, routingKey, queueName, durable, autoDelete, serializer) } def newProtobufRpcClient[O <: Message, I <: Message]( connection: ActorRef, - exchange: String, + exchangeName: String, routingKey: Option[String] = None, durable: Boolean = false, autoDelete: Boolean = true, @@ -103,11 +104,11 @@ object RPC { } }) - startClient(connection, exchange, routingKey, durable, autoDelete, passive, serializer) + startClient(connection, exchangeName, routingKey, durable, autoDelete, passive, serializer) } def newStringRpcServer(connection: ActorRef, - exchange: String, + exchangeName: String, requestHandler: String => String, routingKey: Option[String] = None, queueName: Option[String] = None, @@ -123,7 +124,7 @@ object RPC { def toBinary(t: String) = t.getBytes }) - startServer(connection, exchange, requestHandler, routingKey, queueName, durable, autoDelete, serializer) + startServer(connection, exchangeName, requestHandler, routingKey, queueName, durable, autoDelete, serializer) } def newStringRpcClient(connection: ActorRef, @@ -147,23 +148,21 @@ object RPC { } private def startClient[O, I](connection: ActorRef, - exchange: String, + exchangeName: 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 rKey = routingKey.getOrElse("%s.request".format(exchangeName)) - val client = newRpcClient(connection, exchangeParameters, rKey, serializer) + val client = newRpcClient(connection, exchangeName, rKey, serializer) new RpcClient(client) } private def startServer[I, O](connection: ActorRef, - exchange: String, + exchangeName: String, requestHandler: I => O, routingKey: Option[String] = None, queueName: Option[String] = None, @@ -171,12 +170,10 @@ object RPC { 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 rKey = routingKey.getOrElse("%s.request".format(exchangeName)) val qName = queueName.getOrElse("%s.in".format(rKey)) - newRpcServer(connection, exchangeParameters, rKey, serializer, requestHandler, queueName = Some(qName)) + newRpcServer(connection, exchangeName, rKey, serializer, requestHandler, Some(qName)) } } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcClientActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcClientActor.scala index 10596e393f..90fe3ac66a 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcClientActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RpcClientActor.scala @@ -13,7 +13,7 @@ class RpcClientActor[I,O]( routingKey: String, serializer: RpcClientSerializer[I,O], channelParameters: Option[ChannelParameters] = None) - extends FaultTolerantChannelActor(exchangeParameters, channelParameters) { + extends FaultTolerantChannelActor(Some(exchangeParameters), channelParameters) { import exchangeParameters._ diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 28360f155b..16aae50d09 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -40,19 +40,19 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- object Repositories { - lazy val AkkaRepo = MavenRepository("Akka Repository", "http://scalablesolutions.se/akka/repository") - lazy val CasbahRepo = MavenRepository("Casbah Repo", "http://repo.bumnetworks.com/releases") - lazy val CasbahSnapshotRepo = MavenRepository("Casbah Snapshots", "http://repo.bumnetworks.com/snapshots") - lazy val CodehausRepo = MavenRepository("Codehaus Repo", "http://repository.codehaus.org") +// lazy val AkkaRepo = MavenRepository("Akka Repository", "http://scalablesolutions.se/akka/repository") +// lazy val CasbahRepo = MavenRepository("Casbah Repo", "http://repo.bumnetworks.com/releases") +// lazy val CasbahSnapshotRepo = MavenRepository("Casbah Snapshots", "http://repo.bumnetworks.com/snapshots") +// lazy val CodehausRepo = MavenRepository("Codehaus Repo", "http://repository.codehaus.org") lazy val EmbeddedRepo = MavenRepository("Embedded Repo", (info.projectPath / "embedded-repo").asURL.toString) - lazy val FusesourceSnapshotRepo = MavenRepository("Fusesource Snapshots", "http://repo.fusesource.com/nexus/content/repositories/snapshots") - lazy val GuiceyFruitRepo = MavenRepository("GuiceyFruit Repo", "http://guiceyfruit.googlecode.com/svn/repo/releases/") - lazy val JBossRepo = MavenRepository("JBoss Repo", "https://repository.jboss.org/nexus/content/groups/public/") - lazy val JavaNetRepo = MavenRepository("java.net Repo", "http://download.java.net/maven/2") - lazy val SonatypeSnapshotRepo = MavenRepository("Sonatype OSS Repo", "http://oss.sonatype.org/content/repositories/releases") - lazy val SunJDMKRepo = MavenRepository("Sun JDMK Repo", "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo") - lazy val CasbahRepoReleases = MavenRepository("Casbah Release Repo", "http://repo.bumnetworks.com/releases") - lazy val ZookeeperRepo = MavenRepository("Zookeeper Repo", "http://lilycms.org/maven/maven2/deploy/") +// lazy val FusesourceSnapshotRepo = MavenRepository("Fusesource Snapshots", "http://repo.fusesource.com/nexus/content/repositories/snapshots") +// lazy val GuiceyFruitRepo = MavenRepository("GuiceyFruit Repo", "http://guiceyfruit.googlecode.com/svn/repo/releases/") +// lazy val JBossRepo = MavenRepository("JBoss Repo", "https://repository.jboss.org/nexus/content/groups/public/") +// lazy val JavaNetRepo = MavenRepository("java.net Repo", "http://download.java.net/maven/2") +// lazy val SonatypeSnapshotRepo = MavenRepository("Sonatype OSS Repo", "http://oss.sonatype.org/content/repositories/releases") +// lazy val SunJDMKRepo = MavenRepository("Sun JDMK Repo", "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo") +// lazy val CasbahRepoReleases = MavenRepository("Casbah Release Repo", "http://repo.bumnetworks.com/releases") +// lazy val ZookeeperRepo = MavenRepository("Zookeeper Repo", "http://lilycms.org/maven/maven2/deploy/") } // ------------------------------------------------------------------------------------------------------------------- @@ -63,27 +63,31 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- import Repositories._ - lazy val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", SonatypeSnapshotRepo) - lazy val jettyModuleConfig = ModuleConfiguration("org.eclipse.jetty", sbt.DefaultMavenRepository) - 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 multiverseModuleConfig = ModuleConfiguration("org.multiverse", CodehausRepo) - 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 atomikosModuleConfig = ModuleConfiguration("com.atomikos",sbt.DefaultMavenRepository) - lazy val casbahRelease = ModuleConfiguration("com.novus",CasbahRepoReleases) - lazy val zookeeperRelease = ModuleConfiguration("org.apache.hadoop.zookeeper",ZookeeperRepo) - lazy val casbahModuleConfig = ModuleConfiguration("com.novus", CasbahRepo) - lazy val timeModuleConfig = ModuleConfiguration("org.scala-tools", "time", CasbahSnapshotRepo) +// lazy val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", SonatypeSnapshotRepo) +// lazy val jettyModuleConfig = ModuleConfiguration("org.eclipse.jetty", sbt.DefaultMavenRepository) +// 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 multiverseModuleConfig = ModuleConfiguration("org.multiverse", CodehausRepo) +// 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 atomikosModuleConfig = ModuleConfiguration("com.atomikos",sbt.DefaultMavenRepository) +// lazy val casbahRelease = ModuleConfiguration("com.novus",CasbahRepoReleases) +// lazy val zookeeperRelease = ModuleConfiguration("org.apache.hadoop.zookeeper",ZookeeperRepo) +// lazy val casbahModuleConfig = ModuleConfiguration("com.novus", CasbahRepo) +// lazy val timeModuleConfig = ModuleConfiguration("org.scala-tools", "time", CasbahSnapshotRepo) lazy val embeddedRepo = EmbeddedRepo // This is the only exception, because the embedded repo is fast! + val mavenLocal = "Local Maven Repository" at "file:/e:/maven-repository" + + val efgfpNexusReleasesRepository = "Nexus Releases" at "http://nexus/nexus/content/groups/public" + val efgfpNexusSnaphotsRepository = "Nexus Snapshots" at "http://nexus/nexus/content/groups/public-snapshots" // ------------------------------------------------------------------------------------------------------------------- // Versions From 5df4303318240e014ef946ac6550af88b2a3c889 Mon Sep 17 00:00:00 2001 From: momania Date: Thu, 23 Sep 2010 17:30:49 +0200 Subject: [PATCH 02/32] Updated test to changes in api --- .../akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala | 6 +++--- .../amqp/test/AMQPConsumerConnectionRecoveryTest.scala | 6 +++--- .../amqp/test/AMQPConsumerManualAcknowledgeTest.scala | 9 +++++---- .../akka/amqp/test/AMQPConsumerManualRejectTest.scala | 9 +++++---- .../akka/amqp/test/AMQPConsumerMessageTest.scala | 8 ++++---- .../akka/amqp/test/AMQPProducerChannelRecoveryTest.scala | 2 +- .../amqp/test/AMQPProducerConnectionRecoveryTest.scala | 2 +- .../akka/amqp/test/AMQPProducerMessageTest.scala | 2 +- .../amqp/test/AMQPProtobufProducerConsumerTest.scala | 4 ++-- .../akka/amqp/test/AMQPRpcClientServerTest.scala | 6 +++--- .../akka/amqp/test/AMQPStringProducerConsumerTest.scala | 2 +- 11 files changed, 29 insertions(+), 27 deletions(-) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala index 31a90c8200..90db50556f 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala @@ -23,7 +23,7 @@ class AMQPConsumerChannelRecoveryTest extends JUnitSuite with MustMatchers { val connection = AMQP.newConnection(ConnectionParameters(initReconnectDelay = 50)) try { val producer = AMQP.newProducer(connection, ProducerParameters( - ExchangeParameters("text_exchange", ExchangeType.Direct))) + Some(ExchangeParameters("text_exchange", ExchangeType.Direct)))) val consumerStartedLatch = new StandardLatch val consumerRestartedLatch = new StandardLatch @@ -42,9 +42,9 @@ class AMQPConsumerChannelRecoveryTest extends JUnitSuite with MustMatchers { val payloadLatch = new StandardLatch val consumerExchangeParameters = ExchangeParameters("text_exchange", ExchangeType.Direct) val consumerChannelParameters = ChannelParameters(channelCallback = Some(consumerChannelCallback)) - val consumer = AMQP.newConsumer(connection, ConsumerParameters(consumerExchangeParameters, "non.interesting.routing.key", actor { + val consumer = AMQP.newConsumer(connection, ConsumerParameters("non.interesting.routing.key", actor { case Delivery(payload, _, _, _, _) => payloadLatch.open - }, channelParameters = Some(consumerChannelParameters))) + }, exchangeParameters = Some(consumerExchangeParameters), channelParameters = Some(consumerChannelParameters))) consumerStartedLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) val listenerLatch = new StandardLatch diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala index 50c078a13a..dd8bd41179 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala @@ -38,7 +38,7 @@ class AMQPConsumerConnectionRecoveryTest extends JUnitSuite with MustMatchers { val channelParameters = ChannelParameters(channelCallback = Some(producerChannelCallback)) val producer = AMQP.newProducer(connection, ProducerParameters( - ExchangeParameters("text_exchange", ExchangeType.Direct), channelParameters = Some(channelParameters))) + Some(ExchangeParameters("text_exchange")), channelParameters = Some(channelParameters))) producerStartedLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) @@ -60,9 +60,9 @@ class AMQPConsumerConnectionRecoveryTest extends JUnitSuite with MustMatchers { val payloadLatch = new StandardLatch val consumerExchangeParameters = ExchangeParameters("text_exchange", ExchangeType.Direct) val consumerChannelParameters = ChannelParameters(channelCallback = Some(consumerChannelCallback)) - val consumer = AMQP.newConsumer(connection, ConsumerParameters(consumerExchangeParameters, "non.interesting.routing.key", actor { + val consumer = AMQP.newConsumer(connection, ConsumerParameters("non.interesting.routing.key", actor { case Delivery(payload, _, _, _, _) => payloadLatch.open - }, channelParameters = Some(consumerChannelParameters))) + }, exchangeParameters = Some(consumerExchangeParameters), channelParameters = Some(consumerChannelParameters))) consumerStartedLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) val listenerLatch = new StandardLatch diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala index 011f287636..98de75425b 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala @@ -26,13 +26,13 @@ class AMQPConsumerManualAcknowledgeTest extends JUnitSuite with MustMatchers { case Restarting => () case Stopped => () } - val exchangeParameters = ExchangeParameters("text_exchange",ExchangeType.Direct) + val exchangeParameters = ExchangeParameters("text_exchange") val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) val failLatch = new StandardLatch val acknowledgeLatch = new StandardLatch var deliveryTagCheck: Long = -1 - val consumer:ActorRef = AMQP.newConsumer(connection, ConsumerParameters(exchangeParameters, "manual.ack.this", actor { + val consumer:ActorRef = AMQP.newConsumer(connection, ConsumerParameters("manual.ack.this", actor { case Delivery(payload, _, deliveryTag, _, sender) => { if (!failLatch.isOpen) { failLatch.open @@ -43,10 +43,11 @@ class AMQPConsumerManualAcknowledgeTest extends JUnitSuite with MustMatchers { } } case Acknowledged(deliveryTag) => if (deliveryTagCheck == deliveryTag) acknowledgeLatch.open - }, queueName = Some("self.ack.queue"), selfAcknowledging = false, queueAutoDelete = false, channelParameters = Some(channelParameters))) + }, queueName = Some("self.ack.queue"), exchangeParameters = Some(exchangeParameters), + selfAcknowledging = false, channelParameters = Some(channelParameters))) val producer = AMQP.newProducer(connection, - ProducerParameters(exchangeParameters, channelParameters = Some(channelParameters))) + ProducerParameters(Some(exchangeParameters), channelParameters = Some(channelParameters))) countDown.await(2, TimeUnit.SECONDS) must be (true) producer ! Message("some_payload".getBytes, "manual.ack.this") 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 index cdbfe4028c..14d4068a77 100644 --- 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 @@ -27,19 +27,20 @@ class AMQPConsumerManualRejectTest extends JUnitSuite with MustMatchers { case Restarting => restartingLatch.open case Stopped => () } - val exchangeParameters = ExchangeParameters("text_exchange",ExchangeType.Direct) + val exchangeParameters = ExchangeParameters("text_exchange") val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) val rejectedLatch = new StandardLatch - val consumer:ActorRef = AMQP.newConsumer(connection, ConsumerParameters(exchangeParameters, "manual.reject.this", actor { + val consumer:ActorRef = AMQP.newConsumer(connection, ConsumerParameters("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))) + }, queueName = Some("self.reject.queue"), exchangeParameters = Some(exchangeParameters), + selfAcknowledging = false, channelParameters = Some(channelParameters))) val producer = AMQP.newProducer(connection, - ProducerParameters(exchangeParameters, channelParameters = Some(channelParameters))) + ProducerParameters(Some(exchangeParameters), channelParameters = Some(channelParameters))) countDown.await(2, TimeUnit.SECONDS) must be (true) producer ! Message("some_payload".getBytes, "manual.reject.this") diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala index 88661de58d..bedf382bba 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala @@ -27,16 +27,16 @@ class AMQPConsumerMessageTest extends JUnitSuite with MustMatchers { case Stopped => () } - val exchangeParameters = ExchangeParameters("text_exchange",ExchangeType.Direct) + val exchangeParameters = ExchangeParameters("text_exchange") val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) val payloadLatch = new StandardLatch - val consumer = AMQP.newConsumer(connection, ConsumerParameters(exchangeParameters, "non.interesting.routing.key", actor { + val consumer = AMQP.newConsumer(connection, ConsumerParameters("non.interesting.routing.key", actor { case Delivery(payload, _, _, _, _) => payloadLatch.open - }, channelParameters = Some(channelParameters))) + }, exchangeParameters = Some(exchangeParameters), channelParameters = Some(channelParameters))) val producer = AMQP.newProducer(connection, - ProducerParameters(exchangeParameters, channelParameters = Some(channelParameters))) + ProducerParameters(Some(exchangeParameters), channelParameters = Some(channelParameters))) countDown.await(2, TimeUnit.SECONDS) must be (true) producer ! Message("some_payload".getBytes, "non.interesting.routing.key") diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTest.scala index e0ede02de3..ae0445e8ae 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTest.scala @@ -40,7 +40,7 @@ class AMQPProducerChannelRecoveryTest extends JUnitSuite with MustMatchers { val channelParameters = ChannelParameters(channelCallback = Some(producerCallback)) val producerParameters = ProducerParameters( - ExchangeParameters("text_exchange", ExchangeType.Direct), channelParameters = Some(channelParameters)) + Some(ExchangeParameters("text_exchange")), channelParameters = Some(channelParameters)) val producer = AMQP.newProducer(connection, producerParameters) startedLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTest.scala index ad756ff5f0..4df162190b 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTest.scala @@ -39,7 +39,7 @@ class AMQPProducerConnectionRecoveryTest extends JUnitSuite with MustMatchers { val channelParameters = ChannelParameters(channelCallback = Some(producerCallback)) val producerParameters = ProducerParameters( - ExchangeParameters("text_exchange", ExchangeType.Direct), channelParameters = Some(channelParameters)) + Some(ExchangeParameters("text_exchange")), channelParameters = Some(channelParameters)) val producer = AMQP.newProducer(connection, producerParameters) startedLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTest.scala index 7d485b1b8f..1bd3f81c4e 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTest.scala @@ -30,7 +30,7 @@ class AMQPProducerMessageTest extends JUnitSuite with MustMatchers { } } val producerParameters = ProducerParameters( - ExchangeParameters("text_exchange", ExchangeType.Direct), returnListener = Some(returnListener)) + Some(ExchangeParameters("text_exchange")), returnListener = Some(returnListener)) val producer = AMQP.newProducer(connection, producerParameters) 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 index fdc147210a..dfa3385986 100644 --- 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 @@ -29,9 +29,9 @@ class AMQPProtobufProducerConsumerTest extends JUnitSuite with MustMatchers { assert(response.getHostname == request.getHostname.reverse) responseLatch.open } - AMQP.newProtobufConsumer(connection, "", responseHandler, Some("proto.reply.key")) + AMQP.newProtobufConsumer(connection, responseHandler, None, Some("proto.reply.key")) - val producer = AMQP.newProtobufProducer[AddressProtocol](connection, "protoexchange") + val producer = AMQP.newProtobufProducer[AddressProtocol](connection, Some("protoexchange")) producer.send(request, Some("proto.reply.key")) responseLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) 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 index 1bb487f51c..8712c8186f 100644 --- 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 @@ -28,7 +28,7 @@ class AMQPRpcClientServerTest extends JUnitSuite with MustMatchers { case Stopped => () } - val exchangeParameters = ExchangeParameters("text_topic_exchange", ExchangeType.Topic) + val exchangeName = "text_topic_exchange" val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) @@ -41,7 +41,7 @@ class AMQPRpcClientServerTest extends JUnitSuite with MustMatchers { def requestHandler(request: String) = 3 - val rpcServer = RPC.newRpcServer[String, Int](connection, exchangeParameters, "rpc.routing", rpcServerSerializer, + val rpcServer = RPC.newRpcServer[String, Int](connection, exchangeName, "rpc.routing", rpcServerSerializer, requestHandler, channelParameters = Some(channelParameters)) val rpcClientSerializer = new RpcClientSerializer[String, Int]( @@ -51,7 +51,7 @@ class AMQPRpcClientServerTest extends JUnitSuite with MustMatchers { def fromBinary(bytes: Array[Byte]) = bytes.head.toInt }) - val rpcClient = RPC.newRpcClient[String, Int](connection, exchangeParameters, "rpc.routing", rpcClientSerializer, + val rpcClient = RPC.newRpcClient[String, Int](connection, exchangeName, "rpc.routing", rpcClientSerializer, channelParameters = Some(channelParameters)) countDown.await(2, TimeUnit.SECONDS) must be(true) 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 index f490b605ae..7f3df97f5e 100644 --- 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 @@ -29,7 +29,7 @@ class AMQPStringProducerConsumerTest extends JUnitSuite with MustMatchers { assert(response == request.reverse) responseLatch.open } - AMQP.newStringConsumer(connection, "", responseHandler, Some("string.reply.key")) + AMQP.newStringConsumer(connection, responseHandler, None, Some("string.reply.key")) val producer = AMQP.newStringProducer(connection, "stringexchange") producer.send(request, Some("string.reply.key")) From 3b6fef84067091f08c600e514295fdd8fc4dc0cb Mon Sep 17 00:00:00 2001 From: momania Date: Thu, 23 Sep 2010 18:14:22 +0200 Subject: [PATCH 03/32] Make tests pass again... --- .../se/scalablesolutions/akka/amqp/AMQP.scala | 30 +++++++++---------- .../akka/amqp/ConsumerActor.scala | 12 ++++---- .../akka/amqp/ProducerActor.scala | 2 +- .../AMQPConsumerChannelRecoveryTest.scala | 4 +-- .../AMQPConsumerConnectionRecoveryTest.scala | 2 +- .../AMQPConsumerManualAcknowledgeTest.scala | 5 ++-- .../akka/amqp/test/AMQPTest.scala | 2 +- 7 files changed, 28 insertions(+), 29 deletions(-) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala index be53297cd3..5b2fe6501f 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala @@ -89,20 +89,20 @@ object AMQP { case class ExchangeParameters( exchangeName: String, exchangeType: ExchangeType = ExchangeType.Topic, - exchangeDeclaration: Declaration = new ActiveDeclaration(), - configurationArguments: Map[String, AnyRef] = Map.empty()) { + exchangeDeclaration: Declaration = ActiveDeclaration(), + configurationArguments: Map[String, AnyRef] = Map.empty) { // Needed for Java API usage def this(exchangeName: String) = - this (exchangeName, ExchangeType.Topic, new ActiveDeclaration(), Map.empty()) + this (exchangeName, ExchangeType.Topic, ActiveDeclaration(), Map.empty) // Needed for Java API usage def this(exchangeName: String, exchangeType: ExchangeType) = - this (exchangeName, exchangeType, new ActiveDeclaration(), Map.empty()) + this (exchangeName, exchangeType, ActiveDeclaration(), Map.empty) // Needed for Java API usage def this(exchangeName: String, exchangeType: ExchangeType, exchangeDeclaration: Declaration) = - this (exchangeName, exchangeType, exchangeDeclaration, Map.empty()) + this (exchangeName, exchangeType, exchangeDeclaration, Map.empty) } /** @@ -144,7 +144,7 @@ object AMQP { deliveryHandler: ActorRef, queueName: Option[String] = None, exchangeParameters: Option[ExchangeParameters], - queueDeclaration: Declaration = new ActiveDeclaration(), + queueDeclaration: Declaration = ActiveDeclaration(), selfAcknowledging: Boolean = true, channelParameters: Option[ChannelParameters] = None) { @@ -154,29 +154,29 @@ object AMQP { throw new IllegalArgumentException("A queue name is required when requesting a durable queue.") case PassiveDeclaration => throw new IllegalArgumentException("A queue name is required when requesting passive declaration.") - case NoActionDeclaration => () // ignore + case _ => () // ignore } } // Needed for Java API usage def this(routingKey: String, deliveryHandler: ActorRef) = - this (routingKey, deliveryHandler, None, None, new ActiveDeclaration(), true, None) + this (routingKey, deliveryHandler, None, None, ActiveDeclaration(), true, None) // Needed for Java API usage def this(routingKey: String, deliveryHandler: ActorRef, channelParameters: ChannelParameters) = - this (routingKey, deliveryHandler, None, None, new ActiveDeclaration(), true, Some(channelParameters)) + this (routingKey, deliveryHandler, None, None, ActiveDeclaration(), true, Some(channelParameters)) // Needed for Java API usage def this(routingKey: String, deliveryHandler: ActorRef, selfAcknowledging: Boolean) = - this (routingKey, deliveryHandler, None, None, new ActiveDeclaration(), selfAcknowledging, None) + this (routingKey, deliveryHandler, None, None, ActiveDeclaration(), selfAcknowledging, None) // Needed for Java API usage def this(routingKey: String, deliveryHandler: ActorRef, selfAcknowledging: Boolean, channelParameters: ChannelParameters) = - this (routingKey, deliveryHandler, None, None, new ActiveDeclaration(), selfAcknowledging, Some(channelParameters)) + this (routingKey, deliveryHandler, None, None, ActiveDeclaration(), selfAcknowledging, Some(channelParameters)) // Needed for Java API usage def this(routingKey: String, deliveryHandler: ActorRef, queueName: String) = - this (routingKey, deliveryHandler, Some(queueName), None, new ActiveDeclaration(), true, None) + this (routingKey, deliveryHandler, Some(queueName), None, ActiveDeclaration(), true, None) // Needed for Java API usage def this(routingKey: String, deliveryHandler: ActorRef, queueName: String, queueDeclaration: Declaration, selfAcknowledging: Boolean, channelParameters: ChannelParameters) = @@ -184,15 +184,15 @@ object AMQP { // Needed for Java API usage def this(routingKey: String, deliveryHandler: ActorRef, exchangeParameters: ExchangeParameters) = - this (routingKey, deliveryHandler, None, Some(exchangeParameters), new ActiveDeclaration(), true, None) + this (routingKey, deliveryHandler, None, Some(exchangeParameters), ActiveDeclaration(), true, None) // Needed for Java API usage def this(routingKey: String, deliveryHandler: ActorRef, exchangeParameters: ExchangeParameters, selfAcknowledging: Boolean) = - this (routingKey, deliveryHandler, None, Some(exchangeParameters), new ActiveDeclaration(), selfAcknowledging, None) + this (routingKey, deliveryHandler, None, Some(exchangeParameters), ActiveDeclaration(), selfAcknowledging, None) // Needed for Java API usage def this(routingKey: String, deliveryHandler: ActorRef, queueName: String, exchangeParameters: ExchangeParameters) = - this (routingKey, deliveryHandler, Some(queueName), Some(exchangeParameters), new ActiveDeclaration(), true, None) + this (routingKey, deliveryHandler, Some(queueName), Some(exchangeParameters), ActiveDeclaration(), true, None) // Needed for Java API usage def this(routingKey: String, deliveryHandler: ActorRef, queueName: String, exchangeParameters: ExchangeParameters, queueDeclaration: Declaration) = diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala index 277500e136..e5ff20cbb4 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ConsumerActor.scala @@ -41,9 +41,9 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) log.debug("Actively declaring new queue [%s] for %s", name, toString) val configurationArguments = exchangeParameters match { case Some(params) => params.configurationArguments - case _ => Map.empty() + case _ => Map.empty } - ch.queueDeclare(name, durable, exclusive, autoDelete, JavaConversions.asMap(configurationArguments)) + ch.queueDeclare(name, durable, exclusive, autoDelete, JavaConversions.asMap(configurationArguments.toMap)) case NoActionDeclaration => new com.rabbitmq.client.impl.AMQImpl.Queue.DeclareOk(name, 0, 0) // do nothing here } case None => @@ -52,11 +52,9 @@ private[amqp] class ConsumerActor(consumerParameters: ConsumerParameters) } } - exchangeParameters.foreach { - params => - log.debug("Binding new queue [%s] for %s", queueDeclare.getQueue, toString) - ch.queueBind(queueDeclare.getQueue, params.exchangeName, routingKey) - } + val exchangeName = exchangeParameters.flatMap(params => Some(params.exchangeName)) + log.debug("Binding new queue [%s] for %s", queueDeclare.getQueue, toString) + ch.queueBind(queueDeclare.getQueue, exchangeName.getOrElse(""), routingKey) val tag = ch.basicConsume(queueDeclare.getQueue, false, new DefaultConsumer(ch) with Logging { override def handleDelivery(tag: String, envelope: Envelope, properties: BasicProperties, payload: Array[Byte]) { diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala index 84c80d683b..bb3448f8b5 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ProducerActor.scala @@ -22,7 +22,7 @@ private[amqp] class ProducerActor(producerParameters: ProducerParameters) case message@Message(payload, routingKey, mandatory, immediate, properties) if channel.isDefined => { log.debug("Sending message [%s]", message) - channel.foreach(_.basicPublish(exchangeName.getOrElse(null), routingKey, mandatory, immediate, properties.getOrElse(null), payload)) + channel.foreach(_.basicPublish(exchangeName.getOrElse(""), routingKey, mandatory, immediate, properties.getOrElse(null), payload)) } case message@Message(payload, routingKey, mandatory, immediate, properties) => { log.warning("Unable to send message [%s]", message) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala index 90db50556f..c5f4b7b5c6 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala @@ -23,7 +23,7 @@ class AMQPConsumerChannelRecoveryTest extends JUnitSuite with MustMatchers { val connection = AMQP.newConnection(ConnectionParameters(initReconnectDelay = 50)) try { val producer = AMQP.newProducer(connection, ProducerParameters( - Some(ExchangeParameters("text_exchange", ExchangeType.Direct)))) + Some(ExchangeParameters("text_exchange")))) val consumerStartedLatch = new StandardLatch val consumerRestartedLatch = new StandardLatch @@ -40,7 +40,7 @@ class AMQPConsumerChannelRecoveryTest extends JUnitSuite with MustMatchers { } val payloadLatch = new StandardLatch - val consumerExchangeParameters = ExchangeParameters("text_exchange", ExchangeType.Direct) + val consumerExchangeParameters = ExchangeParameters("text_exchange") val consumerChannelParameters = ChannelParameters(channelCallback = Some(consumerChannelCallback)) val consumer = AMQP.newConsumer(connection, ConsumerParameters("non.interesting.routing.key", actor { case Delivery(payload, _, _, _, _) => payloadLatch.open diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala index dd8bd41179..1b3fc70586 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala @@ -58,7 +58,7 @@ class AMQPConsumerConnectionRecoveryTest extends JUnitSuite with MustMatchers { val payloadLatch = new StandardLatch - val consumerExchangeParameters = ExchangeParameters("text_exchange", ExchangeType.Direct) + val consumerExchangeParameters = ExchangeParameters("text_exchange") val consumerChannelParameters = ChannelParameters(channelCallback = Some(consumerChannelCallback)) val consumer = AMQP.newConsumer(connection, ConsumerParameters("non.interesting.routing.key", actor { case Delivery(payload, _, _, _, _) => payloadLatch.open diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala index 98de75425b..81909d1cd9 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala @@ -10,9 +10,9 @@ 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 +import se.scalablesolutions.akka.amqp.AMQP._ class AMQPConsumerManualAcknowledgeTest extends JUnitSuite with MustMatchers { @@ -44,7 +44,8 @@ class AMQPConsumerManualAcknowledgeTest extends JUnitSuite with MustMatchers { } case Acknowledged(deliveryTag) => if (deliveryTagCheck == deliveryTag) acknowledgeLatch.open }, queueName = Some("self.ack.queue"), exchangeParameters = Some(exchangeParameters), - selfAcknowledging = false, channelParameters = Some(channelParameters))) + selfAcknowledging = false, channelParameters = Some(channelParameters), + queueDeclaration = ActiveDeclaration(autoDelete = false))) val producer = AMQP.newProducer(connection, ProducerParameters(Some(exchangeParameters), channelParameters = Some(channelParameters))) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala index 0c993d6815..850f4f02e8 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala @@ -7,7 +7,7 @@ package se.scalablesolutions.akka.amqp.test import se.scalablesolutions.akka.amqp.AMQP object AMQPTest { - def enabled = false + def enabled = true def withCleanEndState(action: => Unit) { try { From 974c22adb5529f47a65e2bcec571c3c30d61e0a9 Mon Sep 17 00:00:00 2001 From: momania Date: Thu, 23 Sep 2010 18:14:34 +0200 Subject: [PATCH 04/32] Disable test before push --- .../scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala index 850f4f02e8..0c993d6815 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala @@ -7,7 +7,7 @@ package se.scalablesolutions.akka.amqp.test import se.scalablesolutions.akka.amqp.AMQP object AMQPTest { - def enabled = true + def enabled = false def withCleanEndState(action: => Unit) { try { From 3dad2ceb218afce89032fe59acfd6b9896e577e3 Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 24 Sep 2010 07:41:41 +0200 Subject: [PATCH 05/32] back to original project settings :S --- project/build/AkkaProject.scala | 68 ++++++++++++++++----------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 16aae50d09..28360f155b 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -40,19 +40,19 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- object Repositories { -// lazy val AkkaRepo = MavenRepository("Akka Repository", "http://scalablesolutions.se/akka/repository") -// lazy val CasbahRepo = MavenRepository("Casbah Repo", "http://repo.bumnetworks.com/releases") -// lazy val CasbahSnapshotRepo = MavenRepository("Casbah Snapshots", "http://repo.bumnetworks.com/snapshots") -// lazy val CodehausRepo = MavenRepository("Codehaus Repo", "http://repository.codehaus.org") + lazy val AkkaRepo = MavenRepository("Akka Repository", "http://scalablesolutions.se/akka/repository") + lazy val CasbahRepo = MavenRepository("Casbah Repo", "http://repo.bumnetworks.com/releases") + lazy val CasbahSnapshotRepo = MavenRepository("Casbah Snapshots", "http://repo.bumnetworks.com/snapshots") + lazy val CodehausRepo = MavenRepository("Codehaus Repo", "http://repository.codehaus.org") lazy val EmbeddedRepo = MavenRepository("Embedded Repo", (info.projectPath / "embedded-repo").asURL.toString) -// lazy val FusesourceSnapshotRepo = MavenRepository("Fusesource Snapshots", "http://repo.fusesource.com/nexus/content/repositories/snapshots") -// lazy val GuiceyFruitRepo = MavenRepository("GuiceyFruit Repo", "http://guiceyfruit.googlecode.com/svn/repo/releases/") -// lazy val JBossRepo = MavenRepository("JBoss Repo", "https://repository.jboss.org/nexus/content/groups/public/") -// lazy val JavaNetRepo = MavenRepository("java.net Repo", "http://download.java.net/maven/2") -// lazy val SonatypeSnapshotRepo = MavenRepository("Sonatype OSS Repo", "http://oss.sonatype.org/content/repositories/releases") -// lazy val SunJDMKRepo = MavenRepository("Sun JDMK Repo", "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo") -// lazy val CasbahRepoReleases = MavenRepository("Casbah Release Repo", "http://repo.bumnetworks.com/releases") -// lazy val ZookeeperRepo = MavenRepository("Zookeeper Repo", "http://lilycms.org/maven/maven2/deploy/") + lazy val FusesourceSnapshotRepo = MavenRepository("Fusesource Snapshots", "http://repo.fusesource.com/nexus/content/repositories/snapshots") + lazy val GuiceyFruitRepo = MavenRepository("GuiceyFruit Repo", "http://guiceyfruit.googlecode.com/svn/repo/releases/") + lazy val JBossRepo = MavenRepository("JBoss Repo", "https://repository.jboss.org/nexus/content/groups/public/") + lazy val JavaNetRepo = MavenRepository("java.net Repo", "http://download.java.net/maven/2") + lazy val SonatypeSnapshotRepo = MavenRepository("Sonatype OSS Repo", "http://oss.sonatype.org/content/repositories/releases") + lazy val SunJDMKRepo = MavenRepository("Sun JDMK Repo", "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo") + lazy val CasbahRepoReleases = MavenRepository("Casbah Release Repo", "http://repo.bumnetworks.com/releases") + lazy val ZookeeperRepo = MavenRepository("Zookeeper Repo", "http://lilycms.org/maven/maven2/deploy/") } // ------------------------------------------------------------------------------------------------------------------- @@ -63,31 +63,27 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- import Repositories._ -// lazy val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", SonatypeSnapshotRepo) -// lazy val jettyModuleConfig = ModuleConfiguration("org.eclipse.jetty", sbt.DefaultMavenRepository) -// 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 multiverseModuleConfig = ModuleConfiguration("org.multiverse", CodehausRepo) -// 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 atomikosModuleConfig = ModuleConfiguration("com.atomikos",sbt.DefaultMavenRepository) -// lazy val casbahRelease = ModuleConfiguration("com.novus",CasbahRepoReleases) -// lazy val zookeeperRelease = ModuleConfiguration("org.apache.hadoop.zookeeper",ZookeeperRepo) -// lazy val casbahModuleConfig = ModuleConfiguration("com.novus", CasbahRepo) -// lazy val timeModuleConfig = ModuleConfiguration("org.scala-tools", "time", CasbahSnapshotRepo) + lazy val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", SonatypeSnapshotRepo) + lazy val jettyModuleConfig = ModuleConfiguration("org.eclipse.jetty", sbt.DefaultMavenRepository) + 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 multiverseModuleConfig = ModuleConfiguration("org.multiverse", CodehausRepo) + 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 atomikosModuleConfig = ModuleConfiguration("com.atomikos",sbt.DefaultMavenRepository) + lazy val casbahRelease = ModuleConfiguration("com.novus",CasbahRepoReleases) + lazy val zookeeperRelease = ModuleConfiguration("org.apache.hadoop.zookeeper",ZookeeperRepo) + lazy val casbahModuleConfig = ModuleConfiguration("com.novus", CasbahRepo) + lazy val timeModuleConfig = ModuleConfiguration("org.scala-tools", "time", CasbahSnapshotRepo) lazy val embeddedRepo = EmbeddedRepo // This is the only exception, because the embedded repo is fast! - val mavenLocal = "Local Maven Repository" at "file:/e:/maven-repository" - - val efgfpNexusReleasesRepository = "Nexus Releases" at "http://nexus/nexus/content/groups/public" - val efgfpNexusSnaphotsRepository = "Nexus Snapshots" at "http://nexus/nexus/content/groups/public-snapshots" // ------------------------------------------------------------------------------------------------------------------- // Versions From 058339240edd27616f60503366f5d25247c1a4f7 Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 24 Sep 2010 07:49:25 +0200 Subject: [PATCH 06/32] renamed tests to support integration test selection via sbt --- ...Test.scala => AMQPConnectionRecoveryTestIntegration.scala} | 4 ++-- ...scala => AMQPConsumerChannelRecoveryTestIntegration.scala} | 4 ++-- ...la => AMQPConsumerConnectionRecoveryTestIntegration.scala} | 4 ++-- ...ala => AMQPConsumerManualAcknowledgeTestIntegration.scala} | 4 ++-- ...st.scala => AMQPConsumerManualRejectTestIntegration.scala} | 4 ++-- ...ageTest.scala => AMQPConsumerMessageTestIntegration.scala} | 4 ++-- ...scala => AMQPProducerChannelRecoveryTestIntegration.scala} | 4 ++-- ...la => AMQPProducerConnectionRecoveryTestIntegration.scala} | 4 ++-- ...ageTest.scala => AMQPProducerMessageTestIntegration.scala} | 4 ++-- ...cala => AMQPProtobufProducerConsumerTestIntegration.scala} | 4 ++-- ...verTest.scala => AMQPRpcClientServerTestIntegration.scala} | 4 ++-- ...rotobufTest.scala => AMQPRpcProtobufTestIntegration.scala} | 4 ++-- ...RpcStringTest.scala => AMQPRpcStringTestIntegration.scala} | 4 ++-- ....scala => AMQPStringProducerConsumerTestIntegration.scala} | 4 ++-- .../scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala | 3 +-- 15 files changed, 29 insertions(+), 30 deletions(-) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPConnectionRecoveryTest.scala => AMQPConnectionRecoveryTestIntegration.scala} (91%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPConsumerChannelRecoveryTest.scala => AMQPConsumerChannelRecoveryTestIntegration.scala} (93%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPConsumerConnectionRecoveryTest.scala => AMQPConsumerConnectionRecoveryTestIntegration.scala} (94%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPConsumerManualAcknowledgeTest.scala => AMQPConsumerManualAcknowledgeTestIntegration.scala} (92%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPConsumerManualRejectTest.scala => AMQPConsumerManualRejectTestIntegration.scala} (92%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPConsumerMessageTest.scala => AMQPConsumerMessageTestIntegration.scala} (92%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPProducerChannelRecoveryTest.scala => AMQPProducerChannelRecoveryTestIntegration.scala} (91%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPProducerConnectionRecoveryTest.scala => AMQPProducerConnectionRecoveryTestIntegration.scala} (91%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPProducerMessageTest.scala => AMQPProducerMessageTestIntegration.scala} (90%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPProtobufProducerConsumerTest.scala => AMQPProtobufProducerConsumerTestIntegration.scala} (90%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPRpcClientServerTest.scala => AMQPRpcClientServerTestIntegration.scala} (92%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPRpcProtobufTest.scala => AMQPRpcProtobufTestIntegration.scala} (91%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPRpcStringTest.scala => AMQPRpcStringTestIntegration.scala} (89%) rename akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/{AMQPStringProducerConsumerTest.scala => AMQPStringProducerConsumerTestIntegration.scala} (88%) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTestIntegration.scala similarity index 91% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTestIntegration.scala index f9d30227f0..abd1e4c498 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConnectionRecoveryTestIntegration.scala @@ -14,10 +14,10 @@ import org.scalatest.matchers.MustMatchers import org.scalatest.junit.JUnitSuite import org.junit.Test -class AMQPConnectionRecoveryTest extends JUnitSuite with MustMatchers { +class AMQPConnectionRecoveryTestIntegration extends JUnitSuite with MustMatchers { @Test - def connectionAndRecovery = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def connectionAndRecovery = AMQPTest.withCleanEndState { val connectedLatch = new StandardLatch val reconnectingLatch = new StandardLatch diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTestIntegration.scala similarity index 93% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTestIntegration.scala index c5f4b7b5c6..03d315187a 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerChannelRecoveryTestIntegration.scala @@ -15,10 +15,10 @@ import se.scalablesolutions.akka.amqp.AMQP._ import org.scalatest.junit.JUnitSuite import se.scalablesolutions.akka.actor.Actor._ -class AMQPConsumerChannelRecoveryTest extends JUnitSuite with MustMatchers { +class AMQPConsumerChannelRecoveryTestIntegration extends JUnitSuite with MustMatchers { @Test - def consumerChannelRecovery = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def consumerChannelRecovery = AMQPTest.withCleanEndState { val connection = AMQP.newConnection(ConnectionParameters(initReconnectDelay = 50)) try { diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTestIntegration.scala similarity index 94% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTestIntegration.scala index 1b3fc70586..90889d8dc4 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerConnectionRecoveryTestIntegration.scala @@ -15,10 +15,10 @@ import org.scalatest.junit.JUnitSuite import se.scalablesolutions.akka.actor.{Actor, ActorRef} import Actor._ -class AMQPConsumerConnectionRecoveryTest extends JUnitSuite with MustMatchers { +class AMQPConsumerConnectionRecoveryTestIntegration extends JUnitSuite with MustMatchers { @Test - def consumerConnectionRecovery = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def consumerConnectionRecovery = AMQPTest.withCleanEndState { val connection = AMQP.newConnection(ConnectionParameters(initReconnectDelay = 50)) try { diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTestIntegration.scala similarity index 92% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTestIntegration.scala index 81909d1cd9..cfb5c920d4 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualAcknowledgeTestIntegration.scala @@ -14,10 +14,10 @@ import org.multiverse.api.latches.StandardLatch import org.scalatest.junit.JUnitSuite import se.scalablesolutions.akka.amqp.AMQP._ -class AMQPConsumerManualAcknowledgeTest extends JUnitSuite with MustMatchers { +class AMQPConsumerManualAcknowledgeTestIntegration extends JUnitSuite with MustMatchers { @Test - def consumerMessageManualAcknowledge = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def consumerMessageManualAcknowledge = AMQPTest.withCleanEndState { val connection = AMQP.newConnection() try { val countDown = new CountDownLatch(2) 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/AMQPConsumerManualRejectTestIntegration.scala similarity index 92% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualRejectTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualRejectTestIntegration.scala index 14d4068a77..f0b21d1286 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualRejectTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerManualRejectTestIntegration.scala @@ -14,10 +14,10 @@ import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ConsumerParamete import org.multiverse.api.latches.StandardLatch import org.scalatest.junit.JUnitSuite -class AMQPConsumerManualRejectTest extends JUnitSuite with MustMatchers { +class AMQPConsumerManualRejectTestIntegration extends JUnitSuite with MustMatchers { @Test - def consumerMessageManualAcknowledge = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def consumerMessageManualAcknowledge = AMQPTest.withCleanEndState { val connection = AMQP.newConnection() try { val countDown = new CountDownLatch(2) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTestIntegration.scala similarity index 92% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTestIntegration.scala index bedf382bba..0fed1951db 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTestIntegration.scala @@ -13,10 +13,10 @@ import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ConsumerParamete import org.scalatest.junit.JUnitSuite import org.junit.Test -class AMQPConsumerMessageTest extends JUnitSuite with MustMatchers { +class AMQPConsumerMessageTestIntegration extends JUnitSuite with MustMatchers { @Test - def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def consumerMessage = AMQPTest.withCleanEndState { val connection = AMQP.newConnection() try { diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTestIntegration.scala similarity index 91% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTestIntegration.scala index ae0445e8ae..81b9c29945 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerChannelRecoveryTestIntegration.scala @@ -14,10 +14,10 @@ import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ChannelParameter import org.scalatest.junit.JUnitSuite import org.junit.Test -class AMQPProducerChannelRecoveryTest extends JUnitSuite with MustMatchers { +class AMQPProducerChannelRecoveryTestIntegration extends JUnitSuite with MustMatchers { @Test - def producerChannelRecovery = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def producerChannelRecovery = AMQPTest.withCleanEndState { val connection = AMQP.newConnection(ConnectionParameters(initReconnectDelay = 50)) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTestIntegration.scala similarity index 91% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTestIntegration.scala index 4df162190b..9e74bbaf46 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerConnectionRecoveryTestIntegration.scala @@ -14,10 +14,10 @@ import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ChannelParameter import org.scalatest.junit.JUnitSuite import org.junit.Test -class AMQPProducerConnectionRecoveryTest extends JUnitSuite with MustMatchers { +class AMQPProducerConnectionRecoveryTestIntegration extends JUnitSuite with MustMatchers { @Test - def producerConnectionRecovery = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def producerConnectionRecovery = AMQPTest.withCleanEndState { val connection = AMQP.newConnection(ConnectionParameters(initReconnectDelay = 50)) try { diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTestIntegration.scala similarity index 90% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTestIntegration.scala index 1bd3f81c4e..dfc3ccd294 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProducerMessageTestIntegration.scala @@ -16,10 +16,10 @@ import se.scalablesolutions.akka.amqp.AMQP.{ExchangeParameters, ProducerParamete import org.scalatest.junit.JUnitSuite import org.junit.Test -class AMQPProducerMessageTest extends JUnitSuite with MustMatchers { +class AMQPProducerMessageTestIntegration extends JUnitSuite with MustMatchers { @Test - def producerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def producerMessage = AMQPTest.withCleanEndState { val connection: ActorRef = AMQP.newConnection() try { 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/AMQPProtobufProducerConsumerTestIntegration.scala similarity index 90% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProtobufProducerConsumerTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProtobufProducerConsumerTestIntegration.scala index dfa3385986..d6d719d241 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProtobufProducerConsumerTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProtobufProducerConsumerTestIntegration.scala @@ -12,10 +12,10 @@ 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 { +class AMQPProtobufProducerConsumerTestIntegration extends JUnitSuite with MustMatchers { @Test - def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def consumerMessage = AMQPTest.withCleanEndState { val connection = AMQP.newConnection() 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/AMQPRpcClientServerTestIntegration.scala similarity index 92% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTestIntegration.scala index 8712c8186f..d322b243c8 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTestIntegration.scala @@ -14,10 +14,10 @@ import se.scalablesolutions.akka.amqp.AMQP._ import org.scalatest.junit.JUnitSuite import org.junit.Test -class AMQPRpcClientServerTest extends JUnitSuite with MustMatchers { +class AMQPRpcClientServerTestIntegration extends JUnitSuite with MustMatchers { @Test - def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def consumerMessage = AMQPTest.withCleanEndState { val connection = AMQP.newConnection() 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/AMQPRpcProtobufTestIntegration.scala similarity index 91% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTestIntegration.scala index c86dd9bf81..1e4383e8d7 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcProtobufTestIntegration.scala @@ -12,10 +12,10 @@ import se.scalablesolutions.akka.amqp.rpc.RPC import org.multiverse.api.latches.StandardLatch import java.util.concurrent.TimeUnit -class AMQPRpcProtobufTest extends JUnitSuite with MustMatchers { +class AMQPRpcProtobufTestIntegration extends JUnitSuite with MustMatchers { @Test - def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def consumerMessage = AMQPTest.withCleanEndState { val connection = AMQP.newConnection() 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/AMQPRpcStringTestIntegration.scala similarity index 89% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcStringTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcStringTestIntegration.scala index 999100d0da..964cd94adb 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcStringTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcStringTestIntegration.scala @@ -11,10 +11,10 @@ import se.scalablesolutions.akka.amqp.rpc.RPC import org.multiverse.api.latches.StandardLatch import java.util.concurrent.TimeUnit -class AMQPRpcStringTest extends JUnitSuite with MustMatchers { +class AMQPRpcStringTestIntegration extends JUnitSuite with MustMatchers { @Test - def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def consumerMessage = AMQPTest.withCleanEndState { val connection = AMQP.newConnection() 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/AMQPStringProducerConsumerTestIntegration.scala similarity index 88% rename from akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTest.scala rename to akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTestIntegration.scala index 7f3df97f5e..41f698821c 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTestIntegration.scala @@ -11,10 +11,10 @@ import org.multiverse.api.latches.StandardLatch import java.util.concurrent.TimeUnit import se.scalablesolutions.akka.amqp.rpc.RPC -class AMQPStringProducerConsumerTest extends JUnitSuite with MustMatchers { +class AMQPStringProducerConsumerTestIntegration extends JUnitSuite with MustMatchers { @Test - def consumerMessage = if (AMQPTest.enabled) AMQPTest.withCleanEndState { + def consumerMessage = AMQPTest.withCleanEndState { val connection = AMQP.newConnection() diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala index 0c993d6815..ec1b6aa2f4 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala @@ -5,9 +5,8 @@ package se.scalablesolutions.akka.amqp.test import se.scalablesolutions.akka.amqp.AMQP -object AMQPTest { - def enabled = false +object AMQPTest { def withCleanEndState(action: => Unit) { try { From bd8e6771500404d8481509ffceb0d231d245b4de Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 24 Sep 2010 09:07:04 +0200 Subject: [PATCH 07/32] wait a bit longer than the deadline... so test always works... --- .../ExecutorBasedEventDrivenDispatcherActorSpec.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/test/scala/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala b/akka-actor/src/test/scala/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala index 95931df98b..4286c74d55 100644 --- a/akka-actor/src/test/scala/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala +++ b/akka-actor/src/test/scala/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala @@ -130,9 +130,9 @@ class ExecutorBasedEventDrivenDispatcherActorSpec extends JUnitSuite { slowOne ! "hogexecutor" slowOne ! "ping" fastOne ! "ping" - assert(ready.await(5,TimeUnit.SECONDS) === true) - Thread.sleep(deadlineMs) + assert(ready.await(2,TimeUnit.SECONDS) === true) + Thread.sleep(deadlineMs+10) // wait just a bit more than the deadline start.countDown - assert(latch.await(10,TimeUnit.SECONDS) === true) + assert(latch.await(2,TimeUnit.SECONDS) === true) } } From 489db0074f5d7c8f458ea317ce1ccedd77483142 Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 24 Sep 2010 09:29:05 +0200 Subject: [PATCH 08/32] add test filter to the amqp project --- project/build/AkkaProject.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 299207b868..3c4786ef15 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -440,6 +440,8 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { val junit = Dependencies.junit val multiverse = Dependencies.multiverse val scalatest = Dependencies.scalatest + + override def testOptions = createTestFilter( _.endsWith("Test") ) } // ------------------------------------------------------------------------------------------------------------------- From c6470b0e02cbb1e9f7df30f188e940e0654a982d Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 24 Sep 2010 12:13:29 +0200 Subject: [PATCH 09/32] initial take on java examples --- .../akka/amqp/ChannelCallbacks.java | 10 ++ .../akka/amqp/ConnectionCallbacks.java | 10 ++ .../akka/amqp/ExampleSessionJava.java | 142 ++++++++++++++++++ .../akka/amqp/ExchangeTypes.java | 11 ++ .../se/scalablesolutions/akka/amqp/AMQP.scala | 30 +--- .../akka/amqp/AMQPMessage.scala | 22 ++- .../akka/amqp/ExampleSession.scala | 10 +- .../akka/amqp/ExchangeType.scala | 24 ++- ...tringProducerConsumerTestIntegration.scala | 2 +- project/build/AkkaProject.scala | 8 +- 10 files changed, 221 insertions(+), 48 deletions(-) create mode 100644 akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ChannelCallbacks.java create mode 100644 akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ConnectionCallbacks.java create mode 100644 akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java create mode 100644 akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExchangeTypes.java diff --git a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ChannelCallbacks.java b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ChannelCallbacks.java new file mode 100644 index 0000000000..36f54fd825 --- /dev/null +++ b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ChannelCallbacks.java @@ -0,0 +1,10 @@ +package se.scalablesolutions.akka.amqp; + +public final class ChannelCallbacks { + + public static final AMQPMessage STARTED = Started$.MODULE$; + public static final AMQPMessage RESTARTING = Restarting$.MODULE$; + public static final AMQPMessage STOPPED = Stopped$.MODULE$; + + private ChannelCallbacks() {} +} diff --git a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ConnectionCallbacks.java b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ConnectionCallbacks.java new file mode 100644 index 0000000000..1314330928 --- /dev/null +++ b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ConnectionCallbacks.java @@ -0,0 +1,10 @@ +package se.scalablesolutions.akka.amqp; + +public final class ConnectionCallbacks { + + public static final AMQPMessage CONNECTED = Connected$.MODULE$; + public static final AMQPMessage RECONNECTING = Reconnecting$.MODULE$; + public static final AMQPMessage DISCONNECTED = Disconnected$.MODULE$; + + private ConnectionCallbacks() {} +} diff --git a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java new file mode 100644 index 0000000000..ad0d7c3841 --- /dev/null +++ b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java @@ -0,0 +1,142 @@ +package se.scalablesolutions.akka.amqp; + +import se.scalablesolutions.akka.actor.ActorRef; +import se.scalablesolutions.akka.actor.ActorRegistry; +import se.scalablesolutions.akka.actor.UntypedActor; +import se.scalablesolutions.akka.actor.UntypedActorFactory; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class ExampleSessionJava { + + public static void main(String... args) { + new ExampleSessionJava(); + } + + public ExampleSessionJava() { + printTopic("DIRECT"); + direct(); + + printTopic("CALLBACK"); + callback(); + + printTopic("Happy hAkking :-)"); + + // postStop everything the amqp tree except the main AMQP supervisor + // all connections/consumers/producers will be stopped + AMQP.shutdownAll(); + + ActorRegistry.shutdownAll(); + System.exit(0); + } + + private void printTopic(String topic) { + + System.out.println(""); + System.out.println("==== " + topic + " ==="); + System.out.println(""); + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException ignore) { + } + } + + private void direct() { + // defaults to amqp://guest:guest@localhost:5672/ + ActorRef connection = AMQP.newConnection(); + + AMQP.ExchangeParameters exchangeParameters = new AMQP.ExchangeParameters("my_direct_exchange", ExchangeTypes.DIRECT); + + ActorRef deliveryHandler = UntypedActor.actorOf(DirectDeliveryHandlerActor.class); + + AMQP.ConsumerParameters consumerParameters = new AMQP.ConsumerParameters("some.routing", deliveryHandler, exchangeParameters); + ActorRef consumer = AMQP.newConsumer(connection, consumerParameters); + + + ActorRef producer = AMQP.newProducer(connection, new AMQP.ProducerParameters(exchangeParameters)); + producer.sendOneWay(new Message("@jonas_boner: You sucked!!".getBytes(), "some.routing")); + } + + private void callback() { + + final CountDownLatch channelCountdown = new CountDownLatch(2); + + ActorRef connectionCallback = UntypedActor.actorOf(ConnectionCallbackActor.class); + connectionCallback.start(); + + AMQP.ConnectionParameters connectionParameters = new AMQP.ConnectionParameters(connectionCallback); + ActorRef connection = AMQP.newConnection(connectionParameters); + + ActorRef channelCallback = UntypedActor.actorOf(new UntypedActorFactory() { + public UntypedActor create() { + return new ChannelCallbackActor(channelCountdown); + } + }); + channelCallback.start(); + + AMQP.ExchangeParameters exchangeParameters = new AMQP.ExchangeParameters("my_callback_exchange", ExchangeTypes.DIRECT); + AMQP.ChannelParameters channelParameters = new AMQP.ChannelParameters(channelCallback); + + ActorRef dummyHandler = UntypedActor.actorOf(DummyActor.class); + AMQP.ConsumerParameters consumerParameters = new AMQP.ConsumerParameters("callback.routing", dummyHandler, exchangeParameters, channelParameters); + + ActorRef consumer = AMQP.newConsumer(connection, consumerParameters); + + ActorRef producer = AMQP.newProducer(connection, new AMQP.ProducerParameters(exchangeParameters)); + + // Wait until both channels (producer & consumer) are started before stopping the connection + try { + channelCountdown.await(2, TimeUnit.SECONDS); + } catch (InterruptedException ignore) { + } + connection.stop(); + } +} + +class DummyActor extends UntypedActor { + public void onReceive(Object message) throws Exception { + // not used + } +} + +class ChannelCallbackActor extends UntypedActor { + + private final CountDownLatch channelCountdown; + + public ChannelCallbackActor(CountDownLatch channelCountdown) { + this.channelCountdown = channelCountdown; + } + + public void onReceive(Object message) throws Exception { + if (ChannelCallbacks.STARTED.getClass().isAssignableFrom(message.getClass())) { + System.out.println("### >> Channel callback: Started"); + channelCountdown.countDown(); + } else if (ChannelCallbacks.RESTARTING.getClass().isAssignableFrom(message.getClass())) { + } else if (ChannelCallbacks.STOPPED.getClass().isAssignableFrom(message.getClass())) { + System.out.println("### >> Channel callback: Stopped"); + } else throw new IllegalArgumentException("Unknown message: " + message); + } +} + +class ConnectionCallbackActor extends UntypedActor { + + public void onReceive(Object message) throws Exception { + if (ConnectionCallbacks.CONNECTED.getClass().isAssignableFrom(message.getClass())) { + System.out.println("### >> Connection callback: Connected!"); + } else if (ConnectionCallbacks.RECONNECTING.getClass().isAssignableFrom(message.getClass())) { + } else if (ConnectionCallbacks.DISCONNECTED.getClass().isAssignableFrom(message.getClass())) { + System.out.println("### >> Connection callback: Disconnected!"); + } else throw new IllegalArgumentException("Unknown message: " + message); + } +} + +class DirectDeliveryHandlerActor extends UntypedActor { + + public void onReceive(Object message) throws Exception { + if (Delivery.class.isAssignableFrom(message.getClass())) { + Delivery delivery = (Delivery) message; + System.out.println("### >> @george_bush received message from: " + new String(delivery.payload())); + } else throw new IllegalArgumentException("Unknown message: " + message); + } +} diff --git a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExchangeTypes.java b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExchangeTypes.java new file mode 100644 index 0000000000..9a032554fe --- /dev/null +++ b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExchangeTypes.java @@ -0,0 +1,11 @@ +package se.scalablesolutions.akka.amqp; + +// Needed for Java API usage +public final class ExchangeTypes { + public static final ExchangeType DIRECT = Direct$.MODULE$; + public static final ExchangeType FANOUT = Fanout$.MODULE$; + public static final ExchangeType TOPIC = Topic$.MODULE$; + public static final ExchangeType MATCH = Match$.MODULE$; + + private ExchangeTypes() {} +} diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala index 5b2fe6501f..1856f63f57 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala @@ -88,13 +88,13 @@ object AMQP { */ case class ExchangeParameters( exchangeName: String, - exchangeType: ExchangeType = ExchangeType.Topic, + exchangeType: ExchangeType = Topic, exchangeDeclaration: Declaration = ActiveDeclaration(), configurationArguments: Map[String, AnyRef] = Map.empty) { // Needed for Java API usage def this(exchangeName: String) = - this (exchangeName, ExchangeType.Topic, ActiveDeclaration(), Map.empty) + this (exchangeName, Topic, ActiveDeclaration(), Map.empty) // Needed for Java API usage def this(exchangeName: String, exchangeType: ExchangeType) = @@ -186,6 +186,10 @@ object AMQP { def this(routingKey: String, deliveryHandler: ActorRef, exchangeParameters: ExchangeParameters) = this (routingKey, deliveryHandler, None, Some(exchangeParameters), ActiveDeclaration(), true, None) + // Needed for Java API usage + def this(routingKey: String, deliveryHandler: ActorRef, exchangeParameters: ExchangeParameters, channelParameters: ChannelParameters) = + this (routingKey, deliveryHandler, None, Some(exchangeParameters), ActiveDeclaration(), true, Some(channelParameters)) + // Needed for Java API usage def this(routingKey: String, deliveryHandler: ActorRef, exchangeParameters: ExchangeParameters, selfAcknowledging: Boolean) = this (routingKey, deliveryHandler, None, Some(exchangeParameters), ActiveDeclaration(), selfAcknowledging, None) @@ -269,26 +273,6 @@ object AMQP { new ProducerClient(producerRef, rKey, toBinary) } - // Needed for Java API usage - def newStringProducer(connection: ActorRef): ProducerClient[String] = { - newStringProducer(connection, None, None, None) - } - // Needed for Java API usage - def newStringProducer(connection: ActorRef, exchangeName: String): ProducerClient[String] = { - newStringProducer(connection, Some(exchangeName), None, None) - } - - // Needed for Java API usage - def newStringProducer(connection: ActorRef, exchangeName: String, routingKey: String): ProducerClient[String] = { - newStringProducer(connection, Some(exchangeName), Some(routingKey), None) - } - - // Needed for Java API usage - def newStringProducer(connection: ActorRef, exchangeName: String, routingKey: String, producerId: String): ProducerClient[String] = { - newStringProducer(connection, Some(exchangeName), Some(routingKey), Some(producerId)) - } - - def newStringConsumer(connection: ActorRef, handler: String => Unit, exchangeName: Option[String], @@ -310,6 +294,7 @@ object AMQP { newConsumer(connection, ConsumerParameters(rKey, deliveryHandler, Some(qName), exchangeParameters)) } + def newProtobufProducer[O <: com.google.protobuf.Message](connection: ActorRef, exchangeName: Option[String], routingKey: Option[String] = None, @@ -350,6 +335,7 @@ object AMQP { newConsumer(connection, ConsumerParameters(rKey, deliveryHandler, Some(qName), exchangeParameters)) } + /** * Main supervisor */ diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala index 2b53a73d08..a99b1afc67 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala @@ -18,7 +18,23 @@ case class Message( routingKey: String, mandatory: Boolean = false, immediate: Boolean = false, - properties: Option[BasicProperties] = None) extends AMQPMessage + properties: Option[BasicProperties] = None) extends AMQPMessage { + + // Needed for Java API usage + def this(payload: Array[Byte], routingKey: String) = this(payload, routingKey, false, false, None) + + // Needed for Java API usage + def this(payload: Array[Byte], routingKey: String, mandatory: Boolean, immediate: Boolean) = + this(payload, routingKey, mandatory, immediate, None) + + // Needed for Java API usage + def this(payload: Array[Byte], routingKey: String, properties: BasicProperties) = + this(payload, routingKey, false, false, Some(properties)) + + // Needed for Java API usage + def this(payload: Array[Byte], routingKey: String, mandatory: Boolean, immediate: Boolean, properties: BasicProperties) = + this(payload, routingKey, mandatory, immediate, Some(properties)) +} case class Delivery( payload: Array[Byte], @@ -52,8 +68,8 @@ class RejectionException(deliveryTag: Long) extends RuntimeException // internal messages private[akka] case class Failure(cause: Throwable) extends InternalAMQPMessage -private[akka] case class ConnectionShutdown(cause: ShutdownSignalException) extends InternalAMQPMessage -private[akka] case class ChannelShutdown(cause: ShutdownSignalException) extends InternalAMQPMessage +case class ConnectionShutdown(cause: ShutdownSignalException) extends InternalAMQPMessage +case class ChannelShutdown(cause: ShutdownSignalException) extends InternalAMQPMessage private[akka] class MessageNotDeliveredException( val message: String, diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala index 8468d75919..1fa39d1882 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala @@ -67,7 +67,7 @@ object ExampleSession { // defaults to amqp://guest:guest@localhost:5672/ val connection = AMQP.newConnection() - val exchangeParameters = ExchangeParameters("my_direct_exchange", ExchangeType.Direct) + val exchangeParameters = ExchangeParameters("my_direct_exchange", Direct) val consumer = AMQP.newConsumer(connection, ConsumerParameters("some.routing", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) @@ -82,7 +82,7 @@ object ExampleSession { // defaults to amqp://guest:guest@localhost:5672/ val connection = AMQP.newConnection() - val exchangeParameters = ExchangeParameters("my_fanout_exchange", ExchangeType.Fanout) + val exchangeParameters = ExchangeParameters("my_fanout_exchange", Fanout) val bushConsumer = AMQP.newConsumer(connection, ConsumerParameters("@george_bush", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) @@ -101,7 +101,7 @@ object ExampleSession { // defaults to amqp://guest:guest@localhost:5672/ val connection = AMQP.newConnection() - val exchangeParameters = ExchangeParameters("my_topic_exchange", ExchangeType.Topic) + val exchangeParameters = ExchangeParameters("my_topic_exchange", Topic) val bushConsumer = AMQP.newConsumer(connection, ConsumerParameters("@george_bush", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) @@ -135,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_callback_exchange", ExchangeType.Direct) + val exchangeParameters = ExchangeParameters("my_callback_exchange", Direct) val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) val consumer = AMQP.newConsumer(connection, ConsumerParameters("callback.routing", actor { @@ -163,7 +163,7 @@ object ExampleSession { // send by default to: // exchange = exchangeName // routingKey = .request - val producer = AMQP.newStringProducer(connection, exchangeName) + val producer = AMQP.newStringProducer(connection, Some(exchangeName)) producer.send("This shit is easy!") } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala index 1fa6e0543f..332a681ad4 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala @@ -5,17 +5,15 @@ package se.scalablesolutions.akka.amqp sealed trait ExchangeType -object ExchangeType { - case object Direct extends ExchangeType { - override def toString = "direct" - } - case object Topic extends ExchangeType { - override def toString = "topic" - } - case object Fanout extends ExchangeType { - override def toString = "fanout" - } - case object Match extends ExchangeType { - override def toString = "match" - } +case object Direct extends ExchangeType { + override def toString = "direct" +} +case object Topic extends ExchangeType { + override def toString = "topic" +} +case object Fanout extends ExchangeType { + override def toString = "fanout" +} +case object Match extends ExchangeType { + override def toString = "match" } diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTestIntegration.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTestIntegration.scala index 41f698821c..6fa7ccefd2 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTestIntegration.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTestIntegration.scala @@ -31,7 +31,7 @@ class AMQPStringProducerConsumerTestIntegration extends JUnitSuite with MustMatc } AMQP.newStringConsumer(connection, responseHandler, None, Some("string.reply.key")) - val producer = AMQP.newStringProducer(connection, "stringexchange") + val producer = AMQP.newStringProducer(connection, Some("stringexchange")) producer.send(request, Some("string.reply.key")) responseLatch.tryAwait(2, TimeUnit.SECONDS) must be (true) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 3c4786ef15..bc9d7f3a99 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -434,12 +434,12 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { class AkkaAMQPProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { val commons_io = Dependencies.commons_io val rabbit = Dependencies.rabbit - val protobuf = Dependencies.protobuf + val protobuf = Dependencies.protobuf // testing - val junit = Dependencies.junit - val multiverse = Dependencies.multiverse - val scalatest = Dependencies.scalatest + val junit = Dependencies.junit + val multiverse = Dependencies.multiverse + val scalatest = Dependencies.scalatest override def testOptions = createTestFilter( _.endsWith("Test") ) } From 353d01cf05a4533fe44c70189b35de8076ec3ffa Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Tue, 5 Oct 2010 11:13:27 +0200 Subject: [PATCH 10/32] CamelServiceManager.service returns Option[CamelService] (Scala API) CamelServiceManager.getService() returns Option[CamelService] (Java API) Re #457 --- akka-actor/src/main/scala/actor/Agent.scala | 2 +- .../scala/dataflow/DataFlowVariable.scala | 2 +- akka-actor/src/main/scala/util/JavaAPI.scala | 85 ++++++++++++++----- akka-camel/src/main/scala/CamelService.scala | 19 +++-- .../test/scala/CamelServiceManagerTest.scala | 17 ++-- .../src/test/scala/RemoteConsumerTest.scala | 6 +- .../main/scala/StandaloneApplication.scala | 4 +- .../scala/HttpConcurrencyTestStress.scala | 2 +- 8 files changed, 95 insertions(+), 42 deletions(-) diff --git a/akka-actor/src/main/scala/actor/Agent.scala b/akka-actor/src/main/scala/actor/Agent.scala index e5b00d4f5e..76e3e1d9db 100644 --- a/akka-actor/src/main/scala/actor/Agent.scala +++ b/akka-actor/src/main/scala/actor/Agent.scala @@ -6,7 +6,7 @@ package se.scalablesolutions.akka.actor import se.scalablesolutions.akka.stm.Ref import se.scalablesolutions.akka.AkkaException -import se.scalablesolutions.akka.util.{ Function => JFunc, Procedure => JProc } +import se.scalablesolutions.akka.util.JavaAPI.{ Function => JFunc, Procedure => JProc } import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.CountDownLatch diff --git a/akka-actor/src/main/scala/dataflow/DataFlowVariable.scala b/akka-actor/src/main/scala/dataflow/DataFlowVariable.scala index 329682de52..9c91b40833 100644 --- a/akka-actor/src/main/scala/dataflow/DataFlowVariable.scala +++ b/akka-actor/src/main/scala/dataflow/DataFlowVariable.scala @@ -11,7 +11,7 @@ import se.scalablesolutions.akka.actor.{Actor, ActorRef} import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.dispatch.CompletableFuture import se.scalablesolutions.akka.AkkaException -import se.scalablesolutions.akka.util.{ Function, SideEffect } +import se.scalablesolutions.akka.util.JavaAPI.{ Function, SideEffect } /** * Implements Oz-style dataflow (single assignment) variables. diff --git a/akka-actor/src/main/scala/util/JavaAPI.scala b/akka-actor/src/main/scala/util/JavaAPI.scala index 099082595d..0fa7abd738 100644 --- a/akka-actor/src/main/scala/util/JavaAPI.scala +++ b/akka-actor/src/main/scala/util/JavaAPI.scala @@ -1,23 +1,70 @@ package se.scalablesolutions.akka.util -/** A Function interface - * Used to create first-class-functions is Java (sort of) - * Java API - */ -trait Function[T,R] { - def apply(param: T): R +object JavaAPI { + /** A Function interface + * Used to create first-class-functions is Java (sort of) + * Java API + */ + trait Function[T,R] { + def apply(param: T): R + } + + /** A Procedure is like a Function, but it doesn't produce a return value + * Java API + */ + trait Procedure[T] { + def apply(param: T): Unit + } + + /** + * An executable piece of code that takes no parameters and doesn't return any value + */ + trait SideEffect { + def apply: Unit + } + + /** + * This class represents optional values. Instances of Option + * are either instances of case class Some or it is case + * object None. + *

+ * Java API + */ + sealed abstract class Option[A] extends java.lang.Iterable[A] { + def get: A + def isDefined: Boolean + } + + /** + * Class Some[A] represents existing values of type + * A. + *

+ * Java API + */ + final case class Some[A](v: A) extends Option[A] { + import scala.collection.JavaConversions._ + + val sv = scala.Some(v) + + def get = sv.get + def iterator = sv.iterator + def isDefined = true + } + + /** + * This case object represents non-existent values. + *

+ * Java API + */ + case class None[A]() extends Option[A] { + import scala.collection.JavaConversions._ + + def get = throw new NoSuchElementException("None.get") + def iterator = scala.None.iterator + def isDefined = false + } + + def some[A](v: A) = Some(v) + def none[A] = None[A] } -/** A Procedure is like a Function, but it doesn't produce a return value - * Java API - */ -trait Procedure[T] { - def apply(param: T): Unit -} - -/** - * An executable piece of code that takes no parameters and doesn't return any value - */ -trait SideEffect { - def apply: Unit -} diff --git a/akka-camel/src/main/scala/CamelService.scala b/akka-camel/src/main/scala/CamelService.scala index 22b536e383..3e4097ebcf 100644 --- a/akka-camel/src/main/scala/CamelService.scala +++ b/akka-camel/src/main/scala/CamelService.scala @@ -10,7 +10,8 @@ import org.apache.camel.CamelContext import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.actor.{AspectInitRegistry, ActorRegistry} import se.scalablesolutions.akka.config.Config._ -import se.scalablesolutions.akka.util.{Bootable, Logging} +import se.scalablesolutions.akka.util.{Logging, Bootable} +import se.scalablesolutions.akka.util.JavaAPI.{Option => JOption, Some => JSome, None => JNone} /** * Publishes (untyped) consumer actors and typed consumer actors via Camel endpoints. Actors @@ -73,7 +74,7 @@ trait CamelService extends Bootable with Logging { // Register this instance as current CamelService and return it CamelServiceManager.register(this) - CamelServiceManager.service + CamelServiceManager.service.get } /** @@ -136,16 +137,22 @@ object CamelServiceManager { * @see CamelService#stop * @see CamelService#onUnload */ - def stopCamelService = service.stop + def stopCamelService = for (s <- service) s.stop + + /** + * Returns Some(CamelService) if CamelService + * has been started, None otherwise. + */ + def service = _current /** * 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") + def getService: JOption[CamelService] = { + if (_current.isDefined) JSome(_current.get) else JNone[CamelService] + } private[camel] def register(service: CamelService) = if (_current.isDefined) throw new IllegalStateException("current CamelService already registered") diff --git a/akka-camel/src/test/scala/CamelServiceManagerTest.scala b/akka-camel/src/test/scala/CamelServiceManagerTest.scala index fd15ce7154..8f7916d574 100644 --- a/akka-camel/src/test/scala/CamelServiceManagerTest.scala +++ b/akka-camel/src/test/scala/CamelServiceManagerTest.scala @@ -10,19 +10,22 @@ import se.scalablesolutions.akka.actor.ActorRegistry */ class CamelServiceManagerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { - override def afterAll = ActorRegistry.shutdownAll + override def afterAll = { + CamelServiceManager.stopCamelService + ActorRegistry.shutdownAll + } "A CamelServiceManager" when { "the startCamelService method been has been called" must { "have registered the started CamelService instance" in { val service = CamelServiceManager.startCamelService - CamelServiceManager.service must be theSameInstanceAs (service) + CamelServiceManager.service.get 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 } + CamelServiceManager.service must be (None) } } } @@ -32,13 +35,13 @@ class CamelServiceManagerTest extends WordSpec with BeforeAndAfterAll with MustM "a CamelService instance has been started externally" must { "have registered the started CamelService instance" in { service.start - CamelServiceManager.service must be theSameInstanceAs (service) + CamelServiceManager.service.get 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 } + CamelServiceManager.service must be (None) } } } @@ -54,10 +57,6 @@ class CamelServiceManagerTest extends WordSpec with BeforeAndAfterAll with MustM "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 } - } } } } diff --git a/akka-camel/src/test/scala/RemoteConsumerTest.scala b/akka-camel/src/test/scala/RemoteConsumerTest.scala index afba2011d5..8d80d05b36 100644 --- a/akka-camel/src/test/scala/RemoteConsumerTest.scala +++ b/akka-camel/src/test/scala/RemoteConsumerTest.scala @@ -45,7 +45,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh val consumer = actorOf[RemoteConsumer].start when("remote consumer publication is triggered") - var latch = service.expectEndpointActivationCount(1) + var latch = service.get.expectEndpointActivationCount(1) consumer !! "init" assert(latch.await(5000, TimeUnit.MILLISECONDS)) @@ -61,7 +61,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh val consumer = TypedActor.newRemoteInstance(classOf[SampleRemoteTypedConsumer], classOf[SampleRemoteTypedConsumerImpl], host, port) when("remote typed consumer publication is triggered") - var latch = service.expectEndpointActivationCount(1) + var latch = service.get.expectEndpointActivationCount(1) consumer.foo("init") assert(latch.await(5000, TimeUnit.MILLISECONDS)) @@ -77,7 +77,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh val consumer = UntypedActor.actorOf(classOf[SampleRemoteUntypedConsumer]).start when("remote untyped consumer publication is triggered") - var latch = service.expectEndpointActivationCount(1) + var latch = service.get.expectEndpointActivationCount(1) consumer.sendRequestReply(Message("init", Map("test" -> "init"))) assert(latch.await(5000, TimeUnit.MILLISECONDS)) 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 c86295da57..6a8aa461a8 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala @@ -29,7 +29,7 @@ object StandaloneApplication extends Application { assert("hello msg1" == context.createProducerTemplate.requestBody("direct:test", "msg1")) // set expectations on upcoming endpoint activation - val activation = service.expectEndpointActivationCount(1) + val activation = service.get.expectEndpointActivationCount(1) // 'internally' register typed actor (requires CamelService) TypedActor.newInstance(classOf[TypedConsumer2], classOf[TypedConsumer2Impl]) @@ -86,7 +86,7 @@ object StandaloneJmsApplication extends Application { startCamelService // Expect two consumer endpoints to be activated - val completion = service.expectEndpointActivationCount(2) + val completion = service.get.expectEndpointActivationCount(2) val jmsUri = "jms:topic:test" // Wire publisher and consumer using a JMS topic diff --git a/akka-samples/akka-sample-camel/src/test/scala/HttpConcurrencyTestStress.scala b/akka-samples/akka-sample-camel/src/test/scala/HttpConcurrencyTestStress.scala index 3813463601..76cbc58a8b 100644 --- a/akka-samples/akka-sample-camel/src/test/scala/HttpConcurrencyTestStress.scala +++ b/akka-samples/akka-sample-camel/src/test/scala/HttpConcurrencyTestStress.scala @@ -50,7 +50,7 @@ object HttpConcurrencyTestStress { val workers = for (i <- 1 to 8) yield actorOf[HttpServerWorker].start val balancer = loadBalancerActor(new CyclicIterator(workers.toList)) - val completion = service.expectEndpointActivationCount(1) + val completion = service.get.expectEndpointActivationCount(1) val server = actorOf(new HttpServerActor(balancer)).start completion.await } From 77d5f39955a6f7406f1e1b5ab86253a4b7a9227e Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Tue, 5 Oct 2010 15:49:11 +0200 Subject: [PATCH 11/32] Java API for CamelServiceManager and CamelContextManager (usage of JavaAPI.Option) --- akka-actor/src/main/scala/util/JavaAPI.scala | 15 ++- .../main/scala/CamelContextLifecycle.scala | 105 ++++++++++++------ akka-camel/src/main/scala/CamelService.scala | 28 +++-- .../src/main/scala/ConsumerPublisher.scala | 8 +- akka-camel/src/main/scala/Message.scala | 8 +- akka-camel/src/main/scala/Producer.scala | 2 +- .../SampleUntypedForwardingProducer.java | 2 +- .../scala/CamelContextLifecycleTest.scala | 30 +++-- akka-camel/src/test/scala/ConsumerTest.scala | 38 +++---- .../src/test/scala/ProducerFeatureTest.scala | 4 +- .../src/test/scala/RemoteConsumerTest.scala | 6 +- .../scala/UntypedProducerFeatureTest.scala | 4 +- .../component/ActorComponentFeatureTest.scala | 26 ++--- .../TypedActorComponentFeatureTest.scala | 20 ++-- .../src/main/scala/Boot.scala | 2 +- .../main/scala/StandaloneApplication.scala | 12 +- .../scala/CamelServiceSpringFeatureTest.scala | 4 +- 17 files changed, 188 insertions(+), 126 deletions(-) diff --git a/akka-actor/src/main/scala/util/JavaAPI.scala b/akka-actor/src/main/scala/util/JavaAPI.scala index 0fa7abd738..248eaf7fbe 100644 --- a/akka-actor/src/main/scala/util/JavaAPI.scala +++ b/akka-actor/src/main/scala/util/JavaAPI.scala @@ -33,6 +33,7 @@ object JavaAPI { sealed abstract class Option[A] extends java.lang.Iterable[A] { def get: A def isDefined: Boolean + def asScala: scala.Option[A] } /** @@ -44,11 +45,10 @@ object JavaAPI { final case class Some[A](v: A) extends Option[A] { import scala.collection.JavaConversions._ - val sv = scala.Some(v) - - def get = sv.get - def iterator = sv.iterator + def get = v + def iterator = Iterator.single(v) def isDefined = true + def asScala = scala.Some(v) } /** @@ -60,11 +60,16 @@ object JavaAPI { import scala.collection.JavaConversions._ def get = throw new NoSuchElementException("None.get") - def iterator = scala.None.iterator + def iterator = Iterator.empty def isDefined = false + def asScala = scala.None } def some[A](v: A) = Some(v) def none[A] = None[A] + + implicit def java2ScalaOption[A](o: Option[A]): scala.Option[A] = o.asScala + implicit def scala2JavaOption[A](o: scala.Option[A]): Option[A] = + if (o.isDefined) Some(o.get) else None[A] } diff --git a/akka-camel/src/main/scala/CamelContextLifecycle.scala b/akka-camel/src/main/scala/CamelContextLifecycle.scala index 32bb386dee..8348ba4590 100644 --- a/akka-camel/src/main/scala/CamelContextLifecycle.scala +++ b/akka-camel/src/main/scala/CamelContextLifecycle.scala @@ -11,6 +11,7 @@ import org.apache.camel.impl.DefaultCamelContext import se.scalablesolutions.akka.camel.component.TypedActorComponent import se.scalablesolutions.akka.util.Logging +import se.scalablesolutions.akka.util.JavaAPI.{Option => JOption} /** * Manages the lifecycle of a CamelContext. Allowed transitions are @@ -18,12 +19,12 @@ import se.scalablesolutions.akka.util.Logging * * @author Martin Krasser */ -trait CamelContextLifecycle extends Logging { +trait CamelContextLifecycle extends CamelContextLifecycleJavaAPI with Logging { // TODO: enforce correct state transitions // valid: init -> start -> stop -> init ... - private var _context: CamelContext = _ - private var _template: ProducerTemplate = _ + private var _context: Option[CamelContext] = None + private var _template: Option[ProducerTemplate] = None private var _initialized = false private var _started = false @@ -40,25 +41,25 @@ trait CamelContextLifecycle extends Logging { private[camel] var typedActorRegistry: Map[String, AnyRef] = _ /** - * Returns the managed CamelContext. + * Returns Some(CamelContext) if CamelContextLifecycle + * has been initialized, otherwise None. */ - protected def context: CamelContext = _context + protected def context: Option[CamelContext] = _context /** - * Returns the managed ProducerTemplate. + * Returns Some(ProducerTemplate) if CamelContextLifecycle + * has been initialized, otherwise None. */ - protected def template: ProducerTemplate = _template + protected def template: Option[ProducerTemplate] = _template - /** - * Sets the managed CamelContext. - */ - protected def context_= (context: CamelContext) { _context = context } - - /** - * Sets the managed ProducerTemplate. - */ - protected def template_= (template: ProducerTemplate) { _template = template } + def mandatoryContext = + if (context.isDefined) context.get + else throw new IllegalStateException("no current CamelContext") + def mandatoryTemplate = + if (template.isDefined) template.get + else throw new IllegalStateException("no current ProducerTemplate") + def initialized = _initialized def started = _started @@ -66,21 +67,30 @@ trait CamelContextLifecycle extends Logging { * Starts the CamelContext and an associated ProducerTemplate. */ def start = { - context.start - template.start - _started = true - log.info("Camel context started") + for { + c <- context + t <- template + } { + c.start + t.start + _started = true + log.info("Camel context started") + } } /** * Stops the CamelContext and the associated ProducerTemplate. */ def stop = { - template.stop - context.stop - _initialized = false - _started = false - log.info("Camel context stopped") + for { + t <- template + c <- context + } { + t.stop + c.stop + _started = false + log.info("Camel context stopped") + } } /** @@ -98,19 +108,50 @@ trait CamelContextLifecycle extends Logging { def init(context: CamelContext) { this.typedActorComponent = new TypedActorComponent this.typedActorRegistry = typedActorComponent.typedActorRegistry - this.context = context - this.context.setStreamCaching(true) - this.context.addComponent(TypedActorComponent.InternalSchema, typedActorComponent) - this.template = context.createProducerTemplate + + context.setStreamCaching(true) + context.addComponent(TypedActorComponent.InternalSchema, typedActorComponent) + + this._context = Some(context) + this._template = Some(context.createProducerTemplate) + _initialized = true log.info("Camel context initialized") } } +/** + * Java API for CamelContextLifecycle. + * + * @author Martin Krasser + */ +trait CamelContextLifecycleJavaAPI { this: CamelContextLifecycle => + /** + * Returns Some(CamelContext) if CamelContextLifecycle + * has been initialized, otherwise None. + */ + def getContext: JOption[CamelContext] = context + + /** + * Returns Some(ProducerTemplate) if CamelContextLifecycle + * has been initialized, otherwise None. + */ + def getTemplate: JOption[ProducerTemplate] = template +} + /** * Manages a global CamelContext and an associated ProducerTemplate. */ -object CamelContextManager extends CamelContextLifecycle { - override def context: CamelContext = super.context - override def template: ProducerTemplate = super.template +object CamelContextManager extends CamelContextLifecycle with CamelContextLifecycleJavaAPI { + /** + * Returns Some(CamelContext) if CamelContextLifecycle + * has been initialized, otherwise None. + */ + override def context: Option[CamelContext] = super.context + + /** + * Returns Some(ProducerTemplate) if CamelContextLifecycle + * has been initialized, otherwise None. + */ + override def template: Option[ProducerTemplate] = super.template } diff --git a/akka-camel/src/main/scala/CamelService.scala b/akka-camel/src/main/scala/CamelService.scala index 3e4097ebcf..3d6705da00 100644 --- a/akka-camel/src/main/scala/CamelService.scala +++ b/akka-camel/src/main/scala/CamelService.scala @@ -11,7 +11,7 @@ import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.actor.{AspectInitRegistry, ActorRegistry} import se.scalablesolutions.akka.config.Config._ import se.scalablesolutions.akka.util.{Logging, Bootable} -import se.scalablesolutions.akka.util.JavaAPI.{Option => JOption, Some => JSome, None => JNone} +import se.scalablesolutions.akka.util.JavaAPI.{Option => JOption} /** * Publishes (untyped) consumer actors and typed consumer actors via Camel endpoints. Actors @@ -116,7 +116,7 @@ trait CamelService extends Bootable with Logging { * * @author Martin Krasser */ -object CamelServiceManager { +object CamelServiceManager extends CamelServiceManagerJavaAPI { /** * The current (optional) CamelService. Is defined when a CamelService has been started. @@ -145,14 +145,7 @@ object CamelServiceManager { */ def service = _current - /** - * Returns the current CamelService. - * - * @throws IllegalStateException if there's no current CamelService. - */ - def getService: JOption[CamelService] = { - if (_current.isDefined) JSome(_current.get) else JNone[CamelService] - } + // TODO: add mandatoryService (throwing exception if service is not defined) private[camel] def register(service: CamelService) = if (_current.isDefined) throw new IllegalStateException("current CamelService already registered") @@ -163,6 +156,21 @@ object CamelServiceManager { else throw new IllegalStateException("only current CamelService can be unregistered") } +/** + * Java API for CamelServiceManager. + * + * @author Martin Krasser + */ +trait CamelServiceManagerJavaAPI { + /** + * Returns Some(CamelService) if CamelService + * has been started, None otherwise. + *

+ * Java API + */ + def getService: JOption[CamelService] = CamelServiceManager.service +} + /** * @author Martin Krasser */ diff --git a/akka-camel/src/main/scala/ConsumerPublisher.scala b/akka-camel/src/main/scala/ConsumerPublisher.scala index dfc1510ea6..f1bb5d7ab3 100644 --- a/akka-camel/src/main/scala/ConsumerPublisher.scala +++ b/akka-camel/src/main/scala/ConsumerPublisher.scala @@ -23,7 +23,7 @@ private[camel] object ConsumerPublisher extends Logging { * Creates a route to the registered consumer actor. */ def handleConsumerRegistered(event: ConsumerRegistered) { - CamelContextManager.context.addRoutes(new ConsumerActorRoute(event.uri, event.uuid, event.blocking)) + CamelContextManager.mandatoryContext.addRoutes(new ConsumerActorRoute(event.uri, event.uuid, event.blocking)) log.info("published actor %s at endpoint %s" format (event.actorRef, event.uri)) } @@ -31,7 +31,7 @@ private[camel] object ConsumerPublisher extends Logging { * Stops the route to the already un-registered consumer actor. */ def handleConsumerUnregistered(event: ConsumerUnregistered) { - CamelContextManager.context.stopRoute(event.uuid.toString) + CamelContextManager.mandatoryContext.stopRoute(event.uuid.toString) log.info("unpublished actor %s from endpoint %s" format (event.actorRef, event.uri)) } @@ -43,7 +43,7 @@ private[camel] object ConsumerPublisher extends Logging { val objectId = "%s_%s" format (event.init.actorRef.uuid, targetMethod) CamelContextManager.typedActorRegistry.put(objectId, event.typedActor) - CamelContextManager.context.addRoutes(new ConsumerMethodRoute(event.uri, objectId, targetMethod)) + CamelContextManager.mandatoryContext.addRoutes(new ConsumerMethodRoute(event.uri, objectId, targetMethod)) log.info("published method %s of %s at endpoint %s" format (targetMethod, event.typedActor, event.uri)) } @@ -55,7 +55,7 @@ private[camel] object ConsumerPublisher extends Logging { val objectId = "%s_%s" format (event.init.actorRef.uuid, targetMethod) CamelContextManager.typedActorRegistry.remove(objectId) - CamelContextManager.context.stopRoute(objectId) + CamelContextManager.mandatoryContext.stopRoute(objectId) log.info("unpublished method %s of %s from endpoint %s" format (targetMethod, event.typedActor, event.uri)) } } diff --git a/akka-camel/src/main/scala/Message.scala b/akka-camel/src/main/scala/Message.scala index d5cfd23faf..7c503009e8 100644 --- a/akka-camel/src/main/scala/Message.scala +++ b/akka-camel/src/main/scala/Message.scala @@ -24,7 +24,7 @@ case class Message(val body: Any, val headers: Map[String, Any] = Map.empty) { * @see CamelContextManager. */ def bodyAs[T](clazz: Class[T]): T = - CamelContextManager.context.getTypeConverter.mandatoryConvertTo[T](clazz, body) + CamelContextManager.mandatoryContext.getTypeConverter.mandatoryConvertTo[T](clazz, body) /** * Returns the body of the message converted to the type T. Conversion is done @@ -35,7 +35,7 @@ case class Message(val body: Any, val headers: Map[String, Any] = Map.empty) { * @see CamelContextManager. */ def bodyAs[T](implicit m: Manifest[T]): T = - CamelContextManager.context.getTypeConverter.mandatoryConvertTo[T](m.erasure.asInstanceOf[Class[T]], body) + CamelContextManager.mandatoryContext.getTypeConverter.mandatoryConvertTo[T](m.erasure.asInstanceOf[Class[T]], body) /** * Returns those headers from this message whose name is contained in names. @@ -53,14 +53,14 @@ case class Message(val body: Any, val headers: Map[String, Any] = Map.empty) { * 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)) + CamelContextManager.mandatoryContext.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)) + CamelContextManager.mandatoryContext.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 3313c1d0be..2924590c9e 100644 --- a/akka-camel/src/main/scala/Producer.scala +++ b/akka-camel/src/main/scala/Producer.scala @@ -27,7 +27,7 @@ trait ProducerSupport { this: Actor => * Endpoint object resolved from the current CamelContext with * endpointUri. */ - private lazy val endpoint = CamelContextManager.context.getEndpoint(endpointUri) + private lazy val endpoint = CamelContextManager.mandatoryContext.getEndpoint(endpointUri) /** * SendProcessor for producing messages to endpoint. 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 index e909947de8..ec2f7eb560 100644 --- a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedForwardingProducer.java +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedForwardingProducer.java @@ -13,6 +13,6 @@ public class SampleUntypedForwardingProducer extends UntypedProducerActor { public void onReceiveAfterProduce(Object message) { Message msg = (Message)message; String body = msg.bodyAs(String.class); - CamelContextManager.template().sendBody("direct:forward-test-1", body); + CamelContextManager.mandatoryTemplate().sendBody("direct:forward-test-1", body); } } diff --git a/akka-camel/src/test/scala/CamelContextLifecycleTest.scala b/akka-camel/src/test/scala/CamelContextLifecycleTest.scala index cf558ec8d9..6e6889c295 100644 --- a/akka-camel/src/test/scala/CamelContextLifecycleTest.scala +++ b/akka-camel/src/test/scala/CamelContextLifecycleTest.scala @@ -6,22 +6,30 @@ import org.scalatest.junit.JUnitSuite class CamelContextLifecycleTest extends JUnitSuite with CamelContextLifecycle { @Test def shouldManageCustomCamelContext { - assert(context === null) - assert(template === null) + assert(context === None) + assert(template === None) + + intercept[IllegalStateException] { mandatoryContext } + intercept[IllegalStateException] { mandatoryTemplate } + val ctx = new TestCamelContext assert(ctx.isStreamCaching === false) + init(ctx) - assert(context.isStreamCaching === true) - assert(!context.asInstanceOf[TestCamelContext].isStarted) - // In Camel 2.3 CamelComtext.createProducerTemplate starts - // the template before returning it (wasn't started in 2.2) - assert(template.asInstanceOf[DefaultProducerTemplate].isStarted) + + assert(mandatoryContext.isStreamCaching === true) + assert(!mandatoryContext.asInstanceOf[TestCamelContext].isStarted) + assert(mandatoryTemplate.asInstanceOf[DefaultProducerTemplate].isStarted) + start - assert(context.asInstanceOf[TestCamelContext].isStarted) - assert(template.asInstanceOf[DefaultProducerTemplate].isStarted) + + assert(mandatoryContext.asInstanceOf[TestCamelContext].isStarted) + assert(mandatoryTemplate.asInstanceOf[DefaultProducerTemplate].isStarted) + stop - assert(!context.asInstanceOf[TestCamelContext].isStarted) - assert(!template.asInstanceOf[DefaultProducerTemplate].isStarted) + + assert(!mandatoryContext.asInstanceOf[TestCamelContext].isStarted) + assert(!mandatoryTemplate.asInstanceOf[DefaultProducerTemplate].isStarted) } class TestCamelContext extends DefaultCamelContext diff --git a/akka-camel/src/test/scala/ConsumerTest.scala b/akka-camel/src/test/scala/ConsumerTest.scala index 2a2cc0b11f..0af8aec7d5 100644 --- a/akka-camel/src/test/scala/ConsumerTest.scala +++ b/akka-camel/src/test/scala/ConsumerTest.scala @@ -14,7 +14,7 @@ import se.scalablesolutions.akka.actor._ * @author Martin Krasser */ class ConsumerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { - import CamelContextManager.template + import CamelContextManager.mandatoryTemplate import ConsumerTest._ var service: CamelService = _ @@ -45,12 +45,12 @@ class ConsumerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { 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") + mandatoryTemplate.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) + CamelContextManager.mandatoryContext.hasEndpoint("direct:publish-test-2") must be (null) } } "started" must { @@ -58,10 +58,10 @@ class ConsumerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { 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") + mandatoryTemplate.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) + CamelContextManager.mandatoryContext.hasEndpoint("direct:publish-test-2") must not be (null) } } "stopped" must { @@ -70,7 +70,7 @@ class ConsumerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { consumer.stop latch.await(5000, TimeUnit.MILLISECONDS) must be (true) intercept[CamelExecutionException] { - template.requestBody("direct:publish-test-2", "msg2") + mandatoryTemplate.requestBody("direct:publish-test-2", "msg2") } } } @@ -83,9 +83,9 @@ class ConsumerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { 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") + mandatoryTemplate.requestBodyAndHeader("direct:m2", "x", "test", "y") must equal ("m2: x y") + mandatoryTemplate.requestBodyAndHeader("direct:m3", "x", "test", "y") must equal ("m3: x y") + mandatoryTemplate.requestBodyAndHeader("direct:m4", "x", "test", "y") must equal ("m4: x y") } } "stopped" must { @@ -94,13 +94,13 @@ class ConsumerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { TypedActor.stop(actor) latch.await(5000, TimeUnit.MILLISECONDS) must be (true) intercept[CamelExecutionException] { - template.requestBodyAndHeader("direct:m2", "x", "test", "y") + mandatoryTemplate.requestBodyAndHeader("direct:m2", "x", "test", "y") } intercept[CamelExecutionException] { - template.requestBodyAndHeader("direct:m3", "x", "test", "y") + mandatoryTemplate.requestBodyAndHeader("direct:m3", "x", "test", "y") } intercept[CamelExecutionException] { - template.requestBodyAndHeader("direct:m4", "x", "test", "y") + mandatoryTemplate.requestBodyAndHeader("direct:m4", "x", "test", "y") } } } @@ -113,8 +113,8 @@ class ConsumerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { 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") + mandatoryTemplate.requestBody("direct:publish-test-3", "x") must equal ("foo: x") + mandatoryTemplate.requestBody("direct:publish-test-4", "x") must equal ("bar: x") } } "stopped" must { @@ -123,10 +123,10 @@ class ConsumerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { TypedActor.stop(actor) latch.await(5000, TimeUnit.MILLISECONDS) must be (true) intercept[CamelExecutionException] { - template.requestBody("direct:publish-test-3", "x") + mandatoryTemplate.requestBody("direct:publish-test-3", "x") } intercept[CamelExecutionException] { - template.requestBody("direct:publish-test-4", "x") + mandatoryTemplate.requestBody("direct:publish-test-4", "x") } } } @@ -139,7 +139,7 @@ class ConsumerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { 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") + mandatoryTemplate.requestBodyAndHeader("direct:test-untyped-consumer", "x", "test", "y") must equal ("x y") } } "stopped" must { @@ -148,7 +148,7 @@ class ConsumerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { consumer.stop latch.await(5000, TimeUnit.MILLISECONDS) must be (true) intercept[CamelExecutionException] { - template.sendBodyAndHeader("direct:test-untyped-consumer", "blah", "test", "blub") + mandatoryTemplate.sendBodyAndHeader("direct:test-untyped-consumer", "blah", "test", "blub") } } } @@ -162,7 +162,7 @@ class ConsumerTest extends WordSpec with BeforeAndAfterAll with MustMatchers { latch.await(5000, TimeUnit.MILLISECONDS) must be (true) try { - template.requestBody("direct:publish-test-5", "msg3") + mandatoryTemplate.requestBody("direct:publish-test-5", "msg3") fail("expected TimoutException not thrown") } catch { case e => { diff --git a/akka-camel/src/test/scala/ProducerFeatureTest.scala b/akka-camel/src/test/scala/ProducerFeatureTest.scala index a27e05a54f..5f31bcbe1c 100644 --- a/akka-camel/src/test/scala/ProducerFeatureTest.scala +++ b/akka-camel/src/test/scala/ProducerFeatureTest.scala @@ -14,7 +14,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before override protected def beforeAll = { ActorRegistry.shutdownAll CamelContextManager.init - CamelContextManager.context.addRoutes(new TestRoute) + CamelContextManager.mandatoryContext.addRoutes(new TestRoute) CamelContextManager.start } @@ -239,7 +239,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before } } - private def mockEndpoint = CamelContextManager.context.getEndpoint("mock:mock", classOf[MockEndpoint]) + private def mockEndpoint = CamelContextManager.mandatoryContext.getEndpoint("mock:mock", classOf[MockEndpoint]) } object ProducerFeatureTest { diff --git a/akka-camel/src/test/scala/RemoteConsumerTest.scala b/akka-camel/src/test/scala/RemoteConsumerTest.scala index 8d80d05b36..393f7a9a53 100644 --- a/akka-camel/src/test/scala/RemoteConsumerTest.scala +++ b/akka-camel/src/test/scala/RemoteConsumerTest.scala @@ -50,7 +50,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh assert(latch.await(5000, TimeUnit.MILLISECONDS)) then("the published consumer is accessible via its endpoint URI") - val response = CamelContextManager.template.requestBody("direct:remote-consumer", "test") + val response = CamelContextManager.mandatoryTemplate.requestBody("direct:remote-consumer", "test") assert(response === "remote actor: test") } } @@ -66,7 +66,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh assert(latch.await(5000, TimeUnit.MILLISECONDS)) then("the published method is accessible via its endpoint URI") - val response = CamelContextManager.template.requestBody("direct:remote-typed-consumer", "test") + val response = CamelContextManager.mandatoryTemplate.requestBody("direct:remote-typed-consumer", "test") assert(response === "remote typed actor: test") } } @@ -82,7 +82,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh 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") + val response = CamelContextManager.mandatoryTemplate.requestBodyAndHeader("direct:remote-untyped-consumer", "a", "test", "b") assert(response === "a b") } } diff --git a/akka-camel/src/test/scala/UntypedProducerFeatureTest.scala b/akka-camel/src/test/scala/UntypedProducerFeatureTest.scala index c8a0bd8542..0d268785b6 100644 --- a/akka-camel/src/test/scala/UntypedProducerFeatureTest.scala +++ b/akka-camel/src/test/scala/UntypedProducerFeatureTest.scala @@ -14,7 +14,7 @@ class UntypedProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with override protected def beforeAll = { ActorRegistry.shutdownAll CamelContextManager.init - CamelContextManager.context.addRoutes(new TestRoute) + CamelContextManager.mandatoryContext.addRoutes(new TestRoute) CamelContextManager.start } @@ -78,7 +78,7 @@ class UntypedProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with } - private def mockEndpoint = CamelContextManager.context.getEndpoint("mock:mock", classOf[MockEndpoint]) + private def mockEndpoint = CamelContextManager.mandatoryContext.getEndpoint("mock:mock", classOf[MockEndpoint]) } object UntypedProducerFeatureTest { diff --git a/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala b/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala index 331f2c23b6..cc9f750aae 100644 --- a/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala +++ b/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala @@ -18,7 +18,7 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with override protected def beforeAll = { ActorRegistry.shutdownAll CamelContextManager.init - CamelContextManager.context.addRoutes(new TestRoute) + CamelContextManager.mandatoryContext.addRoutes(new TestRoute) CamelContextManager.start } @@ -30,12 +30,12 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with } feature("Communicate with an actor via an actor:uuid endpoint") { - import CamelContextManager.template + import CamelContextManager.mandatoryTemplate 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") + mandatoryTemplate.sendBody("actor:uuid:%s" format actor.uuid, "Martin") assert(latch.await(5000, TimeUnit.MILLISECONDS)) val reply = (actor !! GetRetainedMessage).get.asInstanceOf[Message] assert(reply.body === "Martin") @@ -43,36 +43,36 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with scenario("two-way communication") { val actor = actorOf[Tester2].start - assert(template.requestBody("actor:uuid:%s" format actor.uuid, "Martin") === "Hello Martin") + assert(mandatoryTemplate.requestBody("actor:uuid:%s" format actor.uuid, "Martin") === "Hello Martin") } scenario("two-way communication with timeout") { val actor = actorOf[Tester3].start intercept[RuntimeCamelException] { - template.requestBody("actor:uuid:%s?blocking=true" format actor.uuid, "Martin") + mandatoryTemplate.requestBody("actor:uuid:%s?blocking=true" format actor.uuid, "Martin") } } scenario("two-way communication via a custom route with failure response") { mockEndpoint.expectedBodiesReceived("whatever") - template.requestBody("direct:failure-test-1", "whatever") + mandatoryTemplate.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") + mandatoryTemplate.requestBody("direct:failure-test-2", "whatever") mockEndpoint.assertIsSatisfied } } feature("Communicate with an actor via an actor:id endpoint") { - import CamelContextManager.template + import CamelContextManager.mandatoryTemplate 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") + mandatoryTemplate.sendBody("actor:%s" format actor.id, "Martin") assert(latch.await(5000, TimeUnit.MILLISECONDS)) val reply = (actor !! GetRetainedMessage).get.asInstanceOf[Message] assert(reply.body === "Martin") @@ -80,17 +80,17 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with scenario("two-way communication") { val actor = actorOf[Tester2].start - assert(template.requestBody("actor:%s" format actor.id, "Martin") === "Hello Martin") + assert(mandatoryTemplate.requestBody("actor:%s" format actor.id, "Martin") === "Hello Martin") } scenario("two-way communication via a custom route") { val actor = actorOf[CustomIdActor].start - assert(template.requestBody("direct:custom-id-test-1", "Martin") === "Received Martin") - assert(template.requestBody("direct:custom-id-test-2", "Martin") === "Received Martin") + assert(mandatoryTemplate.requestBody("direct:custom-id-test-1", "Martin") === "Received Martin") + assert(mandatoryTemplate.requestBody("direct:custom-id-test-2", "Martin") === "Received Martin") } } - private def mockEndpoint = CamelContextManager.context.getEndpoint("mock:mock", classOf[MockEndpoint]) + private def mockEndpoint = CamelContextManager.mandatoryContext.getEndpoint("mock:mock", classOf[MockEndpoint]) } object ActorComponentFeatureTest { diff --git a/akka-camel/src/test/scala/component/TypedActorComponentFeatureTest.scala b/akka-camel/src/test/scala/component/TypedActorComponentFeatureTest.scala index 37352eb154..e1f169187a 100644 --- a/akka-camel/src/test/scala/component/TypedActorComponentFeatureTest.scala +++ b/akka-camel/src/test/scala/component/TypedActorComponentFeatureTest.scala @@ -13,7 +13,7 @@ import se.scalablesolutions.akka.camel._ */ class TypedActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach { import TypedActorComponentFeatureTest._ - import CamelContextManager.template + import CamelContextManager.mandatoryTemplate override protected def beforeAll = { val typedActor = TypedActor.newInstance(classOf[SampleTypedActor], classOf[SampleTypedActorImpl]) // not a consumer @@ -24,7 +24,7 @@ class TypedActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll registry.put("ta", typedActor) CamelContextManager.init(new DefaultCamelContext(registry)) - CamelContextManager.context.addRoutes(new CustomRouteBuilder) + CamelContextManager.mandatoryContext.addRoutes(new CustomRouteBuilder) CamelContextManager.start // Internal registration @@ -41,19 +41,19 @@ class TypedActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll 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") + val result1 = mandatoryTemplate.requestBodyAndHeader("%s:tc?method=m2" format InternalSchema, "x", "test", "y") + val result2 = mandatoryTemplate.requestBodyAndHeader("%s:tc?method=m4" format InternalSchema, "x", "test", "y") assert(result1 === "m2: x y") assert(result2 === "m4: x y") } scenario("two-way communication with method returning void") { - val result = template.requestBodyAndHeader("%s:tc?method=m5" format InternalSchema, "x", "test", "y") + val result = mandatoryTemplate.requestBodyAndHeader("%s:tc?method=m5" format InternalSchema, "x", "test", "y") assert(result === "x") // returns initial body } scenario("one-way communication with method returning String") { - val result = template.send("%s:tc?method=m2" format InternalSchema, InOnly, new Processor { + val result = mandatoryTemplate.send("%s:tc?method=m2" format InternalSchema, InOnly, new Processor { def process(exchange: Exchange) = { exchange.getIn.setBody("x") exchange.getIn.setHeader("test", "y") @@ -65,7 +65,7 @@ class TypedActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll } scenario("one-way communication with method returning void") { - val result = template.send("%s:tc?method=m5" format InternalSchema, InOnly, new Processor { + val result = mandatoryTemplate.send("%s:tc?method=m5" format InternalSchema, InOnly, new Processor { def process(exchange: Exchange) = { exchange.getIn.setBody("x") exchange.getIn.setHeader("test", "y") @@ -81,19 +81,19 @@ class TypedActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll 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") + mandatoryTemplate.requestBodyAndHeader("typed-actor:tc?method=m2", "x", "test", "y") } } } feature("Communicate with an externally-registered typed actor using typed-actor endpoint URIs") { scenario("two-way communication with method returning String") { - val result = template.requestBody("typed-actor:ta?method=foo", "test") + val result = mandatoryTemplate.requestBody("typed-actor:ta?method=foo", "test") assert(result === "foo: test") } scenario("two-way communication with method returning String via custom route") { - val result = template.requestBody("direct:test", "test") + val result = mandatoryTemplate.requestBody("direct:test", "test") assert(result === "foo: test") } } 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 98c7c34b7e..6ee82cd454 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala @@ -40,7 +40,7 @@ class Boot { // Use a custom Camel context and a custom touter builder CamelContextManager.init(new DefaultCamelContext(registry)) - CamelContextManager.context.addRoutes(new CustomRouteBuilder) + CamelContextManager.mandatoryContext.addRoutes(new CustomRouteBuilder) val producer = actorOf[Producer1] val mediator = actorOf(new Transformer(producer)) 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 6a8aa461a8..5335c47664 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala @@ -12,7 +12,7 @@ import se.scalablesolutions.akka.camel._ * @author Martin Krasser */ object StandaloneApplication extends Application { - import CamelContextManager.context + import CamelContextManager._ import CamelServiceManager._ // 'externally' register typed actors @@ -21,12 +21,12 @@ object StandaloneApplication extends Application { // customize CamelContext CamelContextManager.init(new DefaultCamelContext(registry)) - CamelContextManager.context.addRoutes(new StandaloneApplicationRoute) + CamelContextManager.mandatoryContext.addRoutes(new StandaloneApplicationRoute) startCamelService // access 'externally' registered typed actors - assert("hello msg1" == context.createProducerTemplate.requestBody("direct:test", "msg1")) + assert("hello msg1" == mandatoryContext.createProducerTemplate.requestBody("direct:test", "msg1")) // set expectations on upcoming endpoint activation val activation = service.get.expectEndpointActivationCount(1) @@ -39,7 +39,7 @@ object StandaloneApplication extends Application { // access 'internally' (automatically) registered typed-actors // (see @consume annotation value at TypedConsumer2.foo method) - assert("default: msg3" == context.createProducerTemplate.requestBody("direct:default", "msg3")) + assert("default: msg3" == mandatoryContext.createProducerTemplate.requestBody("direct:default", "msg3")) stopCamelService @@ -60,7 +60,7 @@ object StandaloneSpringApplication extends Application { val appctx = new ClassPathXmlApplicationContext("/context-standalone.xml") // access 'externally' registered typed actors with typed-actor component - assert("hello msg3" == template.requestBody("direct:test3", "msg3")) + assert("hello msg3" == mandatoryTemplate.requestBody("direct:test3", "msg3")) appctx.close @@ -104,7 +104,7 @@ object StandaloneJmsApplication extends Application { // Send 10 messages to JMS topic directly for(i <- 1 to 10) { - CamelContextManager.template.sendBody(jmsUri, "Camel rocks (%d)" format i) + CamelContextManager.mandatoryTemplate.sendBody(jmsUri, "Camel rocks (%d)" format i) } stopCamelService diff --git a/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala b/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala index e8b0d727c3..94b36b3192 100644 --- a/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala +++ b/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala @@ -22,7 +22,7 @@ class CamelServiceSpringFeatureTest extends FeatureSpec with BeforeAndAfterEach 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")) + assert("hello sample" === mandatoryTemplate.requestBody("direct:test", "sample")) appctx.close } @@ -35,7 +35,7 @@ class CamelServiceSpringFeatureTest extends FeatureSpec with BeforeAndAfterEach assert(context.isInstanceOf[DefaultCamelContext]) context.asInstanceOf[DefaultCamelContext].setRegistry(registry) // access registered typed actor - assert("hello sample" === template.requestBody("typed-actor:custom?method=foo", "sample")) + assert("hello sample" === mandatoryTemplate.requestBody("typed-actor:custom?method=foo", "sample")) appctx.close } } From beb77b17d8b02e25ae266c7bc6ac419172213fd0 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Wed, 6 Oct 2010 13:58:56 +0200 Subject: [PATCH 12/32] Java API for CamelServiceManager and CamelContextManager (refactorings) --- akka-actor/src/main/scala/util/JavaAPI.scala | 84 +++++++------ .../main/scala/CamelContextLifecycle.scala | 119 ++++++++++++------ akka-camel/src/main/scala/CamelService.scala | 47 ++++--- .../SampleUntypedForwardingProducer.java | 2 +- .../test/scala/CamelServiceManagerTest.scala | 4 +- .../src/test/scala/RemoteConsumerTest.scala | 6 +- .../main/scala/StandaloneApplication.scala | 4 +- 7 files changed, 162 insertions(+), 104 deletions(-) diff --git a/akka-actor/src/main/scala/util/JavaAPI.scala b/akka-actor/src/main/scala/util/JavaAPI.scala index 248eaf7fbe..adf074aad9 100644 --- a/akka-actor/src/main/scala/util/JavaAPI.scala +++ b/akka-actor/src/main/scala/util/JavaAPI.scala @@ -1,23 +1,21 @@ package se.scalablesolutions.akka.util object JavaAPI { - /** A Function interface - * Used to create first-class-functions is Java (sort of) - * Java API + /** + * A Function interface. Used to create first-class-functions is Java (sort of). */ trait Function[T,R] { def apply(param: T): R } /** A Procedure is like a Function, but it doesn't produce a return value - * Java API */ trait Procedure[T] { def apply(param: T): Unit } /** - * An executable piece of code that takes no parameters and doesn't return any value + * An executable piece of code that takes no parameters and doesn't return any value. */ trait SideEffect { def apply: Unit @@ -31,45 +29,53 @@ object JavaAPI { * Java API */ sealed abstract class Option[A] extends java.lang.Iterable[A] { + import scala.collection.JavaConversions._ + def get: A - def isDefined: Boolean + def isEmpty: Boolean + def isDefined = !isEmpty def asScala: scala.Option[A] + def iterator = if (isEmpty) Iterator.empty else Iterator.single(get) } - /** - * Class Some[A] represents existing values of type - * A. - *

- * Java API - */ - final case class Some[A](v: A) extends Option[A] { - import scala.collection.JavaConversions._ + object Option { + /** + * Option factory that creates Some + */ + def some[A](v: A): Option[A] = Some(v) - def get = v - def iterator = Iterator.single(v) - def isDefined = true - def asScala = scala.Some(v) + /** + * Option factory that creates None + */ + def none[A] = None.asInstanceOf[Option[A]] + + /** + * Option factory that creates None if + * v is null, Some(v) otherwise. + */ + def option[A](v: A): Option[A] = if (v == null) none else some(v) + + /** + * Class Some[A] represents existing values of type + * A. + */ + private final case class Some[A](v: A) extends Option[A] { + def get = v + def isEmpty = false + def asScala = scala.Some(v) + } + + /** + * This case object represents non-existent values. + */ + private case object None extends Option[Nothing] { + def get = throw new NoSuchElementException("None.get") + def isEmpty = true + def asScala = scala.None + } + + implicit def java2ScalaOption[A](o: Option[A]): scala.Option[A] = o.asScala + implicit def scala2JavaOption[A](o: scala.Option[A]): Option[A] = option(o.get) } - - /** - * This case object represents non-existent values. - *

- * Java API - */ - case class None[A]() extends Option[A] { - import scala.collection.JavaConversions._ - - def get = throw new NoSuchElementException("None.get") - def iterator = Iterator.empty - def isDefined = false - def asScala = scala.None - } - - def some[A](v: A) = Some(v) - def none[A] = None[A] - - implicit def java2ScalaOption[A](o: Option[A]): scala.Option[A] = o.asScala - implicit def scala2JavaOption[A](o: scala.Option[A]): Option[A] = - if (o.isDefined) Some(o.get) else None[A] } diff --git a/akka-camel/src/main/scala/CamelContextLifecycle.scala b/akka-camel/src/main/scala/CamelContextLifecycle.scala index 8348ba4590..7aadac8ac1 100644 --- a/akka-camel/src/main/scala/CamelContextLifecycle.scala +++ b/akka-camel/src/main/scala/CamelContextLifecycle.scala @@ -1,5 +1,5 @@ /** - * Copyright (C) 2009-2010 Scalable Solutions AB + * Copyright (C) 2009-2010 Scalable Solutions AB */ package se.scalablesolutions.akka.camel @@ -19,7 +19,7 @@ import se.scalablesolutions.akka.util.JavaAPI.{Option => JOption} * * @author Martin Krasser */ -trait CamelContextLifecycle extends CamelContextLifecycleJavaAPI with Logging { +trait CamelContextLifecycle extends Logging { // TODO: enforce correct state transitions // valid: init -> start -> stop -> init ... @@ -41,25 +41,65 @@ trait CamelContextLifecycle extends CamelContextLifecycleJavaAPI with Logging { private[camel] var typedActorRegistry: Map[String, AnyRef] = _ /** - * Returns Some(CamelContext) if CamelContextLifecycle - * has been initialized, otherwise None. + * Returns Some(CamelContext) (containing the current CamelContext) + * if CamelContextLifecycle has been initialized, otherwise None. */ - protected def context: Option[CamelContext] = _context + def context: Option[CamelContext] = _context /** - * Returns Some(ProducerTemplate) if CamelContextLifecycle - * has been initialized, otherwise None. + * Returns Some(ProducerTemplate) (containing the current ProducerTemplate) + * if CamelContextLifecycle has been initialized, otherwise None. */ - protected def template: Option[ProducerTemplate] = _template + def template: Option[ProducerTemplate] = _template + /** + * Returns Some(CamelContext) (containing the current CamelContext) + * if CamelContextLifecycle has been initialized, otherwise None. + *

+ * Java API. + */ + def getContext: JOption[CamelContext] = context + + /** + * Returns Some(ProducerTemplate) (containing the current ProducerTemplate) + * if CamelContextLifecycle has been initialized, otherwise None. + *

+ * Java API. + */ + def getTemplate: JOption[ProducerTemplate] = template + + /** + * Returns the current CamelContext if this CamelContextLifecycle + * has been initialized, otherwise throws an IllegalStateException. + */ def mandatoryContext = if (context.isDefined) context.get else throw new IllegalStateException("no current CamelContext") + /** + * Returns the current ProducerTemplate if this CamelContextLifecycle + * has been initialized, otherwise throws an IllegalStateException. + */ def mandatoryTemplate = if (template.isDefined) template.get else throw new IllegalStateException("no current ProducerTemplate") - + + /** + * Returns the current CamelContext if this CamelContextLifecycle + * has been initialized, otherwise throws an IllegalStateException. + *

+ * Java API. + */ + def getMandatoryContext = mandatoryContext + + /** + * Returns the current ProducerTemplate if this CamelContextLifecycle + * has been initialized, otherwise throws an IllegalStateException. + *

+ * Java API. + */ + def getMandatoryTemplate = mandatoryTemplate + def initialized = _initialized def started = _started @@ -89,6 +129,7 @@ trait CamelContextLifecycle extends CamelContextLifecycleJavaAPI with Logging { t.stop c.stop _started = false + _initialized = false log.info("Camel context stopped") } } @@ -120,38 +161,42 @@ trait CamelContextLifecycle extends CamelContextLifecycleJavaAPI with Logging { } } -/** - * Java API for CamelContextLifecycle. - * - * @author Martin Krasser - */ -trait CamelContextLifecycleJavaAPI { this: CamelContextLifecycle => - /** - * Returns Some(CamelContext) if CamelContextLifecycle - * has been initialized, otherwise None. - */ - def getContext: JOption[CamelContext] = context - - /** - * Returns Some(ProducerTemplate) if CamelContextLifecycle - * has been initialized, otherwise None. - */ - def getTemplate: JOption[ProducerTemplate] = template -} - /** * Manages a global CamelContext and an associated ProducerTemplate. */ -object CamelContextManager extends CamelContextLifecycle with CamelContextLifecycleJavaAPI { - /** - * Returns Some(CamelContext) if CamelContextLifecycle - * has been initialized, otherwise None. - */ - override def context: Option[CamelContext] = super.context +object CamelContextManager extends CamelContextLifecycle { + + // ----------------------------------------------------- + // The inherited getters aren't statically accessible + // from Java. Therefore, they are redefined here. + // TODO: investigate if this is a Scala bug. + // ----------------------------------------------------- /** - * Returns Some(ProducerTemplate) if CamelContextLifecycle - * has been initialized, otherwise None. + * see CamelContextLifecycle.getContext + *

+ * Java API. */ - override def template: Option[ProducerTemplate] = super.template + override def getContext: JOption[CamelContext] = super.getContext + + /** + * see CamelContextLifecycle.getTemplate + *

+ * Java API. + */ + override def getTemplate: JOption[ProducerTemplate] = super.getTemplate + + /** + * see CamelContextLifecycle.getMandatoryContext + *

+ * Java API. + */ + override def getMandatoryContext = super.getMandatoryContext + + /** + * see CamelContextLifecycle.getMandatoryTemplate + *

+ * Java API. + */ + override def getMandatoryTemplate = super.getMandatoryTemplate } diff --git a/akka-camel/src/main/scala/CamelService.scala b/akka-camel/src/main/scala/CamelService.scala index 3d6705da00..8509c10b64 100644 --- a/akka-camel/src/main/scala/CamelService.scala +++ b/akka-camel/src/main/scala/CamelService.scala @@ -10,8 +10,8 @@ import org.apache.camel.CamelContext import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.actor.{AspectInitRegistry, ActorRegistry} import se.scalablesolutions.akka.config.Config._ -import se.scalablesolutions.akka.util.{Logging, Bootable} import se.scalablesolutions.akka.util.JavaAPI.{Option => JOption} +import se.scalablesolutions.akka.util.{Logging, Bootable} /** * Publishes (untyped) consumer actors and typed consumer actors via Camel endpoints. Actors @@ -74,7 +74,7 @@ trait CamelService extends Bootable with Logging { // Register this instance as current CamelService and return it CamelServiceManager.register(this) - CamelServiceManager.service.get + CamelServiceManager.mandatoryService } /** @@ -116,7 +116,7 @@ trait CamelService extends Bootable with Logging { * * @author Martin Krasser */ -object CamelServiceManager extends CamelServiceManagerJavaAPI { +object CamelServiceManager { /** * The current (optional) CamelService. Is defined when a CamelService has been started. @@ -140,12 +140,34 @@ object CamelServiceManager extends CamelServiceManagerJavaAPI { def stopCamelService = for (s <- service) s.stop /** - * Returns Some(CamelService) if CamelService + * Returns Some(CamelService) if this CamelService * has been started, None otherwise. */ def service = _current - // TODO: add mandatoryService (throwing exception if service is not defined) + /** + * Returns the current CamelService if CamelService + * has been started, otherwise throws an IllegalStateException. + *

+ * Java API + */ + def getService: JOption[CamelService] = CamelServiceManager.service + + /** + * Returns Some(CamelService) (containing the current CamelService) + * if this CamelServicehas been started, None otherwise. + */ + def mandatoryService = + if (_current.isDefined) _current.get + else throw new IllegalStateException("co current Camel service") + + /** + * Returns Some(CamelService) (containing the current CamelService) + * if this CamelServicehas been started, None otherwise. + *

+ * Java API + */ + def getMandatoryService = mandatoryService private[camel] def register(service: CamelService) = if (_current.isDefined) throw new IllegalStateException("current CamelService already registered") @@ -156,21 +178,6 @@ object CamelServiceManager extends CamelServiceManagerJavaAPI { else throw new IllegalStateException("only current CamelService can be unregistered") } -/** - * Java API for CamelServiceManager. - * - * @author Martin Krasser - */ -trait CamelServiceManagerJavaAPI { - /** - * Returns Some(CamelService) if CamelService - * has been started, None otherwise. - *

- * Java API - */ - def getService: JOption[CamelService] = CamelServiceManager.service -} - /** * @author Martin Krasser */ 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 index ec2f7eb560..bfa34f42e5 100644 --- a/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedForwardingProducer.java +++ b/akka-camel/src/test/java/se/scalablesolutions/akka/camel/SampleUntypedForwardingProducer.java @@ -13,6 +13,6 @@ public class SampleUntypedForwardingProducer extends UntypedProducerActor { public void onReceiveAfterProduce(Object message) { Message msg = (Message)message; String body = msg.bodyAs(String.class); - CamelContextManager.mandatoryTemplate().sendBody("direct:forward-test-1", body); + CamelContextManager.getMandatoryTemplate().sendBody("direct:forward-test-1", body); } } diff --git a/akka-camel/src/test/scala/CamelServiceManagerTest.scala b/akka-camel/src/test/scala/CamelServiceManagerTest.scala index 8f7916d574..712ffec70b 100644 --- a/akka-camel/src/test/scala/CamelServiceManagerTest.scala +++ b/akka-camel/src/test/scala/CamelServiceManagerTest.scala @@ -19,7 +19,7 @@ class CamelServiceManagerTest extends WordSpec with BeforeAndAfterAll with MustM "the startCamelService method been has been called" must { "have registered the started CamelService instance" in { val service = CamelServiceManager.startCamelService - CamelServiceManager.service.get must be theSameInstanceAs (service) + CamelServiceManager.mandatoryService must be theSameInstanceAs (service) } } "the stopCamelService method been has been called" must { @@ -35,7 +35,7 @@ class CamelServiceManagerTest extends WordSpec with BeforeAndAfterAll with MustM "a CamelService instance has been started externally" must { "have registered the started CamelService instance" in { service.start - CamelServiceManager.service.get must be theSameInstanceAs (service) + CamelServiceManager.mandatoryService must be theSameInstanceAs (service) } } "the current CamelService instance has been stopped externally" must { diff --git a/akka-camel/src/test/scala/RemoteConsumerTest.scala b/akka-camel/src/test/scala/RemoteConsumerTest.scala index 393f7a9a53..2218aac25a 100644 --- a/akka-camel/src/test/scala/RemoteConsumerTest.scala +++ b/akka-camel/src/test/scala/RemoteConsumerTest.scala @@ -45,7 +45,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh val consumer = actorOf[RemoteConsumer].start when("remote consumer publication is triggered") - var latch = service.get.expectEndpointActivationCount(1) + var latch = mandatoryService.expectEndpointActivationCount(1) consumer !! "init" assert(latch.await(5000, TimeUnit.MILLISECONDS)) @@ -61,7 +61,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh val consumer = TypedActor.newRemoteInstance(classOf[SampleRemoteTypedConsumer], classOf[SampleRemoteTypedConsumerImpl], host, port) when("remote typed consumer publication is triggered") - var latch = service.get.expectEndpointActivationCount(1) + var latch = mandatoryService.expectEndpointActivationCount(1) consumer.foo("init") assert(latch.await(5000, TimeUnit.MILLISECONDS)) @@ -77,7 +77,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh val consumer = UntypedActor.actorOf(classOf[SampleRemoteUntypedConsumer]).start when("remote untyped consumer publication is triggered") - var latch = service.get.expectEndpointActivationCount(1) + var latch = mandatoryService.expectEndpointActivationCount(1) consumer.sendRequestReply(Message("init", Map("test" -> "init"))) assert(latch.await(5000, TimeUnit.MILLISECONDS)) 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 5335c47664..2ecccb1e02 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala @@ -29,7 +29,7 @@ object StandaloneApplication extends Application { assert("hello msg1" == mandatoryContext.createProducerTemplate.requestBody("direct:test", "msg1")) // set expectations on upcoming endpoint activation - val activation = service.get.expectEndpointActivationCount(1) + val activation = mandatoryService.expectEndpointActivationCount(1) // 'internally' register typed actor (requires CamelService) TypedActor.newInstance(classOf[TypedConsumer2], classOf[TypedConsumer2Impl]) @@ -86,7 +86,7 @@ object StandaloneJmsApplication extends Application { startCamelService // Expect two consumer endpoints to be activated - val completion = service.get.expectEndpointActivationCount(2) + val completion = mandatoryService.expectEndpointActivationCount(2) val jmsUri = "jms:topic:test" // Wire publisher and consumer using a JMS topic From f3cd17f05bb342ae7884d63a8bb33a879943550e Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Wed, 6 Oct 2010 14:02:14 +0200 Subject: [PATCH 13/32] Minor reformattings --- akka-actor/src/main/scala/util/JavaAPI.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/util/JavaAPI.scala b/akka-actor/src/main/scala/util/JavaAPI.scala index adf074aad9..8222cde235 100644 --- a/akka-actor/src/main/scala/util/JavaAPI.scala +++ b/akka-actor/src/main/scala/util/JavaAPI.scala @@ -21,10 +21,10 @@ object JavaAPI { def apply: Unit } - /** + /** * This class represents optional values. Instances of Option - * are either instances of case class Some or it is case - * object None. + * are either instances of case class Some or it is case + * object None. *

* Java API */ From cec86ccf5fdda04d0d6ba15b0c2e45c550b36c9e Mon Sep 17 00:00:00 2001 From: Michael Kober Date: Thu, 7 Oct 2010 10:10:32 +0200 Subject: [PATCH 14/32] fix:ensure that typed actor module is enabled in typed actor methods --- akka-actor/src/main/scala/actor/ActorRegistry.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/akka-actor/src/main/scala/actor/ActorRegistry.scala b/akka-actor/src/main/scala/actor/ActorRegistry.scala index f4ef69f353..40f480c86e 100644 --- a/akka-actor/src/main/scala/actor/ActorRegistry.scala +++ b/akka-actor/src/main/scala/actor/ActorRegistry.scala @@ -46,6 +46,11 @@ object ActorRegistry extends ListenerManagement { * Returns all actors in the system. */ def actors: Array[ActorRef] = filter(_ => true) + + /** + * Returns the number of actors in the system. + */ + def size : Int = actorsByUUID.size /** * Invokes a function for all actors. @@ -127,6 +132,7 @@ object ActorRegistry extends ListenerManagement { * Invokes a function for all typed actors. */ def foreachTypedActor(f: (AnyRef) => Unit) = { + TypedActorModule.ensureTypedActorEnabled val elements = actorsByUUID.elements while (elements.hasMoreElements) { val proxy = typedActorFor(elements.nextElement) @@ -141,6 +147,7 @@ object ActorRegistry extends ListenerManagement { * Returns None if the function never returns Some */ def findTypedActor[T](f: PartialFunction[AnyRef,T]) : Option[T] = { + TypedActorModule.ensureTypedActorEnabled val elements = actorsByUUID.elements while (elements.hasMoreElements) { val proxy = typedActorFor(elements.nextElement) @@ -178,6 +185,7 @@ object ActorRegistry extends ListenerManagement { * Finds any typed actor that matches T. */ def typedActorFor[T <: AnyRef](implicit manifest: Manifest[T]): Option[AnyRef] = { + TypedActorModule.ensureTypedActorEnabled def predicate(proxy: AnyRef) : Boolean = { val actorRef = actorFor(proxy) actorRef.isDefined && manifest.erasure.isAssignableFrom(actorRef.get.actor.getClass) From 78c0b8164c074784c6341e217637104e4bd34e82 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Thu, 7 Oct 2010 12:15:59 +0200 Subject: [PATCH 15/32] Fixing UUID remote request bug --- akka-actor/src/main/scala/util/LockUtil.scala | 58 +++++++++++++++ .../src/main/scala/remote/RemoteClient.scala | 72 +++++++++---------- .../serialization/SerializationProtocol.scala | 4 +- .../ClientInitiatedRemoteActorSpec.scala | 13 ++++ 4 files changed, 107 insertions(+), 40 deletions(-) diff --git a/akka-actor/src/main/scala/util/LockUtil.scala b/akka-actor/src/main/scala/util/LockUtil.scala index 3d1261e468..6df0695f03 100644 --- a/akka-actor/src/main/scala/util/LockUtil.scala +++ b/akka-actor/src/main/scala/util/LockUtil.scala @@ -111,4 +111,62 @@ class SimpleLock { def unlock() { acquired.set(false) } +} + +/** + * An atomic switch that can be either on or off + */ +class Switch(startAsOn: Boolean = false) { + private val switch = new AtomicBoolean(startAsOn) + + protected def transcend(from: Boolean,action: => Unit): Boolean = { + if (switch.compareAndSet(from,!from)) { + try { + action + } catch { + case t => + switch.compareAndSet(!from,from) //Revert status + throw t + } + true + } else false + } + + def switchOff(action: => Unit): Boolean = transcend(from = true, action) + def switchOn(action: => Unit): Boolean = transcend(from = false,action) + + def ifOnYield[T](action: => T): Option[T] = { + if (switch.get) + Some(action) + else + None + } + + def ifOffYield[T](action: => T): Option[T] = { + if (switch.get) + Some(action) + else + None + } + + def ifOn(action: => Unit): Boolean = { + if (switch.get) { + action + true + } + else + false + } + + def ifOff(action: => Unit): Boolean = { + if (!switch.get) { + action + true + } + else + false + } + + def isOn = switch.get + def isOff = !isOn } \ No newline at end of file diff --git a/akka-remote/src/main/scala/remote/RemoteClient.scala b/akka-remote/src/main/scala/remote/RemoteClient.scala index 2694c9aee8..e39b83a503 100644 --- a/akka-remote/src/main/scala/remote/RemoteClient.scala +++ b/akka-remote/src/main/scala/remote/RemoteClient.scala @@ -30,7 +30,7 @@ import java.util.concurrent.atomic.AtomicLong import scala.collection.mutable.{HashSet, HashMap} import scala.reflect.BeanProperty import se.scalablesolutions.akka.actor._ -import se.scalablesolutions.akka.util.{Address, ListenerManagement, Logging, Duration} +import se.scalablesolutions.akka.util._ /** * Life-cycle events for RemoteClient. @@ -200,56 +200,52 @@ class RemoteClient private[akka] ( private val remoteAddress = new InetSocketAddress(hostname, port) //FIXME rewrite to a wrapper object (minimize volatile access and maximize encapsulation) - @volatile private[remote] var isRunning = false @volatile private var bootstrap: ClientBootstrap = _ @volatile private[remote] var connection: ChannelFuture = _ @volatile private[remote] var openChannels: DefaultChannelGroup = _ @volatile private var timer: HashedWheelTimer = _ + private[remote] val runSwitch = new Switch() + + private[remote] def isRunning = runSwitch.isOn private val reconnectionTimeWindow = Duration(config.getInt( "akka.remote.client.reconnection-time-window", 600), TIME_UNIT).toMillis @volatile private var reconnectionTimeWindowStart = 0L - def connect = synchronized { - if (!isRunning) { - openChannels = new DefaultChannelGroup(classOf[RemoteClient].getName) - timer = new HashedWheelTimer - bootstrap = new ClientBootstrap( - new NioClientSocketChannelFactory( - Executors.newCachedThreadPool,Executors.newCachedThreadPool - ) + def connect = runSwitch switchOn { + openChannels = new DefaultChannelGroup(classOf[RemoteClient].getName) + timer = new HashedWheelTimer + bootstrap = new ClientBootstrap( + new NioClientSocketChannelFactory( + Executors.newCachedThreadPool,Executors.newCachedThreadPool ) - bootstrap.setPipelineFactory(new RemoteClientPipelineFactory(name, futures, supervisors, bootstrap, remoteAddress, timer, this)) - bootstrap.setOption("tcpNoDelay", true) - bootstrap.setOption("keepAlive", true) - connection = bootstrap.connect(remoteAddress) - log.info("Starting remote client connection to [%s:%s]", hostname, port) - // Wait until the connection attempt succeeds or fails. - val channel = connection.awaitUninterruptibly.getChannel - openChannels.add(channel) - if (!connection.isSuccess) { - notifyListeners(RemoteClientError(connection.getCause, this)) - log.error(connection.getCause, "Remote client connection to [%s:%s] has failed", hostname, port) - } - notifyListeners(RemoteClientStarted(this)) - isRunning = true + ) + bootstrap.setPipelineFactory(new RemoteClientPipelineFactory(name, futures, supervisors, bootstrap, remoteAddress, timer, this)) + bootstrap.setOption("tcpNoDelay", true) + bootstrap.setOption("keepAlive", true) + connection = bootstrap.connect(remoteAddress) + log.info("Starting remote client connection to [%s:%s]", hostname, port) + // Wait until the connection attempt succeeds or fails. + val channel = connection.awaitUninterruptibly.getChannel + openChannels.add(channel) + if (!connection.isSuccess) { + notifyListeners(RemoteClientError(connection.getCause, this)) + log.error(connection.getCause, "Remote client connection to [%s:%s] has failed", hostname, port) } + notifyListeners(RemoteClientStarted(this)) } - def shutdown = synchronized { + def shutdown = runSwitch switchOff { log.info("Shutting down %s", name) - if (isRunning) { - isRunning = false - notifyListeners(RemoteClientShutdown(this)) - timer.stop - timer = null - openChannels.close.awaitUninterruptibly - openChannels = null - bootstrap.releaseExternalResources - bootstrap = null - connection = null - log.info("%s has been shut down", name) - } + notifyListeners(RemoteClientShutdown(this)) + timer.stop + timer = null + openChannels.close.awaitUninterruptibly + openChannels = null + bootstrap.releaseExternalResources + bootstrap = null + connection = null + log.info("%s has been shut down", name) } @deprecated("Use addListener instead") @@ -423,7 +419,7 @@ class RemoteClientHandler( } } - override def channelClosed(ctx: ChannelHandlerContext, event: ChannelStateEvent) = if (client.isRunning) { + override def channelClosed(ctx: ChannelHandlerContext, event: ChannelStateEvent) = client.runSwitch ifOn { if (client.isWithinReconnectionTimeWindow) { timer.newTimeout(new TimerTask() { def run(timeout: Timeout) = { diff --git a/akka-remote/src/main/scala/serialization/SerializationProtocol.scala b/akka-remote/src/main/scala/serialization/SerializationProtocol.scala index 427c3ad721..847911c43f 100644 --- a/akka-remote/src/main/scala/serialization/SerializationProtocol.scala +++ b/akka-remote/src/main/scala/serialization/SerializationProtocol.scala @@ -285,9 +285,9 @@ object RemoteActorSerialization { case ActorType.TypedActor => actorInfoBuilder.setActorType(TYPED_ACTOR) } val actorInfo = actorInfoBuilder.build - + val requestUuid = newUuid val requestBuilder = RemoteRequestProtocol.newBuilder - .setUuid(UuidProtocol.newBuilder.setHigh(uuid.getTime).setLow(uuid.getClockSeqAndNode).build) + .setUuid(UuidProtocol.newBuilder.setHigh(requestUuid.getTime).setLow(requestUuid.getClockSeqAndNode).build) .setMessage(MessageSerializer.serialize(message)) .setActorInfo(actorInfo) .setIsOneWay(isOneWay) diff --git a/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala b/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala index d39b58d41d..284ba0b87b 100644 --- a/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala +++ b/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala @@ -123,6 +123,19 @@ class ClientInitiatedRemoteActorSpec extends JUnitSuite { actor.stop } + @Test + def shouldSendBangBangMessageAndReceiveReplyConcurrently = { + val actors = (1 to 10). + map(num => { + val a = actorOf[RemoteActorSpecActorBidirectional] + a.makeRemote(HOSTNAME, PORT1) + a.start + }).toList + actors.map(_ !!! "Hello"). + foreach(future => assert("World" === future.await.result.asInstanceOf[Option[String]].get)) + actors.foreach(_.stop) + } + @Test def shouldSendAndReceiveRemoteException { implicit val timeout = 500000000L From 43f5e7af3dd34c0a68799f7d29b9b83fefca6af3 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Thu, 7 Oct 2010 14:02:25 +0200 Subject: [PATCH 16/32] =?UTF-8?q?Fixing=20bug=20where=20ReceiveTimeout=20w?= =?UTF-8?q?asn=C2=B4t=20turned=20off=20on=20actorref.stop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- akka-actor/src/main/scala/actor/ActorRef.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/akka-actor/src/main/scala/actor/ActorRef.scala b/akka-actor/src/main/scala/actor/ActorRef.scala index 552fe2cfdf..821e699965 100644 --- a/akka-actor/src/main/scala/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/actor/ActorRef.scala @@ -825,6 +825,7 @@ class LocalActorRef private[akka] ( */ def stop() = guard.withGuard { if (isRunning) { + receiveTimeout = None cancelReceiveTimeout dispatcher.unregister(this) _transactionFactory = None From cacba818f249a468d3241c44e1278abf4851a579 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Thu, 7 Oct 2010 14:45:40 +0200 Subject: [PATCH 17/32] Moved Java API support to japi package. --- akka-actor/src/main/scala/actor/Agent.scala | 2 +- .../scala/dataflow/DataFlowVariable.scala | 2 +- akka-actor/src/main/scala/japi/JavaAPI.scala | 78 ++++++++++++++++++ akka-actor/src/main/scala/util/JavaAPI.scala | 81 ------------------- .../main/scala/CamelContextLifecycle.scala | 2 +- akka-camel/src/main/scala/CamelService.scala | 2 +- .../scala/CamelServiceSpringFeatureTest.scala | 6 +- 7 files changed, 85 insertions(+), 88 deletions(-) create mode 100644 akka-actor/src/main/scala/japi/JavaAPI.scala delete mode 100644 akka-actor/src/main/scala/util/JavaAPI.scala diff --git a/akka-actor/src/main/scala/actor/Agent.scala b/akka-actor/src/main/scala/actor/Agent.scala index 76e3e1d9db..6b9385ca4e 100644 --- a/akka-actor/src/main/scala/actor/Agent.scala +++ b/akka-actor/src/main/scala/actor/Agent.scala @@ -6,7 +6,7 @@ package se.scalablesolutions.akka.actor import se.scalablesolutions.akka.stm.Ref import se.scalablesolutions.akka.AkkaException -import se.scalablesolutions.akka.util.JavaAPI.{ Function => JFunc, Procedure => JProc } +import se.scalablesolutions.akka.japi.{ Function => JFunc, Procedure => JProc } import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.CountDownLatch diff --git a/akka-actor/src/main/scala/dataflow/DataFlowVariable.scala b/akka-actor/src/main/scala/dataflow/DataFlowVariable.scala index 9c91b40833..56face4b6b 100644 --- a/akka-actor/src/main/scala/dataflow/DataFlowVariable.scala +++ b/akka-actor/src/main/scala/dataflow/DataFlowVariable.scala @@ -11,7 +11,7 @@ import se.scalablesolutions.akka.actor.{Actor, ActorRef} import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.dispatch.CompletableFuture import se.scalablesolutions.akka.AkkaException -import se.scalablesolutions.akka.util.JavaAPI.{ Function, SideEffect } +import se.scalablesolutions.akka.japi.{ Function, SideEffect } /** * Implements Oz-style dataflow (single assignment) variables. diff --git a/akka-actor/src/main/scala/japi/JavaAPI.scala b/akka-actor/src/main/scala/japi/JavaAPI.scala new file mode 100644 index 0000000000..7e79fe8184 --- /dev/null +++ b/akka-actor/src/main/scala/japi/JavaAPI.scala @@ -0,0 +1,78 @@ +package se.scalablesolutions.akka.japi + +/** + * A Function interface. Used to create first-class-functions is Java (sort of). + */ +trait Function[T,R] { + def apply(param: T): R +} + +/** A Procedure is like a Function, but it doesn't produce a return value + */ +trait Procedure[T] { + def apply(param: T): Unit +} + +/** + * An executable piece of code that takes no parameters and doesn't return any value. + */ +trait SideEffect { + def apply: Unit +} + +/** + * This class represents optional values. Instances of Option + * are either instances of case class Some or it is case + * object None. + *

+ * Java API + */ +sealed abstract class Option[A] extends java.lang.Iterable[A] { + import scala.collection.JavaConversions._ + + def get: A + def isEmpty: Boolean + def isDefined = !isEmpty + def asScala: scala.Option[A] + def iterator = if (isEmpty) Iterator.empty else Iterator.single(get) +} + +object Option { + /** + * Option factory that creates Some + */ + def some[A](v: A): Option[A] = Some(v) + + /** + * Option factory that creates None + */ + def none[A] = None.asInstanceOf[Option[A]] + + /** + * Option factory that creates None if + * v is null, Some(v) otherwise. + */ + def option[A](v: A): Option[A] = if (v == null) none else some(v) + + /** + * Class Some[A] represents existing values of type + * A. + */ + final case class Some[A](v: A) extends Option[A] { + def get = v + def isEmpty = false + def asScala = scala.Some(v) + } + + /** + * This case object represents non-existent values. + */ + private case object None extends Option[Nothing] { + def get = throw new NoSuchElementException("None.get") + def isEmpty = true + def asScala = scala.None + } + + implicit def java2ScalaOption[A](o: Option[A]): scala.Option[A] = o.asScala + implicit def scala2JavaOption[A](o: scala.Option[A]): Option[A] = option(o.get) +} \ No newline at end of file diff --git a/akka-actor/src/main/scala/util/JavaAPI.scala b/akka-actor/src/main/scala/util/JavaAPI.scala deleted file mode 100644 index 8222cde235..0000000000 --- a/akka-actor/src/main/scala/util/JavaAPI.scala +++ /dev/null @@ -1,81 +0,0 @@ -package se.scalablesolutions.akka.util - -object JavaAPI { - /** - * A Function interface. Used to create first-class-functions is Java (sort of). - */ - trait Function[T,R] { - def apply(param: T): R - } - - /** A Procedure is like a Function, but it doesn't produce a return value - */ - trait Procedure[T] { - def apply(param: T): Unit - } - - /** - * An executable piece of code that takes no parameters and doesn't return any value. - */ - trait SideEffect { - def apply: Unit - } - - /** - * This class represents optional values. Instances of Option - * are either instances of case class Some or it is case - * object None. - *

- * Java API - */ - sealed abstract class Option[A] extends java.lang.Iterable[A] { - import scala.collection.JavaConversions._ - - def get: A - def isEmpty: Boolean - def isDefined = !isEmpty - def asScala: scala.Option[A] - def iterator = if (isEmpty) Iterator.empty else Iterator.single(get) - } - - object Option { - /** - * Option factory that creates Some - */ - def some[A](v: A): Option[A] = Some(v) - - /** - * Option factory that creates None - */ - def none[A] = None.asInstanceOf[Option[A]] - - /** - * Option factory that creates None if - * v is null, Some(v) otherwise. - */ - def option[A](v: A): Option[A] = if (v == null) none else some(v) - - /** - * Class Some[A] represents existing values of type - * A. - */ - private final case class Some[A](v: A) extends Option[A] { - def get = v - def isEmpty = false - def asScala = scala.Some(v) - } - - /** - * This case object represents non-existent values. - */ - private case object None extends Option[Nothing] { - def get = throw new NoSuchElementException("None.get") - def isEmpty = true - def asScala = scala.None - } - - implicit def java2ScalaOption[A](o: Option[A]): scala.Option[A] = o.asScala - implicit def scala2JavaOption[A](o: scala.Option[A]): Option[A] = option(o.get) - } -} - diff --git a/akka-camel/src/main/scala/CamelContextLifecycle.scala b/akka-camel/src/main/scala/CamelContextLifecycle.scala index 7aadac8ac1..3996cd7baf 100644 --- a/akka-camel/src/main/scala/CamelContextLifecycle.scala +++ b/akka-camel/src/main/scala/CamelContextLifecycle.scala @@ -10,8 +10,8 @@ import org.apache.camel.{ProducerTemplate, CamelContext} import org.apache.camel.impl.DefaultCamelContext import se.scalablesolutions.akka.camel.component.TypedActorComponent +import se.scalablesolutions.akka.japi.{Option => JOption} import se.scalablesolutions.akka.util.Logging -import se.scalablesolutions.akka.util.JavaAPI.{Option => JOption} /** * Manages the lifecycle of a CamelContext. Allowed transitions are diff --git a/akka-camel/src/main/scala/CamelService.scala b/akka-camel/src/main/scala/CamelService.scala index 8509c10b64..d53ff07dec 100644 --- a/akka-camel/src/main/scala/CamelService.scala +++ b/akka-camel/src/main/scala/CamelService.scala @@ -10,7 +10,7 @@ import org.apache.camel.CamelContext import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.actor.{AspectInitRegistry, ActorRegistry} import se.scalablesolutions.akka.config.Config._ -import se.scalablesolutions.akka.util.JavaAPI.{Option => JOption} +import se.scalablesolutions.akka.japi.{Option => JOption} import se.scalablesolutions.akka.util.{Logging, Bootable} /** diff --git a/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala b/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala index 94b36b3192..246ad88f37 100644 --- a/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala +++ b/akka-spring/src/test/scala/CamelServiceSpringFeatureTest.scala @@ -21,7 +21,7 @@ class CamelServiceSpringFeatureTest extends FeatureSpec with BeforeAndAfterEach import CamelContextManager._ scenario("with a custom CamelContext and access a registered typed actor") { val appctx = new ClassPathXmlApplicationContext("/appContextCamelServiceCustom.xml") - assert(context.isInstanceOf[SpringCamelContext]) + assert(mandatoryContext.isInstanceOf[SpringCamelContext]) assert("hello sample" === mandatoryTemplate.requestBody("direct:test", "sample")) appctx.close } @@ -32,8 +32,8 @@ class CamelServiceSpringFeatureTest extends FeatureSpec with BeforeAndAfterEach val registry = new SimpleRegistry registry.put("custom", TypedActor.newInstance(classOf[SampleBeanIntf], classOf[SampleBean])) // set custom registry in DefaultCamelContext - assert(context.isInstanceOf[DefaultCamelContext]) - context.asInstanceOf[DefaultCamelContext].setRegistry(registry) + assert(mandatoryContext.isInstanceOf[DefaultCamelContext]) + mandatoryContext.asInstanceOf[DefaultCamelContext].setRegistry(registry) // access registered typed actor assert("hello sample" === mandatoryTemplate.requestBody("typed-actor:custom?method=foo", "sample")) appctx.close From 80d377aa1b047b92dc63a20b6ec492c7fe05f92e Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Thu, 7 Oct 2010 14:46:57 +0200 Subject: [PATCH 18/32] Tests for Java API support --- .../akka/japi/JavaAPITestBase.java | 42 +++++++++++++++++++ .../src/test/scala/japi/JavaAPITest.scala | 5 +++ 2 files changed, 47 insertions(+) create mode 100644 akka-actor/src/test/java/se/scalablesolutions/akka/japi/JavaAPITestBase.java create mode 100644 akka-actor/src/test/scala/japi/JavaAPITest.scala diff --git a/akka-actor/src/test/java/se/scalablesolutions/akka/japi/JavaAPITestBase.java b/akka-actor/src/test/java/se/scalablesolutions/akka/japi/JavaAPITestBase.java new file mode 100644 index 0000000000..af00530593 --- /dev/null +++ b/akka-actor/src/test/java/se/scalablesolutions/akka/japi/JavaAPITestBase.java @@ -0,0 +1,42 @@ +package se.scalablesolutions.akka.japi; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class JavaAPITestBase { + + @Test public void shouldCreateSomeString() { + Option o = Option.some("abc"); + assertFalse(o.isEmpty()); + assertTrue(o.isDefined()); + assertEquals("abc", o.get()); + } + + @Test public void shouldCreateNone() { + Option o1 = Option.none(); + assertTrue(o1.isEmpty()); + assertFalse(o1.isDefined()); + + Option o2 = Option.none(); + assertTrue(o2.isEmpty()); + assertFalse(o2.isDefined()); + } + + @Test public void shouldEnterForLoop() { + for(String s : Option.some("abc")) { + return; + } + fail("for-loop not entered"); + } + + @Test public void shouldNotEnterForLoop() { + for(Object o : Option.none()) { + fail("for-loop entered"); + } + } + + @Test public void shouldBeSingleton() { + assertSame(Option.none(), Option.none()); + } +} diff --git a/akka-actor/src/test/scala/japi/JavaAPITest.scala b/akka-actor/src/test/scala/japi/JavaAPITest.scala new file mode 100644 index 0000000000..721342b7af --- /dev/null +++ b/akka-actor/src/test/scala/japi/JavaAPITest.scala @@ -0,0 +1,5 @@ +package se.scalablesolutions.akka.japi + +import org.scalatest.junit.JUnitSuite + +class JavaAPITest extends JavaAPITestBase with JUnitSuite \ No newline at end of file From 579526d9578b66e665988a04b0fc489c02e129d3 Mon Sep 17 00:00:00 2001 From: Michael Kober Date: Mon, 4 Oct 2010 14:13:49 +0200 Subject: [PATCH 19/32] register client managed remote actors by uuid --- .../src/main/scala/remote/RemoteServer.scala | 39 +++++++++++++------ .../ClientInitiatedRemoteActorSpec.scala | 31 ++++++++++++++- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/akka-remote/src/main/scala/remote/RemoteServer.scala b/akka-remote/src/main/scala/remote/RemoteServer.scala index 27b9af15a2..40b312f879 100644 --- a/akka-remote/src/main/scala/remote/RemoteServer.scala +++ b/akka-remote/src/main/scala/remote/RemoteServer.scala @@ -572,6 +572,29 @@ class RemoteServerHandler( server.typedActorsByUuid().get(uuid) } + private def findActorByIdOrUuid(id: String, uuid: String) : ActorRef = { + var actorRefOrNull = if (id.startsWith(UUID_PREFIX)) { + findActorByUuid(id.substring(UUID_PREFIX.length)) + } else { + findActorById(id) + } + if (actorRefOrNull eq null) { + actorRefOrNull = findActorByUuid(uuid) + } + actorRefOrNull + } + + private def findTypedActorByIdOrUuid(id: String, uuid: String) : AnyRef = { + var actorRefOrNull = if (id.startsWith(UUID_PREFIX)) { + findTypedActorByUuid(id.substring(UUID_PREFIX.length)) + } else { + findTypedActorById(id) + } + if (actorRefOrNull eq null) { + actorRefOrNull = findTypedActorByUuid(uuid) + } + actorRefOrNull + } /** * Creates a new instance of the actor with name, uuid and timeout specified as arguments. @@ -587,11 +610,7 @@ class RemoteServerHandler( val name = actorInfo.getTarget val timeout = actorInfo.getTimeout - val actorRefOrNull = if (id.startsWith(UUID_PREFIX)) { - findActorByUuid(id.substring(UUID_PREFIX.length)) - } else { - findActorById(id) - } + val actorRefOrNull = findActorByIdOrUuid(id, uuidFrom(uuid.getHigh,uuid.getLow).toString) if (actorRefOrNull eq null) { try { @@ -603,7 +622,7 @@ class RemoteServerHandler( actorRef.id = id actorRef.timeout = timeout actorRef.remoteAddress = None - server.actors.put(id, actorRef) // register by id + server.actorsByUuid.put(actorRef.uuid.toString, actorRef) // register by uuid actorRef } catch { case e => @@ -618,11 +637,7 @@ class RemoteServerHandler( val uuid = actorInfo.getUuid val id = actorInfo.getId - val typedActorOrNull = if (id.startsWith(UUID_PREFIX)) { - findTypedActorByUuid(id.substring(UUID_PREFIX.length)) - } else { - findTypedActorById(id) - } + val typedActorOrNull = findTypedActorByIdOrUuid(id, uuidFrom(uuid.getHigh,uuid.getLow).toString) if (typedActorOrNull eq null) { val typedActorInfo = actorInfo.getTypedActorInfo @@ -639,7 +654,7 @@ class RemoteServerHandler( val newInstance = TypedActor.newInstance( interfaceClass, targetClass.asInstanceOf[Class[_ <: TypedActor]], actorInfo.getTimeout).asInstanceOf[AnyRef] - server.typedActors.put(id, newInstance) // register by id + server.typedActors.put(uuidFrom(uuid.getHigh,uuid.getLow).toString, newInstance) // register by uuid newInstance } catch { case e => diff --git a/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala b/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala index 284ba0b87b..ba550dc2aa 100644 --- a/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala +++ b/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala @@ -56,6 +56,15 @@ object ClientInitiatedRemoteActorSpec { SendOneWayAndReplySenderActor.latch.countDown } } + + class MyActorCustomConstructor extends Actor { + var prefix = "default-" + var count = 0 + def receive = { + case "incrPrefix" => count += 1; prefix = "" + count + "-" + case msg: String => self.reply(prefix + msg) + } + } } class ClientInitiatedRemoteActorSpec extends JUnitSuite { @@ -150,6 +159,26 @@ class ClientInitiatedRemoteActorSpec extends JUnitSuite { assert("Expected exception; to test fault-tolerance" === e.getMessage()) } actor.stop - } + } + + @Test + def shouldRegisterActorByUuid { + val actor1 = actorOf[MyActorCustomConstructor] + actor1.makeRemote(HOSTNAME, PORT1) + actor1.start + actor1 ! "incrPrefix" + assert((actor1 !! "test").get === "1-test") + actor1 ! "incrPrefix" + assert((actor1 !! "test").get === "2-test") + + val actor2 = actorOf[MyActorCustomConstructor] + actor2.makeRemote(HOSTNAME, PORT1) + actor2.start + + assert((actor2 !! "test").get === "default-test") + + actor1.stop + actor2.stop + } } From 06ec4cede8cc497843592761e5350685f96abdd6 Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 8 Oct 2010 11:06:22 +0200 Subject: [PATCH 20/32] - changed exchange types to case classes for java compatibility - made java api for string and protobuf convenience producers/consumers - implemented string and protobuf java api examples --- .../akka/amqp/ExampleSessionJava.java | 77 ++++++++++-- .../akka/amqp/ExchangeTypes.java | 11 -- .../se/scalablesolutions/akka/amqp/AMQP.scala | 119 ++++++++++++++++-- .../akka/amqp/ExampleSession.scala | 11 +- .../akka/amqp/ExchangeType.scala | 24 ++-- 5 files changed, 198 insertions(+), 44 deletions(-) delete mode 100644 akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExchangeTypes.java diff --git a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java index ad0d7c3841..a878bb6b05 100644 --- a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java +++ b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java @@ -5,9 +5,15 @@ import se.scalablesolutions.akka.actor.ActorRegistry; import se.scalablesolutions.akka.actor.UntypedActor; import se.scalablesolutions.akka.actor.UntypedActorFactory; +import se.scalablesolutions.akka.remote.protocol.RemoteProtocol; +import se.scalablesolutions.akka.util.Procedure; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static se.scalablesolutions.akka.amqp.ChannelCallbacks.*; +import static se.scalablesolutions.akka.amqp.ConnectionCallbacks.*; + public class ExampleSessionJava { public static void main(String... args) { @@ -21,13 +27,21 @@ public class ExampleSessionJava { printTopic("CALLBACK"); callback(); - printTopic("Happy hAkking :-)"); + printTopic("EASY STRING PRODUCER AND CONSUMER"); + easyStringProducerConsumer(); + + printTopic("EASY PROTOBUF PRODUCER AND CONSUMER"); + easyProtobufProducerConsumer(); + // postStop everything the amqp tree except the main AMQP supervisor // all connections/consumers/producers will be stopped AMQP.shutdownAll(); ActorRegistry.shutdownAll(); + + printTopic("Happy hAkking :-)"); + System.exit(0); } @@ -46,7 +60,7 @@ public class ExampleSessionJava { // defaults to amqp://guest:guest@localhost:5672/ ActorRef connection = AMQP.newConnection(); - AMQP.ExchangeParameters exchangeParameters = new AMQP.ExchangeParameters("my_direct_exchange", ExchangeTypes.DIRECT); + AMQP.ExchangeParameters exchangeParameters = new AMQP.ExchangeParameters("my_direct_exchange", new ExchangeType.Direct()); ActorRef deliveryHandler = UntypedActor.actorOf(DirectDeliveryHandlerActor.class); @@ -75,7 +89,7 @@ public class ExampleSessionJava { }); channelCallback.start(); - AMQP.ExchangeParameters exchangeParameters = new AMQP.ExchangeParameters("my_callback_exchange", ExchangeTypes.DIRECT); + AMQP.ExchangeParameters exchangeParameters = new AMQP.ExchangeParameters("my_callback_exchange", new ExchangeType.Direct()); AMQP.ChannelParameters channelParameters = new AMQP.ChannelParameters(channelCallback); ActorRef dummyHandler = UntypedActor.actorOf(DummyActor.class); @@ -83,7 +97,7 @@ public class ExampleSessionJava { ActorRef consumer = AMQP.newConsumer(connection, consumerParameters); - ActorRef producer = AMQP.newProducer(connection, new AMQP.ProducerParameters(exchangeParameters)); + ActorRef producer = AMQP.newProducer(connection, new AMQP.ProducerParameters(exchangeParameters, channelParameters)); // Wait until both channels (producer & consumer) are started before stopping the connection try { @@ -92,6 +106,49 @@ public class ExampleSessionJava { } connection.stop(); } + + public void easyStringProducerConsumer() { + ActorRef connection = AMQP.newConnection(); + + String exchangeName = "easy.string"; + + // listen by default to: + // exchange = optional exchangeName + // routingKey = provided routingKey or .request + // queueName = .in + Procedure procedure = new Procedure() { + public void apply(String message) { + System.out.println("### >> Received message: " + message); + } + }; + AMQP.newStringConsumer(connection, procedure, exchangeName); + + // send by default to: + // exchange = exchangeName + // routingKey = .request + AMQP.ProducerClient producer = AMQP.newStringProducer(connection, exchangeName); + + producer.send("This shit is easy!"); + } + + public void easyProtobufProducerConsumer() { + + ActorRef connection = AMQP.newConnection(); + + String exchangeName = "easy.protobuf"; + + Procedure procedure = new Procedure() { + public void apply(RemoteProtocol.AddressProtocol message) { + System.out.println("### >> Received message: " + message); + } + }; + + AMQP.newProtobufConsumer(connection, procedure, exchangeName, RemoteProtocol.AddressProtocol.class); + + AMQP.ProducerClient producerClient = AMQP.newProtobufProducer(connection, exchangeName); + + producerClient.send(RemoteProtocol.AddressProtocol.newBuilder().setHostname("akkarocks.com").setPort(1234).build()); + } } class DummyActor extends UntypedActor { @@ -109,11 +166,11 @@ class ChannelCallbackActor extends UntypedActor { } public void onReceive(Object message) throws Exception { - if (ChannelCallbacks.STARTED.getClass().isAssignableFrom(message.getClass())) { + if (STARTED.getClass().isAssignableFrom(message.getClass())) { System.out.println("### >> Channel callback: Started"); channelCountdown.countDown(); - } else if (ChannelCallbacks.RESTARTING.getClass().isAssignableFrom(message.getClass())) { - } else if (ChannelCallbacks.STOPPED.getClass().isAssignableFrom(message.getClass())) { + } else if (RESTARTING.getClass().isAssignableFrom(message.getClass())) { + } else if (STOPPED.getClass().isAssignableFrom(message.getClass())) { System.out.println("### >> Channel callback: Stopped"); } else throw new IllegalArgumentException("Unknown message: " + message); } @@ -122,10 +179,10 @@ class ChannelCallbackActor extends UntypedActor { class ConnectionCallbackActor extends UntypedActor { public void onReceive(Object message) throws Exception { - if (ConnectionCallbacks.CONNECTED.getClass().isAssignableFrom(message.getClass())) { + if (CONNECTED.getClass().isAssignableFrom(message.getClass())) { System.out.println("### >> Connection callback: Connected!"); - } else if (ConnectionCallbacks.RECONNECTING.getClass().isAssignableFrom(message.getClass())) { - } else if (ConnectionCallbacks.DISCONNECTED.getClass().isAssignableFrom(message.getClass())) { + } else if (RECONNECTING.getClass().isAssignableFrom(message.getClass())) { + } else if (DISCONNECTED.getClass().isAssignableFrom(message.getClass())) { System.out.println("### >> Connection callback: Disconnected!"); } else throw new IllegalArgumentException("Unknown message: " + message); } diff --git a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExchangeTypes.java b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExchangeTypes.java deleted file mode 100644 index 9a032554fe..0000000000 --- a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExchangeTypes.java +++ /dev/null @@ -1,11 +0,0 @@ -package se.scalablesolutions.akka.amqp; - -// Needed for Java API usage -public final class ExchangeTypes { - public static final ExchangeType DIRECT = Direct$.MODULE$; - public static final ExchangeType FANOUT = Fanout$.MODULE$; - public static final ExchangeType TOPIC = Topic$.MODULE$; - public static final ExchangeType MATCH = Match$.MODULE$; - - private ExchangeTypes() {} -} diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala index 1856f63f57..2373219a30 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala @@ -11,6 +11,8 @@ import com.rabbitmq.client.{ReturnListener, ShutdownListener, ConnectionFactory} import ConnectionFactory._ import com.rabbitmq.client.AMQP.BasicProperties import java.lang.{String, IllegalArgumentException} +import reflect.Manifest +import se.scalablesolutions.akka.util.Procedure /** * AMQP Actor API. Implements Connection, Producer and Consumer materialized as Actors. @@ -88,13 +90,13 @@ object AMQP { */ case class ExchangeParameters( exchangeName: String, - exchangeType: ExchangeType = Topic, + exchangeType: ExchangeType = ExchangeType.Topic(), exchangeDeclaration: Declaration = ActiveDeclaration(), configurationArguments: Map[String, AnyRef] = Map.empty) { // Needed for Java API usage def this(exchangeName: String) = - this (exchangeName, Topic, ActiveDeclaration(), Map.empty) + this (exchangeName, ExchangeType.Topic(), ActiveDeclaration(), Map.empty) // Needed for Java API usage def this(exchangeName: String, exchangeType: ExchangeType) = @@ -113,8 +115,7 @@ object AMQP { producerId: Option[String] = None, returnListener: Option[ReturnListener] = None, channelParameters: Option[ChannelParameters] = None) { - - def this() = this(None, None, None, None) + def this() = this (None, None, None, None) // Needed for Java API usage def this(exchangeParameters: ExchangeParameters) = this (Some(exchangeParameters), None, None, None) @@ -147,7 +148,6 @@ object AMQP { queueDeclaration: Declaration = ActiveDeclaration(), selfAcknowledging: Boolean = true, channelParameters: Option[ChannelParameters] = None) { - if (queueName.isEmpty) { queueDeclaration match { case ActiveDeclaration(true, _, _) => @@ -210,7 +210,7 @@ object AMQP { def this(routingKey: String, deliveryHandler: ActorRef, queueName: String, exchangeParameters: ExchangeParameters, queueDeclaration: Declaration, selfAcknowledging: Boolean, channelParameters: ChannelParameters) = this (routingKey, deliveryHandler, Some(queueName), Some(exchangeParameters), queueDeclaration, selfAcknowledging, Some(channelParameters)) - // How about that for some overloading... huh? :P (yes, I know, there are still posibilities left...sue me!) + // How about that for some overloading... huh? :P (yes, I know, there are still possibilities left...sue me!) // Who said java is easy :( } @@ -246,6 +246,15 @@ object AMQP { * Convenience */ class ProducerClient[O](client: ActorRef, routingKey: String, toBinary: ToBinary[O]) { + // Needed for Java API usage + def send(request: O): Unit = { + send(request, None) + } + // Needed for Java API usage + def send(request: O, replyTo: String): Unit = { + send(request, Some(replyTo)) + } + def send(request: O, replyTo: Option[String] = None) = { val basicProperties = new BasicProperties basicProperties.setReplyTo(replyTo.getOrElse(null)) @@ -255,6 +264,27 @@ object AMQP { def stop() = client.stop } + // Needed for Java API usage + def newStringProducer(connection: ActorRef, + exchangeName: String): ProducerClient[String] = { + newStringProducer(connection, Some(exchangeName)) + } + + // Needed for Java API usage + def newStringProducer(connection: ActorRef, + exchangeName: String, + routingKey: String): ProducerClient[String] = { + newStringProducer(connection, Some(exchangeName), Some(routingKey)) + } + + // Needed for Java API usage + def newStringProducer(connection: ActorRef, + exchangeName: String, + routingKey: String, + producerId: String): ProducerClient[String] = { + newStringProducer(connection, Some(exchangeName), Some(routingKey), Some(producerId)) + } + def newStringProducer(connection: ActorRef, exchangeName: Option[String], routingKey: Option[String] = None, @@ -273,6 +303,30 @@ object AMQP { new ProducerClient(producerRef, rKey, toBinary) } + // Needed for Java API usage + def newStringConsumer(connection: ActorRef, + handler: Procedure[String], + exchangeName: String): ActorRef = { + newStringConsumer(connection, handler.apply _, Some(exchangeName)) + } + + // Needed for Java API usage + def newStringConsumer(connection: ActorRef, + handler: Procedure[String], + exchangeName: String, + routingKey: String): ActorRef = { + newStringConsumer(connection, handler.apply _, Some(exchangeName), Some(routingKey)) + } + + // Needed for Java API usage + def newStringConsumer(connection: ActorRef, + handler: Procedure[String], + exchangeName: String, + routingKey: String, + queueName: String): ActorRef = { + newStringConsumer(connection, handler.apply _, Some(exchangeName), Some(routingKey), Some(queueName)) + } + def newStringConsumer(connection: ActorRef, handler: String => Unit, exchangeName: Option[String], @@ -295,6 +349,27 @@ object AMQP { } + // Needed for Java API usage + def newProtobufProducer[O <: com.google.protobuf.Message](connection: ActorRef, + exchangeName: String): ProducerClient[O] = { + newProtobufProducer(connection, Some(exchangeName)) + } + + // Needed for Java API usage + def newProtobufProducer[O <: com.google.protobuf.Message](connection: ActorRef, + exchangeName: String, + routingKey: String): ProducerClient[O] = { + newProtobufProducer(connection, Some(exchangeName), Some(routingKey)) + } + + // Needed for Java API usage + def newProtobufProducer[O <: com.google.protobuf.Message](connection: ActorRef, + exchangeName: String, + routingKey: String, + producerId: String): ProducerClient[O] = { + newProtobufProducer(connection, Some(exchangeName), Some(routingKey), Some(producerId)) + } + def newProtobufProducer[O <: com.google.protobuf.Message](connection: ActorRef, exchangeName: Option[String], routingKey: Option[String] = None, @@ -312,6 +387,36 @@ object AMQP { }) } + // Needed for Java API usage + def newProtobufConsumer[I <: com.google.protobuf.Message](connection: ActorRef, + handler: Procedure[I], + exchangeName: String, + clazz: Class[I]): ActorRef = { + implicit val manifest = Manifest.classType[I](clazz) + newProtobufConsumer[I](connection, handler.apply _, Some(exchangeName)) + } + + // Needed for Java API usage + def newProtobufConsumer[I <: com.google.protobuf.Message](connection: ActorRef, + handler: Procedure[I], + exchangeName: String, + routingKey: String, + clazz: Class[I]): ActorRef = { + implicit val manifest = Manifest.classType[I](clazz) + newProtobufConsumer[I](connection, handler.apply _, Some(exchangeName), Some(routingKey)) + } + + // Needed for Java API usage + def newProtobufConsumer[I <: com.google.protobuf.Message](connection: ActorRef, + handler: Procedure[I], + exchangeName: String, + routingKey: String, + queueName: String, + clazz: Class[I]): ActorRef = { + implicit val manifest = Manifest.classType[I](clazz) + newProtobufConsumer[I](connection, handler.apply _, Some(exchangeName), Some(routingKey), Some(queueName)) + } + def newProtobufConsumer[I <: com.google.protobuf.Message](connection: ActorRef, handler: I => Unit, exchangeName: Option[String], @@ -335,7 +440,7 @@ object AMQP { newConsumer(connection, ConsumerParameters(rKey, deliveryHandler, Some(qName), exchangeParameters)) } - + /** * Main supervisor */ diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala index 1fa39d1882..ed97b359eb 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala @@ -12,6 +12,7 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} import java.lang.String import se.scalablesolutions.akka.amqp.AMQP._ import se.scalablesolutions.akka.remote.protocol.RemoteProtocol.AddressProtocol +import se.scalablesolutions.akka.util.Procedure object ExampleSession { @@ -67,7 +68,7 @@ object ExampleSession { // defaults to amqp://guest:guest@localhost:5672/ val connection = AMQP.newConnection() - val exchangeParameters = ExchangeParameters("my_direct_exchange", Direct) + val exchangeParameters = ExchangeParameters("my_direct_exchange", Direct()) val consumer = AMQP.newConsumer(connection, ConsumerParameters("some.routing", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) @@ -82,7 +83,7 @@ object ExampleSession { // defaults to amqp://guest:guest@localhost:5672/ val connection = AMQP.newConnection() - val exchangeParameters = ExchangeParameters("my_fanout_exchange", Fanout) + val exchangeParameters = ExchangeParameters("my_fanout_exchange", Fanout()) val bushConsumer = AMQP.newConsumer(connection, ConsumerParameters("@george_bush", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) @@ -101,7 +102,7 @@ object ExampleSession { // defaults to amqp://guest:guest@localhost:5672/ val connection = AMQP.newConnection() - val exchangeParameters = ExchangeParameters("my_topic_exchange", Topic) + val exchangeParameters = ExchangeParameters("my_topic_exchange", Topic()) val bushConsumer = AMQP.newConsumer(connection, ConsumerParameters("@george_bush", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) @@ -135,7 +136,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_callback_exchange", Direct) + val exchangeParameters = ExchangeParameters("my_callback_exchange", Direct()) val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) val consumer = AMQP.newConsumer(connection, ConsumerParameters("callback.routing", actor { @@ -177,7 +178,7 @@ object ExampleSession { log.info("Received "+message) } - AMQP.newProtobufConsumer(connection, protobufMessageHandler, Some(exchangeName)) + AMQP.newProtobufConsumer(connection, protobufMessageHandler _, Some(exchangeName)) val producerClient = AMQP.newProtobufProducer[AddressProtocol](connection, Some(exchangeName)) producerClient.send(AddressProtocol.newBuilder.setHostname("akkarocks.com").setPort(1234).build) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala index 332a681ad4..38a0073c30 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala @@ -5,15 +5,17 @@ package se.scalablesolutions.akka.amqp sealed trait ExchangeType -case object Direct extends ExchangeType { - override def toString = "direct" -} -case object Topic extends ExchangeType { - override def toString = "topic" -} -case object Fanout extends ExchangeType { - override def toString = "fanout" -} -case object Match extends ExchangeType { - override def toString = "match" +object ExchangeType { + case class Direct() extends ExchangeType { + override def toString = "direct" + } + case class Topic() extends ExchangeType { + override def toString = "topic" + } + case class Fanout() extends ExchangeType { + override def toString = "fanout" + } + case class Match() extends ExchangeType { + override def toString = "match" + } } From 65e96e71c5ebec30e4bbecbd0854c91192d3a279 Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 8 Oct 2010 12:28:00 +0200 Subject: [PATCH 21/32] - made channel and connection callback java compatible --- .../akka/amqp/ChannelCallbacks.java | 10 ---------- .../akka/amqp/ConnectionCallbacks.java | 10 ---------- .../akka/amqp/ExampleSessionJava.java | 15 ++++++--------- .../scalablesolutions/akka/amqp/AMQPMessage.scala | 14 ++++++-------- .../akka/amqp/ExampleSession.scala | 2 +- .../akka/amqp/FaultTolerantChannelActor.scala | 10 +++++----- .../akka/amqp/FaultTolerantConnectionActor.scala | 8 ++++---- 7 files changed, 22 insertions(+), 47 deletions(-) delete mode 100644 akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ChannelCallbacks.java delete mode 100644 akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ConnectionCallbacks.java diff --git a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ChannelCallbacks.java b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ChannelCallbacks.java deleted file mode 100644 index 36f54fd825..0000000000 --- a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ChannelCallbacks.java +++ /dev/null @@ -1,10 +0,0 @@ -package se.scalablesolutions.akka.amqp; - -public final class ChannelCallbacks { - - public static final AMQPMessage STARTED = Started$.MODULE$; - public static final AMQPMessage RESTARTING = Restarting$.MODULE$; - public static final AMQPMessage STOPPED = Stopped$.MODULE$; - - private ChannelCallbacks() {} -} diff --git a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ConnectionCallbacks.java b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ConnectionCallbacks.java deleted file mode 100644 index 1314330928..0000000000 --- a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ConnectionCallbacks.java +++ /dev/null @@ -1,10 +0,0 @@ -package se.scalablesolutions.akka.amqp; - -public final class ConnectionCallbacks { - - public static final AMQPMessage CONNECTED = Connected$.MODULE$; - public static final AMQPMessage RECONNECTING = Reconnecting$.MODULE$; - public static final AMQPMessage DISCONNECTED = Disconnected$.MODULE$; - - private ConnectionCallbacks() {} -} diff --git a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java index a878bb6b05..6dd8c3dac3 100644 --- a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java +++ b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java @@ -11,9 +11,6 @@ import se.scalablesolutions.akka.util.Procedure; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static se.scalablesolutions.akka.amqp.ChannelCallbacks.*; -import static se.scalablesolutions.akka.amqp.ConnectionCallbacks.*; - public class ExampleSessionJava { public static void main(String... args) { @@ -166,11 +163,11 @@ class ChannelCallbackActor extends UntypedActor { } public void onReceive(Object message) throws Exception { - if (STARTED.getClass().isAssignableFrom(message.getClass())) { + if (Started.class.isAssignableFrom(message.getClass())) { System.out.println("### >> Channel callback: Started"); channelCountdown.countDown(); - } else if (RESTARTING.getClass().isAssignableFrom(message.getClass())) { - } else if (STOPPED.getClass().isAssignableFrom(message.getClass())) { + } else if (Restarting.class.isAssignableFrom(message.getClass())) { + } else if (Stopped.class.isAssignableFrom(message.getClass())) { System.out.println("### >> Channel callback: Stopped"); } else throw new IllegalArgumentException("Unknown message: " + message); } @@ -179,10 +176,10 @@ class ChannelCallbackActor extends UntypedActor { class ConnectionCallbackActor extends UntypedActor { public void onReceive(Object message) throws Exception { - if (CONNECTED.getClass().isAssignableFrom(message.getClass())) { + if (Connected.class.isAssignableFrom(message.getClass())) { System.out.println("### >> Connection callback: Connected!"); - } else if (RECONNECTING.getClass().isAssignableFrom(message.getClass())) { - } else if (DISCONNECTED.getClass().isAssignableFrom(message.getClass())) { + } else if (Reconnecting.class.isAssignableFrom(message.getClass())) { + } else if (Disconnected.class.isAssignableFrom(message.getClass())) { System.out.println("### >> Connection callback: Disconnected!"); } else throw new IllegalArgumentException("Unknown message: " + message); } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala index a99b1afc67..26fab46799 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala @@ -5,8 +5,6 @@ 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 @@ -46,18 +44,18 @@ case class Delivery( // connection messages case object Connect extends AMQPMessage -case object Connected extends AMQPMessage -case object Reconnecting extends AMQPMessage -case object Disconnected extends AMQPMessage +case class Connected() extends AMQPMessage // Needed for Java API usage +case class Reconnecting() extends AMQPMessage // Needed for Java API usage +case class Disconnected() extends AMQPMessage // Needed for Java API usage case object ChannelRequest extends InternalAMQPMessage // channel messages case object Start extends AMQPMessage -case object Started extends AMQPMessage -case object Restarting extends AMQPMessage -case object Stopped extends AMQPMessage +case class Started() extends AMQPMessage // Needed for Java API usage +case class Restarting() extends AMQPMessage // Needed for Java API usage +case class Stopped() extends AMQPMessage // Needed for Java API usage // delivery messages case class Acknowledge(deliveryTag: Long) extends AMQPMessage diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala index ed97b359eb..b0fc0dd59f 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala @@ -12,7 +12,7 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} import java.lang.String import se.scalablesolutions.akka.amqp.AMQP._ import se.scalablesolutions.akka.remote.protocol.RemoteProtocol.AddressProtocol -import se.scalablesolutions.akka.util.Procedure +import se.scalablesolutions.akka.amqp.ExchangeType._ object ExampleSession { diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala index 6617c62a44..d2333f8b7c 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala @@ -46,14 +46,14 @@ abstract private[amqp] class FaultTolerantChannelActor( } else { // channel error log.error(cause, "%s self restarting because of channel shutdown", toString) - notifyCallback(Restarting) + notifyCallback(Restarting()) self ! Start } } case Failure(cause) => log.error(cause, "%s self restarting because of channel failure", toString) closeChannel - notifyCallback(Restarting) + notifyCallback(Restarting()) self ! Start } @@ -81,7 +81,7 @@ abstract private[amqp] class FaultTolerantChannelActor( setupChannel(ch) channel = Some(ch) - notifyCallback(Started) + notifyCallback(Started()) log.info("Channel setup for %s", toString) } @@ -89,7 +89,7 @@ abstract private[amqp] class FaultTolerantChannelActor( channel.foreach { ch => if (ch.isOpen) ch.close - notifyCallback(Stopped) + notifyCallback(Stopped()) log.info("%s channel closed", toString) } channel = None @@ -100,7 +100,7 @@ abstract private[amqp] class FaultTolerantChannelActor( } override def preRestart(reason: Throwable) = { - notifyCallback(Restarting) + notifyCallback(Restarting()) closeChannel } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala index 72a897dccf..07c6f14954 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala @@ -72,7 +72,7 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio log.info("Successfully (re)connected to AMQP Server %s:%s [%s]", host, port, self.id) log.debug("Sending new channel to %d already linked actors", self.linkedActorsAsList.size) self.linkedActorsAsList.foreach(_ ! conn.createChannel) - notifyCallback(Connected) + notifyCallback(Connected()) } } catch { case e: Exception => @@ -81,7 +81,7 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio , connectionParameters.initReconnectDelay, self.id) reconnectionTimer.schedule(new TimerTask() { override def run = { - notifyCallback(Reconnecting) + notifyCallback(Reconnecting()) self ! Connect } }, connectionParameters.initReconnectDelay) @@ -92,7 +92,7 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio try { connection.foreach(_.close) log.debug("Disconnected AMQP connection at %s:%s [%s]", host, port, self.id) - notifyCallback(Disconnected) + notifyCallback(Disconnected()) } catch { case e: IOException => log.error("Could not close AMQP connection %s:%s [%s]", host, port, self.id) case _ => () @@ -114,7 +114,7 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio override def preRestart(reason: Throwable) = disconnect override def postRestart(reason: Throwable) = { - notifyCallback(Reconnecting) + notifyCallback(Reconnecting()) connect } } From a43fcb0934ffeed5ed9ae1e86e1ddb6a06ee621b Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 8 Oct 2010 13:40:13 +0200 Subject: [PATCH 22/32] -1 volatile field in ActorRef, trapExit is migrated into faultHandler --- .../src/main/scala/actor/ActorRef.scala | 103 +++++------------- .../src/main/scala/actor/Supervisor.scala | 33 +++--- .../main/scala/config/SupervisionConfig.scala | 45 ++++++-- .../supervisor/RestartStrategySpec.scala | 6 +- .../supervisor/SupervisorHierarchySpec.scala | 6 +- .../actor/supervisor/SupervisorSpec.scala | 3 +- .../se/scalablesolutions/akka/amqp/AMQP.scala | 3 +- .../amqp/FaultTolerantConnectionActor.scala | 4 +- .../src/main/scala/ChatServer.scala | 4 +- .../src/main/scala/actor/TypedActor.scala | 19 +--- .../typed-actor/TypedActorLifecycleSpec.scala | 2 +- 11 files changed, 87 insertions(+), 141 deletions(-) diff --git a/akka-actor/src/main/scala/actor/ActorRef.scala b/akka-actor/src/main/scala/actor/ActorRef.scala index 821e699965..d7a1578379 100644 --- a/akka-actor/src/main/scala/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/actor/ActorRef.scala @@ -6,7 +6,6 @@ package se.scalablesolutions.akka.actor import se.scalablesolutions.akka.dispatch._ import se.scalablesolutions.akka.config.Config._ -import se.scalablesolutions.akka.config.{ AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy } import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.stm.global._ import se.scalablesolutions.akka.stm.TransactionManagement._ @@ -27,6 +26,7 @@ import java.util.{ Map => JMap } import java.lang.reflect.Field import scala.reflect.BeanProperty +import se.scalablesolutions.akka.config.{NoFaultHandlingStrategy, AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy} object ActorRefStatus { /** LifeCycles for ActorRefs @@ -126,39 +126,19 @@ trait ActorRef extends ActorRefShared with TransactionManagement with Logging wi /** * 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 all exceptions: - *

-   * getContext().setTrapExit(new Class[]{Throwable.class});
-   * 
- * - * Trap specific exceptions only: - *
-   * getContext().setTrapExit(new Class[]{MyApplicationException.class, MyApplicationError.class});
-   * 
- */ - def setTrapExit(exceptions: Array[Class[_ <: Throwable]]) = trapExit = exceptions.toList - def getTrapExit(): Array[Class[_ <: Throwable]] = trapExit.toArray - - /** - * Akka Java API - * If 'trapExit' is set for the actor to act as supervisor, then a 'faultHandler' must be defined. + * A faultHandler defines what should be done when a linked actor signals an error. *

* Can be one of: *

-   * getContext().setFaultHandler(new AllForOneStrategy(maxNrOfRetries, withinTimeRange));
+   * getContext().setFaultHandler(new AllForOneStrategy(new Class[]{Throwable.class},maxNrOfRetries, withinTimeRange));
    * 
* Or: *
-   * getContext().setFaultHandler(new OneForOneStrategy(maxNrOfRetries, withinTimeRange));
+   * getContext().setFaultHandler(new OneForOneStrategy(new Class[]{Throwable.class},maxNrOfRetries, withinTimeRange));
    * 
*/ - def setFaultHandler(handler: FaultHandlingStrategy) = this.faultHandler = Some(handler) - def getFaultHandler(): Option[FaultHandlingStrategy] = faultHandler + def setFaultHandler(handler: FaultHandlingStrategy) + def getFaultHandler(): FaultHandlingStrategy @volatile private[akka] var _dispatcher: MessageDispatcher = Dispatchers.defaultGlobalDispatcher @@ -520,7 +500,7 @@ trait ActorRef extends ActorRefShared with TransactionManagement with Logging wi * Links an other actor to this actor. Links are unidirectional and means that a the linking actor will * receive a notification if the linked actor has crashed. *

- * If the 'trapExit' member field has been set to at contain at least one exception class then it will + * If the 'trapExit' member field of the 'faultHandler' 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'. */ @@ -845,7 +825,7 @@ class LocalActorRef private[akka] ( * Links an other actor to this actor. Links are unidirectional and means that a the linking actor will * receive a notification if the linked actor has crashed. *

- * If the 'trapExit' member field has been set to at contain at least one exception class then it will + * If the 'trapExit' member field of the 'faultHandler' 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'. *

@@ -1034,20 +1014,19 @@ class LocalActorRef private[akka] ( } protected[akka] def handleTrapExit(dead: ActorRef, reason: Throwable): Unit = { - if (trapExit.exists(_.isAssignableFrom(reason.getClass))) { + if (faultHandler.trapExit.exists(_.isAssignableFrom(reason.getClass))) { faultHandler match { - case Some(AllForOneStrategy(maxNrOfRetries, withinTimeRange)) => + case AllForOneStrategy(_,maxNrOfRetries, withinTimeRange) => restartLinkedActors(reason, maxNrOfRetries, withinTimeRange) - case Some(OneForOneStrategy(maxNrOfRetries, withinTimeRange)) => + case OneForOneStrategy(_,maxNrOfRetries, withinTimeRange) => dead.restart(reason, maxNrOfRetries, withinTimeRange) - case None => throw new IllegalActorStateException( - "No 'faultHandler' defined for an actor with the 'trapExit' member field defined " + - "\n\tto non-empty list of exception classes - can't proceed " + toString) + case NoFaultHandlingStrategy => + notifySupervisorWithMessage(Exit(this, reason)) //This shouldn't happen } } else { - notifySupervisorWithMessage(Exit(this, reason)) // if 'trapExit' is not defined then pass the Exit on + notifySupervisorWithMessage(Exit(this, reason)) // if 'trapExit' isn't triggered then pass the Exit on } } @@ -1360,7 +1339,7 @@ object RemoteActorSystemMessage { */ private[akka] case class RemoteActorRef private[akka] ( classOrServiceName: String, - val className: String, + val actorClassName: String, val hostname: String, val port: Int, _timeout: Long, @@ -1374,7 +1353,6 @@ private[akka] case class RemoteActorRef private[akka] ( timeout = _timeout start - lazy val remoteClient = RemoteClientModule.clientFor(hostname, port, loader) def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit = RemoteClientModule.send[Any]( @@ -1391,21 +1369,18 @@ private[akka] case class RemoteActorRef private[akka] ( else throw new IllegalActorStateException("Expected a future from remote call to actor " + toString) } - def start: ActorRef = { + def start: ActorRef = synchronized { _status = ActorRefStatus.RUNNING this } - def stop: Unit = { - _status = ActorRefStatus.SHUTDOWN - postMessageToMailbox(RemoteActorSystemMessage.Stop, None) + def stop: Unit = synchronized { + if (_status != ActorRefStatus.SHUTDOWN) { + _status = ActorRefStatus.SHUTDOWN + postMessageToMailbox(RemoteActorSystemMessage.Stop, None) + } } - /** - * Returns the class name for the Actor instance that is managed by the ActorRef. - */ - def actorClassName: String = className - protected[akka] def registerSupervisorAsRemoteActor: Option[Uuid] = None val remoteAddress: Option[InetSocketAddress] = Some(new InetSocketAddress(hostname, port)) @@ -1496,47 +1471,21 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef => /** * 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. + * Don't forget to supply a List of exception types to intercept (trapExit) *

* Can be one of: *

-   *  faultHandler = Some(AllForOneStrategy(maxNrOfRetries, withinTimeRange))
+   *  faultHandler = AllForOneStrategy(trapExit = List(classOf[Exception]),maxNrOfRetries, withinTimeRange)
    * 
* Or: *
-   *  faultHandler = Some(OneForOneStrategy(maxNrOfRetries, withinTimeRange))
+   *  faultHandler = OneForOneStrategy(trapExit = List(classOf[Exception]),maxNrOfRetries, withinTimeRange)
    * 
*/ @volatile - var faultHandler: Option[FaultHandlingStrategy] = None + @BeanProperty + var faultHandler: FaultHandlingStrategy = NoFaultHandlingStrategy /** * The reference sender Actor of the last received message. diff --git a/akka-actor/src/main/scala/actor/Supervisor.scala b/akka-actor/src/main/scala/actor/Supervisor.scala index 28d883e5ea..ba559e6945 100644 --- a/akka-actor/src/main/scala/actor/Supervisor.scala +++ b/akka-actor/src/main/scala/actor/Supervisor.scala @@ -79,14 +79,14 @@ object Supervisor { object SupervisorFactory { def apply(config: SupervisorConfig) = new SupervisorFactory(config) - private[akka] def retrieveFaultHandlerAndTrapExitsFrom(config: SupervisorConfig): - Tuple2[FaultHandlingStrategy, List[Class[_ <: Throwable]]] = config match { - case SupervisorConfig(RestartStrategy(scheme, maxNrOfRetries, timeRange, trapExceptions), _) => - scheme match { - case AllForOne => (AllForOneStrategy(maxNrOfRetries, timeRange), trapExceptions) - case OneForOne => (OneForOneStrategy(maxNrOfRetries, timeRange), trapExceptions) - } - } + private[akka] def retrieveFaultHandlerAndTrapExitsFrom(config: SupervisorConfig): FaultHandlingStrategy = + config match { + case SupervisorConfig(RestartStrategy(scheme, maxNrOfRetries, timeRange, trapExceptions), _) => + scheme match { + case AllForOne => AllForOneStrategy(trapExceptions,maxNrOfRetries, timeRange) + case OneForOne => OneForOneStrategy(trapExceptions,maxNrOfRetries, timeRange) + } + } } /** @@ -99,9 +99,8 @@ class SupervisorFactory private[akka] (val config: SupervisorConfig) extends Log def newInstance: Supervisor = newInstanceFor(config) - def newInstanceFor(config: SupervisorConfig): Supervisor = { - val (handler, trapExits) = SupervisorFactory.retrieveFaultHandlerAndTrapExitsFrom(config) - val supervisor = new Supervisor(handler, trapExits) + def newInstanceFor(config: SupervisorConfig): Supervisor = { + val supervisor = new Supervisor(SupervisorFactory.retrieveFaultHandlerAndTrapExitsFrom(config)) supervisor.configure(config) supervisor.start supervisor @@ -121,13 +120,13 @@ class SupervisorFactory private[akka] (val config: SupervisorConfig) extends Log * @author Jonas Bonér */ sealed class Supervisor private[akka] ( - handler: FaultHandlingStrategy, trapExceptions: List[Class[_ <: Throwable]]) { + handler: FaultHandlingStrategy) { import Supervisor._ private val _childActors = new ConcurrentHashMap[String, List[ActorRef]] private val _childSupervisors = new CopyOnWriteArrayList[Supervisor] - private[akka] val supervisor = actorOf(new SupervisorActor(handler, trapExceptions)).start + private[akka] val supervisor = actorOf(new SupervisorActor(handler)).start def uuid = supervisor.uuid @@ -179,13 +178,9 @@ sealed class Supervisor private[akka] ( * * @author Jonas Bonér */ -final class SupervisorActor private[akka] ( - handler: FaultHandlingStrategy, - trapExceptions: List[Class[_ <: Throwable]]) extends Actor { +final class SupervisorActor private[akka] (handler: FaultHandlingStrategy) extends Actor { import self._ - - trapExit = trapExceptions - faultHandler = Some(handler) + faultHandler = handler override def postStop(): Unit = shutdownLinkedActors diff --git a/akka-actor/src/main/scala/config/SupervisionConfig.scala b/akka-actor/src/main/scala/config/SupervisionConfig.scala index 43a5e21395..12202f5d9d 100644 --- a/akka-actor/src/main/scala/config/SupervisionConfig.scala +++ b/akka-actor/src/main/scala/config/SupervisionConfig.scala @@ -7,20 +7,45 @@ package se.scalablesolutions.akka.config import se.scalablesolutions.akka.actor.{ActorRef} import se.scalablesolutions.akka.dispatch.MessageDispatcher -sealed abstract class FaultHandlingStrategy -object AllForOneStrategy { - def apply(maxNrOfRetries: Int, withinTimeRange: Int): AllForOneStrategy = - AllForOneStrategy(if (maxNrOfRetries < 0) None else Some(maxNrOfRetries), - if (withinTimeRange < 0) None else Some(withinTimeRange)) +sealed abstract class FaultHandlingStrategy { + def trapExit: List[Class[_ <: Throwable]] +} + +object AllForOneStrategy { + def apply(trapExit: List[Class[_ <: Throwable]], maxNrOfRetries: Int, withinTimeRange: Int) = + new AllForOneStrategy(trapExit, if (maxNrOfRetries < 0) None else Some(maxNrOfRetries), if (withinTimeRange < 0) None else Some(withinTimeRange)) + def apply(trapExit: Array[Class[Throwable]], maxNrOfRetries: Int, withinTimeRange: Int) = + new AllForOneStrategy(trapExit.toList,maxNrOfRetries,withinTimeRange) +} + +case class AllForOneStrategy(trapExit: List[Class[_ <: Throwable]], + maxNrOfRetries: Option[Int] = None, + withinTimeRange: Option[Int] = None) extends FaultHandlingStrategy { + def this(trapExit: List[Class[_ <: Throwable]],maxNrOfRetries: Int, withinTimeRange: Int) = + this(trapExit, if (maxNrOfRetries < 0) None else Some(maxNrOfRetries), if (withinTimeRange < 0) None else Some(withinTimeRange)) + def this(trapExit: Array[Class[Throwable]],maxNrOfRetries: Int, withinTimeRange: Int) = + this(trapExit.toList,maxNrOfRetries,withinTimeRange) } -case class AllForOneStrategy(maxNrOfRetries: Option[Int] = None, withinTimeRange: Option[Int] = None) extends FaultHandlingStrategy object OneForOneStrategy { - def apply(maxNrOfRetries: Int, withinTimeRange: Int): OneForOneStrategy = - this(if (maxNrOfRetries < 0) None else Some(maxNrOfRetries), - if (withinTimeRange < 0) None else Some(withinTimeRange)) + def apply(trapExit: List[Class[_ <: Throwable]], maxNrOfRetries: Int, withinTimeRange: Int) = + new OneForOneStrategy(trapExit, if (maxNrOfRetries < 0) None else Some(maxNrOfRetries), if (withinTimeRange < 0) None else Some(withinTimeRange)) + def apply(trapExit: Array[Class[Throwable]], maxNrOfRetries: Int, withinTimeRange: Int) = + new OneForOneStrategy(trapExit.toList,maxNrOfRetries,withinTimeRange) +} + +case class OneForOneStrategy(trapExit: List[Class[_ <: Throwable]], + maxNrOfRetries: Option[Int] = None, + withinTimeRange: Option[Int] = None) extends FaultHandlingStrategy { + def this(trapExit: List[Class[_ <: Throwable]],maxNrOfRetries: Int, withinTimeRange: Int) = + this(trapExit, if (maxNrOfRetries < 0) None else Some(maxNrOfRetries), if (withinTimeRange < 0) None else Some(withinTimeRange)) + def this(trapExit: Array[Class[Throwable]],maxNrOfRetries: Int, withinTimeRange: Int) = + this(trapExit.toList,maxNrOfRetries,withinTimeRange) +} + +case object NoFaultHandlingStrategy extends FaultHandlingStrategy { + def trapExit: List[Class[_ <: Throwable]] = Nil } -case class OneForOneStrategy(maxNrOfRetries: Option[Int] = None, withinTimeRange: Option[Int] = None) extends FaultHandlingStrategy /** * Configuration classes - not to be used as messages. diff --git a/akka-actor/src/test/scala/actor/supervisor/RestartStrategySpec.scala b/akka-actor/src/test/scala/actor/supervisor/RestartStrategySpec.scala index b9fa238963..7dd7545d34 100644 --- a/akka-actor/src/test/scala/actor/supervisor/RestartStrategySpec.scala +++ b/akka-actor/src/test/scala/actor/supervisor/RestartStrategySpec.scala @@ -22,8 +22,7 @@ class RestartStrategySpec extends JUnitSuite { def slaveShouldStayDeadAfterMaxRestarts = { val boss = actorOf(new Actor{ - self.trapExit = List(classOf[Throwable]) - self.faultHandler = Some(OneForOneStrategy(1, 1000)) + self.faultHandler = OneForOneStrategy(List(classOf[Throwable]), 1, 1000) protected def receive = { case _ => () } }).start @@ -75,8 +74,7 @@ class RestartStrategySpec extends JUnitSuite { def slaveShouldBeImmortalWithoutMaxRestarts = { val boss = actorOf(new Actor{ - self.trapExit = List(classOf[Throwable]) - self.faultHandler = Some(OneForOneStrategy(None, None)) + self.faultHandler = OneForOneStrategy(List(classOf[Throwable]), None, None) protected def receive = { case _ => () } }).start diff --git a/akka-actor/src/test/scala/actor/supervisor/SupervisorHierarchySpec.scala b/akka-actor/src/test/scala/actor/supervisor/SupervisorHierarchySpec.scala index ffc9dbd860..b1f8af27c0 100644 --- a/akka-actor/src/test/scala/actor/supervisor/SupervisorHierarchySpec.scala +++ b/akka-actor/src/test/scala/actor/supervisor/SupervisorHierarchySpec.scala @@ -37,8 +37,7 @@ class SupervisorHierarchySpec extends JUnitSuite { val workerThree = actorOf(new CountDownActor(countDown)) val boss = actorOf(new Actor{ - self.trapExit = List(classOf[Throwable]) - self.faultHandler = Some(OneForOneStrategy(5, 1000)) + self.faultHandler = OneForOneStrategy(List(classOf[Throwable]), 5, 1000) protected def receive = { case _ => () } }).start @@ -63,8 +62,7 @@ class SupervisorHierarchySpec extends JUnitSuite { val countDown = new CountDownLatch(2) val crasher = actorOf(new CountDownActor(countDown)) val boss = actorOf(new Actor{ - self.trapExit = List(classOf[Throwable]) - self.faultHandler = Some(OneForOneStrategy(1, 5000)) + self.faultHandler = OneForOneStrategy(List(classOf[Throwable]), 1, 5000) protected def receive = { case MaximumNumberOfRestartsWithinTimeRangeReached(_, _, _, _) => countDown.countDown diff --git a/akka-actor/src/test/scala/actor/supervisor/SupervisorSpec.scala b/akka-actor/src/test/scala/actor/supervisor/SupervisorSpec.scala index f7d1752ded..d7390a0d43 100644 --- a/akka-actor/src/test/scala/actor/supervisor/SupervisorSpec.scala +++ b/akka-actor/src/test/scala/actor/supervisor/SupervisorSpec.scala @@ -95,8 +95,7 @@ object SupervisorSpec { } class Master extends Actor { - self.trapExit = classOf[Exception] :: Nil - self.faultHandler = Some(OneForOneStrategy(5, 1000)) + self.faultHandler = OneForOneStrategy(List(classOf[Exception]), 5, 1000) val temp = self.spawnLink[TemporaryActor] override def receive = { case Die => temp !! (Die, 5000) diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala index cd73d27e03..09cfd47add 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala @@ -184,8 +184,7 @@ object AMQP { class AMQPSupervisorActor extends Actor { import self._ - faultHandler = Some(OneForOneStrategy(5, 5000)) - trapExit = List(classOf[Throwable]) + faultHandler = OneForOneStrategy(List(classOf[Throwable]),5, 5000) def receive = { case _ => {} // ignore all messages diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala index bf5a192299..2bb821d5eb 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala @@ -17,9 +17,7 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio self.id = "amqp-connection-%s".format(host) self.lifeCycle = Permanent - - self.trapExit = List(classOf[Throwable]) - self.faultHandler = Some(OneForOneStrategy(5, 5000)) + self.faultHandler = OneForOneStrategy(List(classOf[Throwable]),5, 5000) val reconnectionTimer = new Timer("%s-timer".format(self.id)) diff --git a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala index e5b60b364f..b65f833763 100644 --- a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala +++ b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala @@ -170,9 +170,7 @@ trait RedisChatStorageFactory { this: Actor => * Chat server. Manages sessions and redirects all other messages to the Session for the client. */ trait ChatServer extends Actor { - self.faultHandler = Some(OneForOneStrategy(5, 5000)) - self.trapExit = List(classOf[Exception]) - + self.faultHandler = OneForOneStrategy(List(classOf[Exception]),5, 5000) val storage: ActorRef log.info("Chat server is starting up...") diff --git a/akka-typed-actor/src/main/scala/actor/TypedActor.scala b/akka-typed-actor/src/main/scala/actor/TypedActor.scala index 48e0d5a405..8b9cc2034a 100644 --- a/akka-typed-actor/src/main/scala/actor/TypedActor.scala +++ b/akka-typed-actor/src/main/scala/actor/TypedActor.scala @@ -665,13 +665,12 @@ object TypedActor extends Logging { * @param trapExceptions array of exceptions that should be handled by the supervisor */ def link(supervisor: AnyRef, supervised: AnyRef, - handler: FaultHandlingStrategy, trapExceptions: Array[Class[_ <: Throwable]]) = { + handler: FaultHandlingStrategy) = { 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.faultHandler = handler supervisorActor.link(supervisedActor) } @@ -688,18 +687,6 @@ object TypedActor extends Logging { 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 @@ -708,7 +695,7 @@ object TypedActor extends Logging { 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) + supervisorActor.faultHandler = handler this } diff --git a/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorLifecycleSpec.scala b/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorLifecycleSpec.scala index 781537d5a9..f2903adf03 100644 --- a/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorLifecycleSpec.scala +++ b/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorLifecycleSpec.scala @@ -87,7 +87,7 @@ class TypedActorLifecycleSpec extends Spec with ShouldMatchers with BeforeAndAft SamplePojoImpl.reset val pojo = TypedActor.newInstance(classOf[SimpleJavaPojo], classOf[SimpleJavaPojoImpl]) val supervisor = TypedActor.newInstance(classOf[SimpleJavaPojo], classOf[SimpleJavaPojoImpl]) - link(supervisor, pojo, OneForOneStrategy(3, 2000), Array(classOf[Throwable])) + link(supervisor, pojo, OneForOneStrategy(Array(classOf[Throwable]), 3, 2000)) pojo.throwException Thread.sleep(500) SimpleJavaPojoImpl._pre should be(true) From 997612119c1de3d641141c91024e358dad43c208 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 8 Oct 2010 14:59:24 +0200 Subject: [PATCH 23/32] Changed != SHUTDOWN to == RUNNING --- akka-actor/src/main/scala/actor/ActorRef.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/actor/ActorRef.scala b/akka-actor/src/main/scala/actor/ActorRef.scala index d7a1578379..208f317362 100644 --- a/akka-actor/src/main/scala/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/actor/ActorRef.scala @@ -1375,7 +1375,7 @@ private[akka] case class RemoteActorRef private[akka] ( } def stop: Unit = synchronized { - if (_status != ActorRefStatus.SHUTDOWN) { + if (_status == ActorRefStatus.RUNNING) { _status = ActorRefStatus.SHUTDOWN postMessageToMailbox(RemoteActorSystemMessage.Stop, None) } From 90831a9767e7bfe8eb9bf090ea985d98d9b3a203 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 8 Oct 2010 15:43:00 +0200 Subject: [PATCH 24/32] Removing linkedActorsAsList, switching _linkedActors to be a volatile lazy val instead of Option with lazy semantics --- .../src/main/scala/actor/ActorRef.scala | 28 +++++++------------ .../src/main/scala/actor/ActorRegistry.scala | 11 ++------ .../amqp/FaultTolerantConnectionActor.scala | 7 +++-- .../src/main/scala/remote/RemoteServer.scala | 3 +- 4 files changed, 18 insertions(+), 31 deletions(-) diff --git a/akka-actor/src/main/scala/actor/ActorRef.scala b/akka-actor/src/main/scala/actor/ActorRef.scala index 208f317362..eefcccced6 100644 --- a/akka-actor/src/main/scala/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/actor/ActorRef.scala @@ -600,8 +600,6 @@ trait ActorRef extends ActorRefShared with TransactionManagement with Logging wi protected[akka] def linkedActors: JMap[Uuid, ActorRef] - protected[akka] def linkedActorsAsList: List[ActorRef] - override def hashCode: Int = HashCode.hash(HashCode.SEED, uuid) override def equals(that: Any): Boolean = { @@ -640,7 +638,7 @@ class LocalActorRef private[akka] ( @volatile private[akka] var _remoteAddress: Option[InetSocketAddress] = None // only mutable to maintain identity across nodes @volatile - private[akka] var _linkedActors: Option[ConcurrentHashMap[Uuid, ActorRef]] = None + private[akka] lazy val _linkedActors = new ConcurrentHashMap[Uuid, ActorRef] @volatile private[akka] var _supervisor: Option[ActorRef] = None @volatile @@ -944,9 +942,12 @@ class LocalActorRef private[akka] ( /** * Shuts down and removes all linked actors. */ - def shutdownLinkedActors(): Unit = { - linkedActorsAsList.foreach(_.stop) - linkedActors.clear + def shutdownLinkedActors() { + val i = linkedActors.values.iterator + while(i.hasNext) { + i.next.stop + i.remove + } } /** @@ -1082,7 +1083,8 @@ class LocalActorRef private[akka] ( } protected[akka] def restartLinkedActors(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]) = { - linkedActorsAsList.foreach { actorRef => + import scala.collection.JavaConversions._ + linkedActors.values foreach { actorRef => actorRef.lifeCycle match { // either permanent or none where default is permanent case Temporary => shutDownTemporaryActor(actorRef) @@ -1099,16 +1101,7 @@ class LocalActorRef private[akka] ( } else None } - protected[akka] def linkedActors: JMap[Uuid, ActorRef] = guard.withGuard { - if (_linkedActors.isEmpty) { - val actors = new ConcurrentHashMap[Uuid, ActorRef] - _linkedActors = Some(actors) - actors - } else _linkedActors.get - } - - protected[akka] def linkedActorsAsList: List[ActorRef] = - linkedActors.values.toArray.toList.asInstanceOf[List[ActorRef]] + protected[akka] def linkedActors: JMap[Uuid, ActorRef] = _linkedActors // ========= PRIVATE FUNCTIONS ========= @@ -1411,7 +1404,6 @@ private[akka] case class RemoteActorRef private[akka] ( protected[akka] def restart(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]): Unit = unsupported protected[akka] def restartLinkedActors(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]): Unit = unsupported protected[akka] def linkedActors: JMap[Uuid, ActorRef] = unsupported - protected[akka] def linkedActorsAsList: List[ActorRef] = unsupported protected[akka] def invoke(messageHandle: MessageInvocation): Unit = unsupported protected[akka] def remoteAddress_=(addr: Option[InetSocketAddress]): Unit = unsupported protected[akka] def supervisor_=(sup: Option[ActorRef]): Unit = unsupported diff --git a/akka-actor/src/main/scala/actor/ActorRegistry.scala b/akka-actor/src/main/scala/actor/ActorRegistry.scala index 40f480c86e..41bff91132 100644 --- a/akka-actor/src/main/scala/actor/ActorRegistry.scala +++ b/akka-actor/src/main/scala/actor/ActorRegistry.scala @@ -187,7 +187,7 @@ object ActorRegistry extends ListenerManagement { def typedActorFor[T <: AnyRef](implicit manifest: Manifest[T]): Option[AnyRef] = { TypedActorModule.ensureTypedActorEnabled def predicate(proxy: AnyRef) : Boolean = { - val actorRef = actorFor(proxy) + val actorRef = TypedActorModule.typedActorObjectInstance.get.actorFor(proxy) actorRef.isDefined && manifest.erasure.isAssignableFrom(actorRef.get.actor.getClass) } findTypedActor({ case a:AnyRef if predicate(a) => a }) @@ -199,7 +199,7 @@ object ActorRegistry extends ListenerManagement { def typedActorsFor[T <: AnyRef](clazz: Class[T]): Array[AnyRef] = { TypedActorModule.ensureTypedActorEnabled def predicate(proxy: AnyRef) : Boolean = { - val actorRef = actorFor(proxy) + val actorRef = TypedActorModule.typedActorObjectInstance.get.actorFor(proxy) actorRef.isDefined && clazz.isAssignableFrom(actorRef.get.actor.getClass) } filterTypedActors(predicate) @@ -233,13 +233,6 @@ object ActorRegistry extends ListenerManagement { TypedActorModule.typedActorObjectInstance.get.proxyFor(actorRef) } - /** - * Get the underlying typed actor for a given proxy. - */ - private def actorFor(proxy: AnyRef): Option[ActorRef] = { - TypedActorModule.typedActorObjectInstance.get.actorFor(proxy) - } - /** * Registers an actor in the ActorRegistry. diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala index 2bb821d5eb..77df7f887e 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala @@ -8,9 +8,9 @@ import java.util.{TimerTask, Timer} import java.io.IOException import com.rabbitmq.client._ import se.scalablesolutions.akka.amqp.AMQP.ConnectionParameters -import se.scalablesolutions.akka.actor.{Exit, Actor} import se.scalablesolutions.akka.config.ScalaConfig.{Permanent, LifeCycle} import se.scalablesolutions.akka.config.OneForOneStrategy +import se.scalablesolutions.akka.actor.{ActorRef, Exit, Actor} private[amqp] class FaultTolerantConnectionActor(connectionParameters: ConnectionParameters) extends Actor { import connectionParameters._ @@ -68,8 +68,9 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio } }) log.info("Successfully (re)connected to AMQP Server %s:%s [%s]", host, port, self.id) - log.debug("Sending new channel to %d already linked actors", self.linkedActorsAsList.size) - self.linkedActorsAsList.foreach(_ ! conn.createChannel) + log.debug("Sending new channel to %d already linked actors", self.linkedActors.size) + import scala.collection.JavaConversions._ + self.linkedActors.values.iterator.foreach(_ ! conn.createChannel) notifyCallback(Connected) } } catch { diff --git a/akka-remote/src/main/scala/remote/RemoteServer.scala b/akka-remote/src/main/scala/remote/RemoteServer.scala index 40b312f879..deb3c05b87 100644 --- a/akka-remote/src/main/scala/remote/RemoteServer.scala +++ b/akka-remote/src/main/scala/remote/RemoteServer.scala @@ -66,7 +66,8 @@ object RemoteNode extends RemoteServer * * @author Jonas Bonér */ -object RemoteServer { +object +RemoteServer { val UUID_PREFIX = "uuid:" val HOSTNAME = config.getString("akka.remote.server.hostname", "localhost") val PORT = config.getInt("akka.remote.server.port", 9999) From 0831d124dd4a99c5892bf37f5e965bfd1389835d Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 8 Oct 2010 15:59:10 +0200 Subject: [PATCH 25/32] Removing isInInitialization, reading that from actorRefInCreation, -1 volatile field per actorref --- akka-actor/src/main/scala/actor/ActorRef.scala | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/akka-actor/src/main/scala/actor/ActorRef.scala b/akka-actor/src/main/scala/actor/ActorRef.scala index eefcccced6..4382d78d5c 100644 --- a/akka-actor/src/main/scala/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/actor/ActorRef.scala @@ -642,8 +642,6 @@ class LocalActorRef private[akka] ( @volatile private[akka] var _supervisor: Option[ActorRef] = None @volatile - private var isInInitialization = false - @volatile private var maxNrOfRetriesCount: Int = 0 @volatile private var restartsWithinTimeRangeTimestamp: Long = 0L @@ -791,7 +789,8 @@ class LocalActorRef private[akka] ( } _status = ActorRefStatus.RUNNING - if (!isInInitialization) initializeActorInstance + //If actorRefInCreation is empty, we're outside creation of the actor, and so we can initialize the actor instance. + if (Actor.actorRefInCreation.value.isEmpty) initializeActorInstance checkReceiveTimeout //Schedule the initial Receive timeout } @@ -1128,7 +1127,6 @@ class LocalActorRef private[akka] ( private[this] def newActor: Actor = { Actor.actorRefInCreation.withValue(Some(this)) { - isInInitialization = true val actor = actorFactory match { case Left(Some(clazz)) => import ReflectiveAccess.{ createInstance, noParams, noArgs } @@ -1146,7 +1144,6 @@ class LocalActorRef private[akka] ( } if (actor eq null) throw new ActorInitializationException( "Actor instance passed to ActorRef can not be 'null'") - isInInitialization = false actor } } From 69b856e69ddef0868ecebede6298bbbcd0f2dc87 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 8 Oct 2010 16:25:53 +0200 Subject: [PATCH 26/32] Serialization of RemoteActorRef unborked --- .../src/main/scala/serialization/SerializationProtocol.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-remote/src/main/scala/serialization/SerializationProtocol.scala b/akka-remote/src/main/scala/serialization/SerializationProtocol.scala index 847911c43f..f4a1c945ba 100644 --- a/akka-remote/src/main/scala/serialization/SerializationProtocol.scala +++ b/akka-remote/src/main/scala/serialization/SerializationProtocol.scala @@ -243,13 +243,13 @@ object RemoteActorSerialization { val host = homeAddress.getHostName val port = homeAddress.getPort - Actor.log.debug("Register serialized Actor [%s] as remote @ [%s:%s]", actorClass.getName, host, port) + Actor.log.debug("Register serialized Actor [%s] as remote @ [%s:%s]", actorClassName, host, port) RemoteServer.getOrCreateServer(homeAddress) ActorRegistry.registerActorByUuid(homeAddress, uuid.toString, ar) RemoteActorRefProtocol.newBuilder .setClassOrServiceName(uuid.toString) - .setActorClassname(actorClass.getName) + .setActorClassname(actorClassName) .setHomeAddress(AddressProtocol.newBuilder.setHostname(host).setPort(port).build) .setTimeout(timeout) .build From 31e74bfa5323c17e823c151d4295410a04e0bb5e Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 8 Oct 2010 17:40:53 +0200 Subject: [PATCH 27/32] - Finshed up java api for RPC - Made case objects 'java compatible' via getInstance function - Added RPC examples in java examplesession --- .../akka/amqp/ExampleSessionJava.java | 89 +++++++- .../se/scalablesolutions/akka/amqp/AMQP.scala | 12 +- .../akka/amqp/AMQPMessage.scala | 24 +- .../akka/amqp/ExampleSession.scala | 11 +- .../akka/amqp/ExchangeType.scala | 28 +-- .../akka/amqp/FaultTolerantChannelActor.scala | 10 +- .../amqp/FaultTolerantConnectionActor.scala | 8 +- .../scalablesolutions/akka/amqp/rpc/RPC.scala | 205 ++++++++++++++++-- .../AMQPConsumerMessageTestIntegration.scala | 45 ++-- ...tobufProducerConsumerTestIntegration.scala | 2 +- .../AMQPRpcClientServerTestIntegration.scala | 2 +- .../test/AMQPRpcStringTestIntegration.scala | 2 +- ...tringProducerConsumerTestIntegration.scala | 4 +- .../akka/amqp/test/AMQPTest.scala | 10 +- 14 files changed, 348 insertions(+), 104 deletions(-) diff --git a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java index 6dd8c3dac3..19b12d4c64 100644 --- a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java +++ b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java @@ -1,16 +1,22 @@ package se.scalablesolutions.akka.amqp; +import org.multiverse.api.latches.StandardLatch; +import scala.Option; import se.scalablesolutions.akka.actor.ActorRef; import se.scalablesolutions.akka.actor.ActorRegistry; import se.scalablesolutions.akka.actor.UntypedActor; import se.scalablesolutions.akka.actor.UntypedActorFactory; +import se.scalablesolutions.akka.amqp.rpc.RPC; import se.scalablesolutions.akka.remote.protocol.RemoteProtocol; + +import se.scalablesolutions.akka.util.Function; import se.scalablesolutions.akka.util.Procedure; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +@SuppressWarnings({"unchecked"}) public class ExampleSessionJava { public static void main(String... args) { @@ -30,6 +36,11 @@ public class ExampleSessionJava { printTopic("EASY PROTOBUF PRODUCER AND CONSUMER"); easyProtobufProducerConsumer(); + printTopic("EASY STRING RPC"); + easyStringRpc(); + + printTopic("EASY PROTOBUF RPC"); + easyProtobufRpc(); // postStop everything the amqp tree except the main AMQP supervisor // all connections/consumers/producers will be stopped @@ -57,7 +68,7 @@ public class ExampleSessionJava { // defaults to amqp://guest:guest@localhost:5672/ ActorRef connection = AMQP.newConnection(); - AMQP.ExchangeParameters exchangeParameters = new AMQP.ExchangeParameters("my_direct_exchange", new ExchangeType.Direct()); + AMQP.ExchangeParameters exchangeParameters = new AMQP.ExchangeParameters("my_direct_exchange", Direct.getInstance()); ActorRef deliveryHandler = UntypedActor.actorOf(DirectDeliveryHandlerActor.class); @@ -86,7 +97,7 @@ public class ExampleSessionJava { }); channelCallback.start(); - AMQP.ExchangeParameters exchangeParameters = new AMQP.ExchangeParameters("my_callback_exchange", new ExchangeType.Direct()); + AMQP.ExchangeParameters exchangeParameters = new AMQP.ExchangeParameters("my_callback_exchange", Direct.getInstance()); AMQP.ChannelParameters channelParameters = new AMQP.ChannelParameters(channelCallback); ActorRef dummyHandler = UntypedActor.actorOf(DummyActor.class); @@ -133,7 +144,7 @@ public class ExampleSessionJava { ActorRef connection = AMQP.newConnection(); String exchangeName = "easy.protobuf"; - + Procedure procedure = new Procedure() { public void apply(RemoteProtocol.AddressProtocol message) { System.out.println("### >> Received message: " + message); @@ -146,6 +157,66 @@ public class ExampleSessionJava { producerClient.send(RemoteProtocol.AddressProtocol.newBuilder().setHostname("akkarocks.com").setPort(1234).build()); } + + public void easyStringRpc() { + + ActorRef connection = AMQP.newConnection(); + + String exchangeName = "easy.stringrpc"; + + // listen by default to: + // exchange = exchangeName + // routingKey = .request + // queueName = .in + RPC.newStringRpcServer(connection, exchangeName, new Function() { + public String apply(String request) { + System.out.println("### >> Got request: " + request); + return "Response to: '" + request + "'"; + } + }); + + // send by default to: + // exchange = exchangeName + // routingKey = .request + RPC.RpcClient stringRpcClient = RPC.newStringRpcClient(connection, exchangeName); + + Option response = stringRpcClient.call("AMQP Rocks!"); + System.out.println("### >> Got response: " + response); + + final StandardLatch standardLatch = new StandardLatch(); + stringRpcClient.callAsync("AMQP is dead easy", new Procedure() { + public void apply(String request) { + System.out.println("### >> This is handled async: " + request); + standardLatch.open(); + } + }); + try { + standardLatch.tryAwait(2, TimeUnit.SECONDS); + } catch (InterruptedException ignore) { + } + } + + + public void easyProtobufRpc() { + + ActorRef connection = AMQP.newConnection(); + + String exchangeName = "easy.protobuf.rpc"; + + RPC.newProtobufRpcServer(connection, exchangeName, new Function() { + public RemoteProtocol.AddressProtocol apply(RemoteProtocol.AddressProtocol request) { + return RemoteProtocol.AddressProtocol.newBuilder().setHostname(request.getHostname()).setPort(request.getPort()).build(); + } + }, RemoteProtocol.AddressProtocol.class); + + RPC.RpcClient protobufRpcClient = + RPC.newProtobufRpcClient(connection, exchangeName, RemoteProtocol.AddressProtocol.class); + + scala.Option response = + protobufRpcClient.call(RemoteProtocol.AddressProtocol.newBuilder().setHostname("localhost").setPort(4321).build()); + + System.out.println("### >> Got response: " + response); + } } class DummyActor extends UntypedActor { @@ -163,11 +234,11 @@ class ChannelCallbackActor extends UntypedActor { } public void onReceive(Object message) throws Exception { - if (Started.class.isAssignableFrom(message.getClass())) { + if (Started.getInstance().getClass().isAssignableFrom(message.getClass())) { System.out.println("### >> Channel callback: Started"); channelCountdown.countDown(); - } else if (Restarting.class.isAssignableFrom(message.getClass())) { - } else if (Stopped.class.isAssignableFrom(message.getClass())) { + } else if (Restarting.getInstance().getClass().isAssignableFrom(message.getClass())) { + } else if (Stopped.getInstance().getClass().isAssignableFrom(message.getClass())) { System.out.println("### >> Channel callback: Stopped"); } else throw new IllegalArgumentException("Unknown message: " + message); } @@ -176,10 +247,10 @@ class ChannelCallbackActor extends UntypedActor { class ConnectionCallbackActor extends UntypedActor { public void onReceive(Object message) throws Exception { - if (Connected.class.isAssignableFrom(message.getClass())) { + if (Connected.getInstance().getClass().isAssignableFrom(message.getClass())) { System.out.println("### >> Connection callback: Connected!"); - } else if (Reconnecting.class.isAssignableFrom(message.getClass())) { - } else if (Disconnected.class.isAssignableFrom(message.getClass())) { + } else if (Reconnecting.getInstance().getClass().isAssignableFrom(message.getClass())) { + } else if (Disconnected.getInstance().getClass().isAssignableFrom(message.getClass())) { System.out.println("### >> Connection callback: Disconnected!"); } else throw new IllegalArgumentException("Unknown message: " + message); } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala index 2373219a30..5a56502de8 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQP.scala @@ -74,8 +74,12 @@ object AMQP { * Declaration type used for either exchange or queue declaration */ sealed trait Declaration - case object NoActionDeclaration extends Declaration - case object PassiveDeclaration extends Declaration + case object NoActionDeclaration extends Declaration { + def getInstance() = this // Needed for Java API usage + } + case object PassiveDeclaration extends Declaration { + def getInstance() = this // Needed for Java API usage + } case class ActiveDeclaration(durable: Boolean = false, autoDelete: Boolean = true, exclusive: Boolean = false) extends Declaration { // Needed for Java API usage @@ -90,13 +94,13 @@ object AMQP { */ case class ExchangeParameters( exchangeName: String, - exchangeType: ExchangeType = ExchangeType.Topic(), + exchangeType: ExchangeType = Topic, exchangeDeclaration: Declaration = ActiveDeclaration(), configurationArguments: Map[String, AnyRef] = Map.empty) { // Needed for Java API usage def this(exchangeName: String) = - this (exchangeName, ExchangeType.Topic(), ActiveDeclaration(), Map.empty) + this (exchangeName, Topic, ActiveDeclaration(), Map.empty) // Needed for Java API usage def this(exchangeName: String, exchangeType: ExchangeType) = diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala index 26fab46799..7f1ef053de 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/AMQPMessage.scala @@ -44,18 +44,30 @@ case class Delivery( // connection messages case object Connect extends AMQPMessage -case class Connected() extends AMQPMessage // Needed for Java API usage -case class Reconnecting() extends AMQPMessage // Needed for Java API usage -case class Disconnected() extends AMQPMessage // Needed for Java API usage +case object Connected extends AMQPMessage { + def getInstance() = this // Needed for Java API usage +} +case object Reconnecting extends AMQPMessage { + def getInstance() = this // Needed for Java API usage +} +case object Disconnected extends AMQPMessage { + def getInstance() = this // Needed for Java API usage +} case object ChannelRequest extends InternalAMQPMessage // channel messages case object Start extends AMQPMessage -case class Started() extends AMQPMessage // Needed for Java API usage -case class Restarting() extends AMQPMessage // Needed for Java API usage -case class Stopped() extends AMQPMessage // Needed for Java API usage +case object Started extends AMQPMessage { + def getInstance() = this // Needed for Java API usage +} +case object Restarting extends AMQPMessage { + def getInstance() = this // Needed for Java API usage +} +case object Stopped extends AMQPMessage { + def getInstance() = this // Needed for Java API usage +} // delivery messages case class Acknowledge(deliveryTag: Long) extends AMQPMessage diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala index b0fc0dd59f..00756aa959 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExampleSession.scala @@ -12,7 +12,6 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} import java.lang.String import se.scalablesolutions.akka.amqp.AMQP._ import se.scalablesolutions.akka.remote.protocol.RemoteProtocol.AddressProtocol -import se.scalablesolutions.akka.amqp.ExchangeType._ object ExampleSession { @@ -68,7 +67,7 @@ object ExampleSession { // defaults to amqp://guest:guest@localhost:5672/ val connection = AMQP.newConnection() - val exchangeParameters = ExchangeParameters("my_direct_exchange", Direct()) + val exchangeParameters = ExchangeParameters("my_direct_exchange", Direct) val consumer = AMQP.newConsumer(connection, ConsumerParameters("some.routing", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) @@ -83,7 +82,7 @@ object ExampleSession { // defaults to amqp://guest:guest@localhost:5672/ val connection = AMQP.newConnection() - val exchangeParameters = ExchangeParameters("my_fanout_exchange", Fanout()) + val exchangeParameters = ExchangeParameters("my_fanout_exchange", Fanout) val bushConsumer = AMQP.newConsumer(connection, ConsumerParameters("@george_bush", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) @@ -102,7 +101,7 @@ object ExampleSession { // defaults to amqp://guest:guest@localhost:5672/ val connection = AMQP.newConnection() - val exchangeParameters = ExchangeParameters("my_topic_exchange", Topic()) + val exchangeParameters = ExchangeParameters("my_topic_exchange", Topic) val bushConsumer = AMQP.newConsumer(connection, ConsumerParameters("@george_bush", actor { case Delivery(payload, _, _, _, _) => log.info("@george_bush received message from: %s", new String(payload)) @@ -136,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_callback_exchange", Direct()) + val exchangeParameters = ExchangeParameters("my_callback_exchange", Direct) val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) val consumer = AMQP.newConsumer(connection, ConsumerParameters("callback.routing", actor { @@ -202,7 +201,7 @@ object ExampleSession { def requestHandler(request: String) = 3 val rpcServer = RPC.newRpcServer[String,Int](connection, exchangeName, "rpc.in.key", rpcServerSerializer, - requestHandler, queueName = Some("rpc.in.key.queue")) + requestHandler _, queueName = Some("rpc.in.key.queue")) /** Client */ diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala index 38a0073c30..2c35a017e4 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/ExchangeType.scala @@ -5,17 +5,19 @@ package se.scalablesolutions.akka.amqp sealed trait ExchangeType -object ExchangeType { - case class Direct() extends ExchangeType { - override def toString = "direct" - } - case class Topic() extends ExchangeType { - override def toString = "topic" - } - case class Fanout() extends ExchangeType { - override def toString = "fanout" - } - case class Match() extends ExchangeType { - override def toString = "match" - } +case object Direct extends ExchangeType { + def getInstance() = this // Needed for Java API usage + override def toString = "direct" +} +case object Topic extends ExchangeType { + def getInstance() = this // Needed for Java API usage + override def toString = "topic" +} +case object Fanout extends ExchangeType { + def getInstance() = this // Needed for Java API usage + override def toString = "fanout" +} +case object Match extends ExchangeType { + def getInstance() = this // Needed for Java API usage + override def toString = "match" } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala index d2333f8b7c..6617c62a44 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantChannelActor.scala @@ -46,14 +46,14 @@ abstract private[amqp] class FaultTolerantChannelActor( } else { // channel error log.error(cause, "%s self restarting because of channel shutdown", toString) - notifyCallback(Restarting()) + notifyCallback(Restarting) self ! Start } } case Failure(cause) => log.error(cause, "%s self restarting because of channel failure", toString) closeChannel - notifyCallback(Restarting()) + notifyCallback(Restarting) self ! Start } @@ -81,7 +81,7 @@ abstract private[amqp] class FaultTolerantChannelActor( setupChannel(ch) channel = Some(ch) - notifyCallback(Started()) + notifyCallback(Started) log.info("Channel setup for %s", toString) } @@ -89,7 +89,7 @@ abstract private[amqp] class FaultTolerantChannelActor( channel.foreach { ch => if (ch.isOpen) ch.close - notifyCallback(Stopped()) + notifyCallback(Stopped) log.info("%s channel closed", toString) } channel = None @@ -100,7 +100,7 @@ abstract private[amqp] class FaultTolerantChannelActor( } override def preRestart(reason: Throwable) = { - notifyCallback(Restarting()) + notifyCallback(Restarting) closeChannel } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala index 07c6f14954..72a897dccf 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala @@ -72,7 +72,7 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio log.info("Successfully (re)connected to AMQP Server %s:%s [%s]", host, port, self.id) log.debug("Sending new channel to %d already linked actors", self.linkedActorsAsList.size) self.linkedActorsAsList.foreach(_ ! conn.createChannel) - notifyCallback(Connected()) + notifyCallback(Connected) } } catch { case e: Exception => @@ -81,7 +81,7 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio , connectionParameters.initReconnectDelay, self.id) reconnectionTimer.schedule(new TimerTask() { override def run = { - notifyCallback(Reconnecting()) + notifyCallback(Reconnecting) self ! Connect } }, connectionParameters.initReconnectDelay) @@ -92,7 +92,7 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio try { connection.foreach(_.close) log.debug("Disconnected AMQP connection at %s:%s [%s]", host, port, self.id) - notifyCallback(Disconnected()) + notifyCallback(Disconnected) } catch { case e: IOException => log.error("Could not close AMQP connection %s:%s [%s]", host, port, self.id) case _ => () @@ -114,7 +114,7 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio override def preRestart(reason: Throwable) = disconnect override def postRestart(reason: Throwable) = { - notifyCallback(Reconnecting()) + notifyCallback(Reconnecting) connect } } diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala index 394315a68b..87800c7742 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala @@ -5,21 +5,80 @@ import com.google.protobuf.Message import se.scalablesolutions.akka.actor.{Actor, ActorRef} import Actor._ import se.scalablesolutions.akka.amqp._ +import se.scalablesolutions.akka.util.Procedure +import reflect.Manifest object RPC { + // Needed for Java API usage + def newRpcClient[O, I](connection: ActorRef, + exchangeName: String, + routingKey: String, + serializer: RpcClientSerializer[O, I]): ActorRef = { + newRpcClient(connection, exchangeName, routingKey, serializer, None) + } + + // Needed for Java API usage + def newRpcClient[O, I](connection: ActorRef, + exchangeName: String, + routingKey: String, + serializer: RpcClientSerializer[O, I], + channelParameters: ChannelParameters): ActorRef = { + newRpcClient(connection, exchangeName, routingKey, serializer, Some(channelParameters)) + } + def newRpcClient[O, I](connection: ActorRef, exchangeName: String, routingKey: String, serializer: RpcClientSerializer[O, I], channelParameters: Option[ChannelParameters] = None): ActorRef = { val rpcActor: ActorRef = actorOf(new RpcClientActor[O, I]( - ExchangeParameters(exchangeName), routingKey, serializer, channelParameters)) + ExchangeParameters(exchangeName, exchangeDeclaration = PassiveDeclaration), routingKey, serializer, channelParameters)) connection.startLink(rpcActor) rpcActor ! Start rpcActor } + // Needed for Java API usage + def newRpcServer[I, O](connection: ActorRef, + exchangeName: String, + routingKey: String, + serializer: RpcServerSerializer[I, O], + requestHandler: se.scalablesolutions.akka.util.Function[I,O]): RpcServerHandle = { + newRpcServer(connection, exchangeName, routingKey, serializer, requestHandler.apply _) + } + + // Needed for Java API usage + def newRpcServer[I, O](connection: ActorRef, + exchangeName: String, + routingKey: String, + serializer: RpcServerSerializer[I, O], + requestHandler: se.scalablesolutions.akka.util.Function[I,O], + queueName: String): RpcServerHandle = { + newRpcServer(connection, exchangeName, routingKey, serializer, requestHandler.apply _, Some(queueName)) + } + + // Needed for Java API usage + def newRpcServer[I, O](connection: ActorRef, + exchangeName: String, + routingKey: String, + serializer: RpcServerSerializer[I, O], + requestHandler: se.scalablesolutions.akka.util.Function[I,O], + channelParameters: ChannelParameters): RpcServerHandle = { + newRpcServer(connection, exchangeName, routingKey, serializer, requestHandler.apply _, None, Some(channelParameters)) + } + + // Needed for Java API usage + def newRpcServer[I, O](connection: ActorRef, + exchangeName: String, + routingKey: String, + serializer: RpcServerSerializer[I, O], + requestHandler: se.scalablesolutions.akka.util.Function[I,O], + queueName: String, + channelParameters: ChannelParameters): RpcServerHandle = { + newRpcServer(connection, exchangeName, routingKey, serializer, requestHandler.apply _, Some(queueName), Some(channelParameters)) + } + def newRpcServer[I, O](connection: ActorRef, exchangeName: String, routingKey: String, @@ -52,10 +111,28 @@ object RPC { * RPC convenience */ class RpcClient[O, I](client: ActorRef){ + + // Needed for Java API usage + def call(request: O): Option[I] = { + call(request, 5000) + } + def call(request: O, timeout: Long = 5000): Option[I] = { (client.!!(request, timeout)).as[I] } + // Needed for Java API usage + def callAsync(request: O, responseHandler: Procedure[I]): Unit = { + callAsync(request, 5000, responseHandler) + } + + // Needed for Java API usage + def callAsync(request: O, timeout: Long, responseHandler: Procedure[I]): Unit = { + callAsync(request, timeout){ + case Some(response) => responseHandler.apply(response) + } + } + def callAsync(request: O, timeout: Long = 5000)(responseHandler: PartialFunction[Option[I],Unit]) = { spawn { val result = call(request, timeout) @@ -65,14 +142,49 @@ object RPC { def stop = client.stop } + + // Needed for Java API usage + def newProtobufRpcServer[I <: Message, O <: Message]( + connection: ActorRef, + exchangeName: String, + requestHandler: se.scalablesolutions.akka.util.Function[I,O], + resultClazz: Class[I]): RpcServerHandle = { + + implicit val manifest = Manifest.classType[I](resultClazz) + newProtobufRpcServer(connection, exchangeName, requestHandler.apply _) + } + + // Needed for Java API usage + def newProtobufRpcServer[I <: Message, O <: Message]( + connection: ActorRef, + exchangeName: String, + requestHandler: se.scalablesolutions.akka.util.Function[I,O], + routingKey: String, + resultClazz: Class[I]): RpcServerHandle = { + + implicit val manifest = Manifest.classType[I](resultClazz) + newProtobufRpcServer(connection, exchangeName, requestHandler.apply _, Some(routingKey)) + } + + // Needed for Java API usage + def newProtobufRpcServer[I <: Message, O <: Message]( + connection: ActorRef, + exchangeName: String, + requestHandler: se.scalablesolutions.akka.util.Function[I,O], + routingKey: String, + queueName: String, + resultClazz: Class[I]): RpcServerHandle = { + + implicit val manifest = Manifest.classType[I](resultClazz) + newProtobufRpcServer(connection, exchangeName, requestHandler.apply _, Some(routingKey), Some(queueName)) + } + def newProtobufRpcServer[I <: Message, O <: Message]( connection: ActorRef, exchangeName: String, requestHandler: I => O, routingKey: Option[String] = None, - queueName: Option[String] = None, - durable: Boolean = false, - autoDelete: Boolean = true)(implicit manifest: Manifest[I]): RpcServerHandle = { + queueName: Option[String] = None)(implicit manifest: Manifest[I]): RpcServerHandle = { val serializer = new RpcServerSerializer[I, O]( new FromBinary[I] { @@ -83,16 +195,34 @@ object RPC { def toBinary(t: O) = t.toByteArray }) - startServer(connection, exchangeName, requestHandler, routingKey, queueName, durable, autoDelete, serializer) + startServer(connection, exchangeName, requestHandler, routingKey, queueName, serializer) + } + + // Needed for Java API usage + def newProtobufRpcClient[O <: Message, I <: Message]( + connection: ActorRef, + exchangeName: String, + resultClazz: Class[I]): RpcClient[O, I] = { + + implicit val manifest = Manifest.classType[I](resultClazz) + newProtobufRpcClient(connection, exchangeName, None) + } + + // Needed for Java API usage + def newProtobufRpcClient[O <: Message, I <: Message]( + connection: ActorRef, + exchangeName: String, + routingKey: String, + resultClazz: Class[I]): RpcClient[O, I] = { + + implicit val manifest = Manifest.classType[I](resultClazz) + newProtobufRpcClient(connection, exchangeName, Some(routingKey)) } def newProtobufRpcClient[O <: Message, I <: Message]( connection: ActorRef, exchangeName: String, - routingKey: Option[String] = None, - durable: Boolean = false, - autoDelete: Boolean = true, - passive: Boolean = true)(implicit manifest: Manifest[I]): RpcClient[O, I] = { + routingKey: Option[String] = None)(implicit manifest: Manifest[I]): RpcClient[O, I] = { val serializer = new RpcClientSerializer[O, I]( @@ -104,16 +234,38 @@ object RPC { } }) - startClient(connection, exchangeName, routingKey, durable, autoDelete, passive, serializer) + startClient(connection, exchangeName, routingKey, serializer) + } + + // Needed for Java API usage + def newStringRpcServer(connection: ActorRef, + exchangeName: String, + requestHandler: se.scalablesolutions.akka.util.Function[String,String]): RpcServerHandle = { + newStringRpcServer(connection, exchangeName, requestHandler.apply _) + } + + // Needed for Java API usage + def newStringRpcServer(connection: ActorRef, + exchangeName: String, + requestHandler: se.scalablesolutions.akka.util.Function[String,String], + routingKey: String): RpcServerHandle = { + newStringRpcServer(connection, exchangeName, requestHandler.apply _, Some(routingKey)) + } + + // Needed for Java API usage + def newStringRpcServer(connection: ActorRef, + exchangeName: String, + requestHandler: se.scalablesolutions.akka.util.Function[String,String], + routingKey: String, + queueName: String): RpcServerHandle = { + newStringRpcServer(connection, exchangeName, requestHandler.apply _, Some(routingKey), Some(queueName)) } def newStringRpcServer(connection: ActorRef, exchangeName: String, requestHandler: String => String, routingKey: Option[String] = None, - queueName: Option[String] = None, - durable: Boolean = false, - autoDelete: Boolean = true): RpcServerHandle = { + queueName: Option[String] = None): RpcServerHandle = { val serializer = new RpcServerSerializer[String, String]( new FromBinary[String] { @@ -124,15 +276,25 @@ object RPC { def toBinary(t: String) = t.getBytes }) - startServer(connection, exchangeName, requestHandler, routingKey, queueName, durable, autoDelete, serializer) + startServer(connection, exchangeName, requestHandler, routingKey, queueName, serializer) + } + + // Needed for Java API usage + def newStringRpcClient(connection: ActorRef, + exchange: String): RpcClient[String, String] = { + newStringRpcClient(connection, exchange, None) + } + + // Needed for Java API usage + def newStringRpcClient(connection: ActorRef, + exchange: String, + routingKey: String): RpcClient[String, String] = { + newStringRpcClient(connection, exchange, Some(routingKey)) } def newStringRpcClient(connection: ActorRef, exchange: String, - routingKey: Option[String] = None, - durable: Boolean = false, - autoDelete: Boolean = true, - passive: Boolean = true): RpcClient[String, String] = { + routingKey: Option[String] = None): RpcClient[String, String] = { val serializer = new RpcClientSerializer[String, String]( @@ -144,15 +306,12 @@ object RPC { } }) - startClient(connection, exchange, routingKey, durable, autoDelete, passive, serializer) + startClient(connection, exchange, routingKey, serializer) } private def startClient[O, I](connection: ActorRef, exchangeName: String, routingKey: Option[String] = None, - durable: Boolean = false, - autoDelete: Boolean = true, - passive: Boolean = true, serializer: RpcClientSerializer[O, I]): RpcClient[O, I] = { val rKey = routingKey.getOrElse("%s.request".format(exchangeName)) @@ -166,8 +325,6 @@ object RPC { requestHandler: I => O, routingKey: Option[String] = None, queueName: Option[String] = None, - durable: Boolean = false, - autoDelete: Boolean = true, serializer: RpcServerSerializer[I, O]): RpcServerHandle = { val rKey = routingKey.getOrElse("%s.request".format(exchangeName)) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTestIntegration.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTestIntegration.scala index 0fed1951db..baa6b4e551 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTestIntegration.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPConsumerMessageTestIntegration.scala @@ -18,31 +18,26 @@ class AMQPConsumerMessageTestIntegration extends JUnitSuite with MustMatchers { @Test def consumerMessage = AMQPTest.withCleanEndState { val connection = AMQP.newConnection() - try { - - val countDown = new CountDownLatch(2) - val channelCallback = actor { - case Started => countDown.countDown - case Restarting => () - case Stopped => () - } - - val exchangeParameters = ExchangeParameters("text_exchange") - val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) - - val payloadLatch = new StandardLatch - val consumer = AMQP.newConsumer(connection, ConsumerParameters("non.interesting.routing.key", actor { - case Delivery(payload, _, _, _, _) => payloadLatch.open - }, exchangeParameters = Some(exchangeParameters), channelParameters = Some(channelParameters))) - - val producer = AMQP.newProducer(connection, - ProducerParameters(Some(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) - } finally { - connection.stop + val countDown = new CountDownLatch(2) + val channelCallback = actor { + case Started => countDown.countDown + case Restarting => () + case Stopped => () } + + val exchangeParameters = ExchangeParameters("text_exchange") + val channelParameters = ChannelParameters(channelCallback = Some(channelCallback)) + + val payloadLatch = new StandardLatch + val consumer = AMQP.newConsumer(connection, ConsumerParameters("non.interesting.routing.key", actor { + case Delivery(payload, _, _, _, _) => payloadLatch.open + }, exchangeParameters = Some(exchangeParameters), channelParameters = Some(channelParameters))) + + val producer = AMQP.newProducer(connection, + ProducerParameters(Some(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) } } diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProtobufProducerConsumerTestIntegration.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProtobufProducerConsumerTestIntegration.scala index d6d719d241..1502d1e40b 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProtobufProducerConsumerTestIntegration.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPProtobufProducerConsumerTestIntegration.scala @@ -29,7 +29,7 @@ class AMQPProtobufProducerConsumerTestIntegration extends JUnitSuite with MustMa assert(response.getHostname == request.getHostname.reverse) responseLatch.open } - AMQP.newProtobufConsumer(connection, responseHandler, None, Some("proto.reply.key")) + AMQP.newProtobufConsumer(connection, responseHandler _, None, Some("proto.reply.key")) val producer = AMQP.newProtobufProducer[AddressProtocol](connection, Some("protoexchange")) producer.send(request, Some("proto.reply.key")) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTestIntegration.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTestIntegration.scala index d322b243c8..b4f2a49939 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTestIntegration.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcClientServerTestIntegration.scala @@ -42,7 +42,7 @@ class AMQPRpcClientServerTestIntegration extends JUnitSuite with MustMatchers { def requestHandler(request: String) = 3 val rpcServer = RPC.newRpcServer[String, Int](connection, exchangeName, "rpc.routing", rpcServerSerializer, - requestHandler, channelParameters = Some(channelParameters)) + requestHandler _, channelParameters = Some(channelParameters)) val rpcClientSerializer = new RpcClientSerializer[String, Int]( new ToBinary[String] { diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcStringTestIntegration.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcStringTestIntegration.scala index 964cd94adb..fb36af74ab 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcStringTestIntegration.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPRpcStringTestIntegration.scala @@ -18,7 +18,7 @@ class AMQPRpcStringTestIntegration extends JUnitSuite with MustMatchers { val connection = AMQP.newConnection() - RPC.newStringRpcServer(connection, "stringservice", requestHandler) + RPC.newStringRpcServer(connection, "stringservice", requestHandler _) val protobufClient = RPC.newStringRpcClient(connection, "stringservice") diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTestIntegration.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTestIntegration.scala index 6fa7ccefd2..a9de971815 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTestIntegration.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPStringProducerConsumerTestIntegration.scala @@ -20,7 +20,7 @@ class AMQPStringProducerConsumerTestIntegration extends JUnitSuite with MustMatc val responseLatch = new StandardLatch - RPC.newStringRpcServer(connection, "stringexchange", requestHandler) + RPC.newStringRpcServer(connection, "stringexchange", requestHandler _) val request = "somemessage" @@ -29,7 +29,7 @@ class AMQPStringProducerConsumerTestIntegration extends JUnitSuite with MustMatc assert(response == request.reverse) responseLatch.open } - AMQP.newStringConsumer(connection, responseHandler, None, Some("string.reply.key")) + AMQP.newStringConsumer(connection, responseHandler _, None, Some("string.reply.key")) val producer = AMQP.newStringProducer(connection, Some("stringexchange")) producer.send(request, Some("string.reply.key")) diff --git a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala index ec1b6aa2f4..2a35df0a77 100644 --- a/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala +++ b/akka-amqp/src/test/scala/se/scalablesolutions/akka/amqp/test/AMQPTest.scala @@ -10,9 +10,13 @@ object AMQPTest { def withCleanEndState(action: => Unit) { try { - action - } finally { - AMQP.shutdownAll + try { + action + } finally { + AMQP.shutdownAll + } + } catch { + case e => println(e) } } } From fa2268a3e6d2c7146abe152c8c83ff8aee15ea32 Mon Sep 17 00:00:00 2001 From: momania Date: Fri, 8 Oct 2010 18:10:57 +0200 Subject: [PATCH 28/32] after merge cleanup --- .../akka/amqp/ExampleSessionJava.java | 4 +-- .../amqp/FaultTolerantConnectionActor.scala | 3 --- .../scalablesolutions/akka/amqp/rpc/RPC.scala | 26 +++++++++---------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java index 19b12d4c64..398feb17ce 100644 --- a/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java +++ b/akka-amqp/src/main/java/se/scalablesolutions/akka/amqp/ExampleSessionJava.java @@ -10,8 +10,8 @@ import se.scalablesolutions.akka.actor.UntypedActorFactory; import se.scalablesolutions.akka.amqp.rpc.RPC; import se.scalablesolutions.akka.remote.protocol.RemoteProtocol; -import se.scalablesolutions.akka.util.Function; -import se.scalablesolutions.akka.util.Procedure; +import se.scalablesolutions.akka.japi.Function; +import se.scalablesolutions.akka.japi.Procedure; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala index e23acb8bc8..16ec8db389 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/FaultTolerantConnectionActor.scala @@ -19,9 +19,6 @@ private[amqp] class FaultTolerantConnectionActor(connectionParameters: Connectio self.lifeCycle = Permanent self.faultHandler = OneForOneStrategy(List(classOf[Throwable])) - self.trapExit = List(classOf[Throwable]) - self.faultHandler = Some(OneForOneStrategy(None, None)) // never die - val reconnectionTimer = new Timer("%s-timer".format(self.id)) val connectionFactory: ConnectionFactory = new ConnectionFactory() diff --git a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala index 87800c7742..ed0f8be7e1 100644 --- a/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala +++ b/akka-amqp/src/main/scala/se/scalablesolutions/akka/amqp/rpc/RPC.scala @@ -5,8 +5,8 @@ import com.google.protobuf.Message import se.scalablesolutions.akka.actor.{Actor, ActorRef} import Actor._ import se.scalablesolutions.akka.amqp._ -import se.scalablesolutions.akka.util.Procedure import reflect.Manifest +import se.scalablesolutions.akka.japi object RPC { @@ -44,7 +44,7 @@ object RPC { exchangeName: String, routingKey: String, serializer: RpcServerSerializer[I, O], - requestHandler: se.scalablesolutions.akka.util.Function[I,O]): RpcServerHandle = { + requestHandler: japi.Function[I,O]): RpcServerHandle = { newRpcServer(connection, exchangeName, routingKey, serializer, requestHandler.apply _) } @@ -53,7 +53,7 @@ object RPC { exchangeName: String, routingKey: String, serializer: RpcServerSerializer[I, O], - requestHandler: se.scalablesolutions.akka.util.Function[I,O], + requestHandler: Function[I,O], queueName: String): RpcServerHandle = { newRpcServer(connection, exchangeName, routingKey, serializer, requestHandler.apply _, Some(queueName)) } @@ -63,7 +63,7 @@ object RPC { exchangeName: String, routingKey: String, serializer: RpcServerSerializer[I, O], - requestHandler: se.scalablesolutions.akka.util.Function[I,O], + requestHandler: japi.Function[I,O], channelParameters: ChannelParameters): RpcServerHandle = { newRpcServer(connection, exchangeName, routingKey, serializer, requestHandler.apply _, None, Some(channelParameters)) } @@ -73,7 +73,7 @@ object RPC { exchangeName: String, routingKey: String, serializer: RpcServerSerializer[I, O], - requestHandler: se.scalablesolutions.akka.util.Function[I,O], + requestHandler: japi.Function[I,O], queueName: String, channelParameters: ChannelParameters): RpcServerHandle = { newRpcServer(connection, exchangeName, routingKey, serializer, requestHandler.apply _, Some(queueName), Some(channelParameters)) @@ -122,12 +122,12 @@ object RPC { } // Needed for Java API usage - def callAsync(request: O, responseHandler: Procedure[I]): Unit = { + def callAsync(request: O, responseHandler: japi.Procedure[I]): Unit = { callAsync(request, 5000, responseHandler) } // Needed for Java API usage - def callAsync(request: O, timeout: Long, responseHandler: Procedure[I]): Unit = { + def callAsync(request: O, timeout: Long, responseHandler: japi.Procedure[I]): Unit = { callAsync(request, timeout){ case Some(response) => responseHandler.apply(response) } @@ -147,7 +147,7 @@ object RPC { def newProtobufRpcServer[I <: Message, O <: Message]( connection: ActorRef, exchangeName: String, - requestHandler: se.scalablesolutions.akka.util.Function[I,O], + requestHandler: japi.Function[I,O], resultClazz: Class[I]): RpcServerHandle = { implicit val manifest = Manifest.classType[I](resultClazz) @@ -158,7 +158,7 @@ object RPC { def newProtobufRpcServer[I <: Message, O <: Message]( connection: ActorRef, exchangeName: String, - requestHandler: se.scalablesolutions.akka.util.Function[I,O], + requestHandler: japi.Function[I,O], routingKey: String, resultClazz: Class[I]): RpcServerHandle = { @@ -170,7 +170,7 @@ object RPC { def newProtobufRpcServer[I <: Message, O <: Message]( connection: ActorRef, exchangeName: String, - requestHandler: se.scalablesolutions.akka.util.Function[I,O], + requestHandler: japi.Function[I,O], routingKey: String, queueName: String, resultClazz: Class[I]): RpcServerHandle = { @@ -240,14 +240,14 @@ object RPC { // Needed for Java API usage def newStringRpcServer(connection: ActorRef, exchangeName: String, - requestHandler: se.scalablesolutions.akka.util.Function[String,String]): RpcServerHandle = { + requestHandler: japi.Function[String,String]): RpcServerHandle = { newStringRpcServer(connection, exchangeName, requestHandler.apply _) } // Needed for Java API usage def newStringRpcServer(connection: ActorRef, exchangeName: String, - requestHandler: se.scalablesolutions.akka.util.Function[String,String], + requestHandler: japi.Function[String,String], routingKey: String): RpcServerHandle = { newStringRpcServer(connection, exchangeName, requestHandler.apply _, Some(routingKey)) } @@ -255,7 +255,7 @@ object RPC { // Needed for Java API usage def newStringRpcServer(connection: ActorRef, exchangeName: String, - requestHandler: se.scalablesolutions.akka.util.Function[String,String], + requestHandler: japi.Function[String,String], routingKey: String, queueName: String): RpcServerHandle = { newStringRpcServer(connection, exchangeName, requestHandler.apply _, Some(routingKey), Some(queueName)) From e3a8f9ba26ff5a56a25f3ef8430fafd181ecbc95 Mon Sep 17 00:00:00 2001 From: Andrey Popp <8mayday@gmail.com> Date: Fri, 8 Oct 2010 20:09:13 +0400 Subject: [PATCH 29/32] Add more specs for restart strategy params. --- .../supervisor/RestartStrategySpec.scala | 208 ++++++++++++++++-- 1 file changed, 192 insertions(+), 16 deletions(-) diff --git a/akka-actor/src/test/scala/actor/supervisor/RestartStrategySpec.scala b/akka-actor/src/test/scala/actor/supervisor/RestartStrategySpec.scala index b9fa238963..777aced548 100644 --- a/akka-actor/src/test/scala/actor/supervisor/RestartStrategySpec.scala +++ b/akka-actor/src/test/scala/actor/supervisor/RestartStrategySpec.scala @@ -4,6 +4,8 @@ package se.scalablesolutions.akka.actor +import java.lang.Thread.sleep + import org.scalatest.junit.JUnitSuite import org.junit.Test @@ -19,17 +21,18 @@ class RestartStrategySpec extends JUnitSuite { object Crash @Test - def slaveShouldStayDeadAfterMaxRestarts = { + def slaveShouldStayDeadAfterMaxRestartsWithinTimeRange = { val boss = actorOf(new Actor{ self.trapExit = List(classOf[Throwable]) - self.faultHandler = Some(OneForOneStrategy(1, 1000)) + self.faultHandler = Some(OneForOneStrategy(Some(2), Some(1000))) protected def receive = { case _ => () } }).start val restartLatch = new StandardLatch val secondRestartLatch = new StandardLatch - val countDownLatch = new CountDownLatch(2) + val countDownLatch = new CountDownLatch(3) + val stopLatch = new StandardLatch val slave = actorOf(new Actor{ @@ -39,13 +42,14 @@ class RestartStrategySpec extends JUnitSuite { case Crash => throw new Exception("Crashing...") } override def postRestart(reason: Throwable) = { - restartLatch.open + if (!restartLatch.isOpen) + restartLatch.open + else + secondRestartLatch.open } override def postStop = { - if (restartLatch.isOpen) { - secondRestartLatch.open - } + stopLatch.open } }) boss.startLink(slave) @@ -56,23 +60,24 @@ class RestartStrategySpec extends JUnitSuite { // test restart and post restart ping assert(restartLatch.tryAwait(1, TimeUnit.SECONDS)) - assert(countDownLatch.await(1, TimeUnit.SECONDS)) + + assert(slave.isRunning) // now crash again... should not restart slave ! Crash + slave ! Ping 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)) + assert(countDownLatch.await(1, TimeUnit.SECONDS)) + + slave ! Crash + assert(stopLatch.tryAwait(1, TimeUnit.SECONDS)) + + assert(!slave.isRunning) } @Test - def slaveShouldBeImmortalWithoutMaxRestarts = { + def slaveShouldBeImmortalWithoutMaxRestartsAndTimeRange = { val boss = actorOf(new Actor{ self.trapExit = List(classOf[Throwable]) @@ -96,6 +101,177 @@ class RestartStrategySpec extends JUnitSuite { boss.startLink(slave) (1 to 100) foreach { _ => slave ! Crash } assert(countDownLatch.await(120, TimeUnit.SECONDS)) + assert(slave.isRunning) + } + + @Test + def slaveShouldRestartAfterNumberOfCrashesNotWithinTimeRange = { + + val boss = actorOf(new Actor{ + self.trapExit = List(classOf[Throwable]) + self.faultHandler = Some(OneForOneStrategy(Some(2), Some(500))) + protected def receive = { case _ => () } + }).start + + val restartLatch = new StandardLatch + val secondRestartLatch = new StandardLatch + val thirdRestartLatch = new StandardLatch + val pingLatch = new StandardLatch + val secondPingLatch = new StandardLatch + + val slave = actorOf(new Actor{ + + protected def receive = { + case Ping => + if (!pingLatch.isOpen) pingLatch.open else secondPingLatch.open + case Crash => throw new Exception("Crashing...") + } + override def postRestart(reason: Throwable) = { + if (!restartLatch.isOpen) + restartLatch.open + else if (!secondRestartLatch.isOpen) + secondRestartLatch.open + else + thirdRestartLatch.open + } + + override def postStop = { + if (restartLatch.isOpen) { + secondRestartLatch.open + } + } + }) + boss.startLink(slave) + + slave ! Ping + slave ! Crash + + assert(restartLatch.tryAwait(1, TimeUnit.SECONDS)) + assert(pingLatch.tryAwait(1, TimeUnit.SECONDS)) + + slave ! Ping + slave ! Crash + + assert(secondRestartLatch.tryAwait(1, TimeUnit.SECONDS)) + assert(secondPingLatch.tryAwait(1, TimeUnit.SECONDS)) + + // sleep to go out of the restart strategy's time range + sleep(700L) + + // now crash again... should and post restart ping + slave ! Crash + slave ! Ping + + assert(thirdRestartLatch.tryAwait(1, TimeUnit.SECONDS)) + + assert(slave.isRunning) + } + + @Test + def slaveShouldNotRestartAfterMaxRetries = { + val boss = actorOf(new Actor{ + self.trapExit = List(classOf[Throwable]) + self.faultHandler = Some(OneForOneStrategy(Some(2), None)) + protected def receive = { case _ => () } + }).start + + val restartLatch = new StandardLatch + val secondRestartLatch = new StandardLatch + val countDownLatch = new CountDownLatch(3) + val stopLatch = new StandardLatch + + + val slave = actorOf(new Actor{ + + protected def receive = { + case Ping => countDownLatch.countDown + case Crash => throw new Exception("Crashing...") + } + override def postRestart(reason: Throwable) = { + if (!restartLatch.isOpen) + restartLatch.open + else + secondRestartLatch.open + } + + override def postStop = { + stopLatch.open + } + }) + boss.startLink(slave) + + slave ! Ping + slave ! Crash + slave ! Ping + + // test restart and post restart ping + assert(restartLatch.tryAwait(1, TimeUnit.SECONDS)) + + assert(slave.isRunning) + + // now crash again... should not restart + slave ! Crash + slave ! Ping + + assert(secondRestartLatch.tryAwait(1, TimeUnit.SECONDS)) + assert(countDownLatch.await(1, TimeUnit.SECONDS)) + + sleep(700L) + + slave ! Crash + assert(stopLatch.tryAwait(1, TimeUnit.SECONDS)) + + assert(!slave.isRunning) + } + + @Test + def slaveShouldNotRestartWithinTimeRange = { + val boss = actorOf(new Actor{ + self.trapExit = List(classOf[Throwable]) + self.faultHandler = Some(OneForOneStrategy(None, Some(1000))) + protected def receive = { case _ => () } + }).start + + val restartLatch = new StandardLatch + val countDownLatch = new CountDownLatch(3) + val stopLatch = new StandardLatch + + + 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 postStop = { + stopLatch.open + } + }) + boss.startLink(slave) + + slave ! Ping + slave ! Crash + slave ! Ping + + // test restart and post restart ping + assert(restartLatch.tryAwait(1, TimeUnit.SECONDS)) + + assert(slave.isRunning) + + // now crash again... should not restart + slave ! Crash + slave ! Ping + + assert(countDownLatch.await(1, TimeUnit.SECONDS)) + + slave ! Crash + assert(stopLatch.tryAwait(1, TimeUnit.SECONDS)) + + assert(!slave.isRunning) } } From 9d18fac7291ed06f9c3ed78ca437c23ee091f287 Mon Sep 17 00:00:00 2001 From: Andrey Popp <8mayday@gmail.com> Date: Fri, 8 Oct 2010 20:39:39 +0400 Subject: [PATCH 30/32] Rework restart strategy restart decision. --- .../src/main/scala/actor/ActorRef.scala | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/akka-actor/src/main/scala/actor/ActorRef.scala b/akka-actor/src/main/scala/actor/ActorRef.scala index 552fe2cfdf..9352e31c91 100644 --- a/akka-actor/src/main/scala/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/actor/ActorRef.scala @@ -1050,19 +1050,24 @@ class LocalActorRef private[akka] ( } } + protected[akka] def canRestart(maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]): Boolean = { + (maxNrOfRetries, withinTimeRange) match { + case (None, None) => // immortal + true + case (Some(maxNrOfRetries), None) => // restrict number of restarts + maxNrOfRetriesCount < maxNrOfRetries + case (None, Some(withinTimeRange)) => // cannot restart within time range since last restart + canRestart(Some(1), Some(withinTimeRange)) + case (Some(maxNrOfRetries), Some(withinTimeRange)) => // cannot restart more than N within M timerange + !((maxNrOfRetriesCount >= maxNrOfRetries) && + (System.currentTimeMillis - restartsWithinTimeRangeTimestamp < withinTimeRange)) + } + } + protected[akka] def restart(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]): Unit = { - if (maxNrOfRetriesCount == 0) restartsWithinTimeRangeTimestamp = System.currentTimeMillis // first time around + if (maxNrOfRetriesCount == 0) restartsWithinTimeRangeTimestamp = System.currentTimeMillis - val tooManyRestarts = if (maxNrOfRetries.isDefined) { - maxNrOfRetriesCount += 1 - maxNrOfRetriesCount > maxNrOfRetries.get - } else false - - val restartingHasExpired = if (withinTimeRange.isDefined) - (System.currentTimeMillis - restartsWithinTimeRangeTimestamp) > withinTimeRange.get - else false - - if (tooManyRestarts || restartingHasExpired) { + if (!canRestart(maxNrOfRetries, withinTimeRange)) { val notification = MaximumNumberOfRestartsWithinTimeRangeReached(this, maxNrOfRetries, withinTimeRange, reason) Actor.log.warning( "Maximum number of restarts [%s] within time range [%s] reached." + @@ -1096,6 +1101,13 @@ class LocalActorRef private[akka] ( else restartActor(failedActor, reason) _status = ActorRefStatus.RUNNING + + // update restart parameters + if (maxNrOfRetries.isDefined && maxNrOfRetriesCount % maxNrOfRetries.get == 0 && maxNrOfRetriesCount != 0) + restartsWithinTimeRangeTimestamp = System.currentTimeMillis + else if (!maxNrOfRetries.isDefined) + restartsWithinTimeRangeTimestamp = System.currentTimeMillis + maxNrOfRetriesCount += 1 } } } From 54c5ddf974a375fc182e8ef7c9e65ce88be637db Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 8 Oct 2010 21:49:57 +0200 Subject: [PATCH 31/32] Switching to a more accurate approach that involves no locking and no thread locals --- akka-actor/src/main/scala/actor/ActorRef.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/actor/ActorRef.scala b/akka-actor/src/main/scala/actor/ActorRef.scala index 4382d78d5c..62634edadf 100644 --- a/akka-actor/src/main/scala/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/actor/ActorRef.scala @@ -789,8 +789,9 @@ class LocalActorRef private[akka] ( } _status = ActorRefStatus.RUNNING - //If actorRefInCreation is empty, we're outside creation of the actor, and so we can initialize the actor instance. - if (Actor.actorRefInCreation.value.isEmpty) initializeActorInstance + //If we are not currently creating this ActorRef instance + if ((actorInstance ne null) && (actorInstance.get ne null)) + initializeActorInstance checkReceiveTimeout //Schedule the initial Receive timeout } From bdeaa744938cb2662e133fd4d16edb41fac7183f Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 8 Oct 2010 22:28:39 +0200 Subject: [PATCH 32/32] Removed all allocations from the canRestart-method --- .../src/main/scala/actor/ActorRef.scala | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/akka-actor/src/main/scala/actor/ActorRef.scala b/akka-actor/src/main/scala/actor/ActorRef.scala index a52ba52c1f..7f7311bee6 100644 --- a/akka-actor/src/main/scala/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/actor/ActorRef.scala @@ -1032,16 +1032,15 @@ class LocalActorRef private[akka] ( } protected[akka] def canRestart(maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]): Boolean = { - (maxNrOfRetries, withinTimeRange) match { - case (None, None) => // immortal - true - case (Some(maxNrOfRetries), None) => // restrict number of restarts - maxNrOfRetriesCount < maxNrOfRetries - case (None, Some(withinTimeRange)) => // cannot restart within time range since last restart - canRestart(Some(1), Some(withinTimeRange)) - case (Some(maxNrOfRetries), Some(withinTimeRange)) => // cannot restart more than N within M timerange - !((maxNrOfRetriesCount >= maxNrOfRetries) && - (System.currentTimeMillis - restartsWithinTimeRangeTimestamp < withinTimeRange)) + if (maxNrOfRetries.isEmpty && withinTimeRange.isEmpty) { //Immortal + true + } + else if (withinTimeRange.isEmpty) { // restrict number of restarts + maxNrOfRetriesCount < maxNrOfRetries.get + } else { // cannot restart more than N within M timerange + val maxRetries = if (maxNrOfRetries.isEmpty) 1 else maxNrOfRetries.get //Default to 1, has to match timerange also + !((maxNrOfRetriesCount >= maxRetries) && + (System.currentTimeMillis - restartsWithinTimeRangeTimestamp < withinTimeRange.get)) } }