Fixed problem with implicit sender + updated changes.xml

This commit is contained in:
jboner 2009-11-23 15:19:53 +01:00
parent f98184ff34
commit 2fcf0279d6
15 changed files with 322 additions and 211 deletions

View file

@ -161,7 +161,6 @@ private[akka] sealed case class AspectInit(
*/
@Aspect("perInstance")
private[akka] sealed class ActiveObjectAspect {
import Actor._
@volatile var isInitialized = false
var target: Class[_] = _
@ -188,6 +187,7 @@ private[akka] sealed class ActiveObjectAspect {
}
private def localDispatch(joinPoint: JoinPoint): AnyRef = {
import Actor.Sender.Self
val rtti = joinPoint.getRtti.asInstanceOf[MethodRtti]
if (isOneWay(rtti)) actor ! Invocation(joinPoint, true, true)
else {

View file

@ -70,7 +70,18 @@ object Actor {
val TIMEOUT = config.getInt("akka.actor.timeout", 5000)
val SERIALIZE_MESSAGES = config.getBool("akka.actor.serialize-messages", false)
implicit val any: AnyRef = this
object Sender extends Actor {
implicit val Self: AnyRef = this
def receive = {
case unknown =>
log.error(
"Actor.Sender can't process messages. Received message [%s]." +
"This error could occur if you either:" +
"\n\t- Explicitly send a message to the Actor.Sender object." +
"\n\t- Invoking the 'reply(..)' method or sending a message to the 'sender' reference " +
"\n\t when you have sent the original request from a instance *not* being an actor.", unknown)
}
}
/**
* Use to create an anonymous event-driven actor.
@ -165,7 +176,7 @@ object Actor {
trait Actor extends Logging with TransactionManagement {
ActorRegistry.register(this)
implicit val self: AnyRef = this
implicit protected val self: Actor = this
// FIXME http://www.assembla.com/spaces/akka/tickets/56-Change-UUID-generation-for-the-TransactionManagement-trait
private[akka] var _uuid = Uuid.newUuid.toString
@ -398,12 +409,30 @@ trait Actor extends Logging with TransactionManagement {
/**
* Sends a one-way asynchronous message. E.g. fire-and-forget semantics.
* <p/>
*
* If invoked from within an actor then the actor reference is implicitly passed on as the implicit 'sender' argument.
* <p/>
*
* This actor 'sender' reference is then available in the receiving actor in the 'sender' member variable.
* <p/>
* If invoked from within another object then add this import to resolve the implicit argument:
* <pre>
* import se.scalablesolutions.akka.actor.Actor._
* actor ! message
* </pre>
* <p/>
*
* If invoked from within a *non* Actor instance then either add this import to resolve the implicit argument:
* <pre>
* import Actor.Sender._
* actor ! message
* </pre>
*
* Or pass in the implicit argument explicitly:
* <pre>
* actor.!(message)(this)
* </pre>
*
* Or use the 'send(..)' method;
* <pre>
* actor.send(message)
* </pre>
*/
def !(message: AnyRef)(implicit sender: AnyRef) = {

View file

@ -156,6 +156,7 @@ class RemoteClientHandler(val name: String,
val supervisors: ConcurrentMap[String, Actor],
val bootstrap: ClientBootstrap)
extends SimpleChannelUpstreamHandler with Logging {
import Actor.Sender.Self
override def handleUpstream(ctx: ChannelHandlerContext, event: ChannelEvent) = {
if (event.isInstanceOf[ChannelStateEvent] &&
@ -166,7 +167,6 @@ class RemoteClientHandler(val name: String,
}
override def messageReceived(ctx: ChannelHandlerContext, event: MessageEvent) {
import Actor._
try {
val result = event.getMessage
if (result.isInstanceOf[RemoteReply]) {

View file

@ -139,12 +139,13 @@ class RemoteServerHandler(val name: String, val applicationLoader: Option[ClassL
}
private def dispatchToActor(request: RemoteRequest, channel: Channel) = {
import Actor._
log.debug("Dispatching to remote actor [%s]", request.getTarget)
val actor = createActor(request.getTarget, request.getUuid, request.getTimeout)
actor.start
val message = RemoteProtocolBuilder.getMessage(request)
if (request.getIsOneWay) actor ! message
if (request.getIsOneWay) {
actor.send(message)
}
else {
try {
val resultOrNone = actor !! message

View file

@ -27,25 +27,27 @@ class ActorFireForgetRequestReplyTest extends JUnitSuite {
@Test
def shouldReplyToBangMessageUsingReply = {
import Actor._
import Actor.Sender.Self
val replyActor = new ReplyActor
replyActor.start
val senderActor = new SenderActor(replyActor)
senderActor.start
senderActor ! "Init"
Thread.sleep(10000)
Thread.sleep(1000)
assert("Reply" === state.s)
}
@Test
def shouldReplyToBangMessageUsingImplicitSender = {
import Actor._
import Actor.Sender.Self
val replyActor = new ReplyActor
replyActor.start
val senderActor = new SenderActor(replyActor)
senderActor.start
senderActor ! "InitImplicit"
Thread.sleep(10000)
Thread.sleep(1000)
assert("ReplyImplicit" === state.s)
}
}

View file

@ -8,7 +8,8 @@ import org.junit.Test
import se.scalablesolutions.akka.dispatch.Dispatchers
class EventBasedSingleThreadActorTest extends JUnitSuite {
import Actor._
import Actor.Sender.Self
private val unit = TimeUnit.MILLISECONDS
class TestActor extends Actor {

View file

@ -6,7 +6,8 @@ import org.scalatest.junit.JUnitSuite
import org.junit.Test
class EventBasedThreadPoolActorTest extends JUnitSuite {
import Actor._
import Actor.Sender.Self
private val unit = TimeUnit.MILLISECONDS
class TestActor extends Actor {

View file

@ -27,7 +27,8 @@ class RemoteActorSpecActorBidirectional extends Actor {
}
class RemoteActorTest extends JUnitSuite {
import Actor._
import Actor.Sender.Self
akka.Config.config
new Thread(new Runnable() {
def run = {

View file

@ -20,7 +20,8 @@ object Log {
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
class RemoteSupervisorTest extends JUnitSuite {
import Actor._
import Actor.Sender.Self
akka.Config.config
new Thread(new Runnable() {
def run = {

View file

@ -13,7 +13,8 @@ import org.junit.Test
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
class SupervisorTest extends JUnitSuite {
import Actor._
import Actor.Sender.Self
var messageLog: String = ""
var oneWayLog: String = ""

View file

@ -8,7 +8,8 @@ import org.junit.Test
import se.scalablesolutions.akka.dispatch.Dispatchers
class ThreadBasedActorTest extends JUnitSuite {
import Actor._
import Actor.Sender.Self
private val unit = TimeUnit.MILLISECONDS
class TestActor extends Actor {

View file

@ -31,10 +31,8 @@ import java.io.IOException
* val consumer = AMQP.newConsumer(params, hostname, port, exchange, ExchangeType.Direct, Serializer.ScalaJSON, None, 100)
*
* consumer ! MessageConsumerListener(queue, routingKey, new Actor() {
* def receive = {
* case Message(payload, _, _, _, _) => log.debug("Received message: %s", payload)
* }
* consumer ! MessageConsumerListener(queue, routingKey, actor {
* case Message(payload, _, _, _, _) => log.debug("Received message: %s", payload)
* })
*
* val producer = AMQP.newProducer(params, hostname, port, exchange, Serializer.ScalaJSON, None, None, 100)
@ -43,15 +41,113 @@ import java.io.IOException
*
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
object AMQP extends Actor {
private val connections = new ConcurrentHashMap[FaultTolerantConnectionActor, FaultTolerantConnectionActor]
faultHandler = Some(OneForOneStrategy(5, 5000))
trapExit = List(classOf[Throwable])
start
object AMQP {
private val supervisor = new AMQPSupervisor
def newProducer(
config: ConnectionParameters,
hostname: String,
port: Int,
exchangeName: String,
returnListener: Option[ReturnListener],
shutdownListener: Option[ShutdownListener],
initReconnectDelay: Long) =
supervisor.newProducer(
config, hostname, port, exchangeName, returnListener, shutdownListener, initReconnectDelay)
def newConsumer(
config: ConnectionParameters,
hostname: String,
port: Int,
exchangeName: String,
exchangeType: ExchangeType,
shutdownListener: Option[ShutdownListener],
initReconnectDelay: Long,
passive: Boolean,
durable: Boolean,
configurationArguments: Map[String, AnyRef]) =
supervisor.newConsumer(
config, hostname, port, exchangeName, exchangeType,
shutdownListener, initReconnectDelay, passive, durable, configurationArguments)
def stopConnection(connection: FaultTolerantConnectionActor) = supervisor.stopConnection(connection)
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
class AMQPSupervisor extends Actor {
private val connections = new ConcurrentHashMap[FaultTolerantConnectionActor, FaultTolerantConnectionActor]
faultHandler = Some(OneForOneStrategy(5, 5000))
trapExit = List(classOf[Throwable])
start
def newProducer(
config: ConnectionParameters,
hostname: String,
port: Int,
exchangeName: String,
returnListener: Option[ReturnListener],
shutdownListener: Option[ShutdownListener],
initReconnectDelay: Long): Producer = {
val producer = new Producer(
new ConnectionFactory(config),
hostname, port,
exchangeName,
returnListener,
shutdownListener,
initReconnectDelay)
startLink(producer)
producer
}
def newConsumer(
config: ConnectionParameters,
hostname: String,
port: Int,
exchangeName: String,
exchangeType: ExchangeType,
shutdownListener: Option[ShutdownListener],
initReconnectDelay: Long,
passive: Boolean,
durable: Boolean,
configurationArguments: Map[String, AnyRef]): Consumer = {
val consumer = new Consumer(
new ConnectionFactory(config),
hostname, port,
exchangeName,
exchangeType,
shutdownListener,
initReconnectDelay,
passive,
durable,
configurationArguments)
startLink(consumer)
consumer
}
def stopConnection(connection: FaultTolerantConnectionActor) = {
connection ! Stop
unlink(connection)
connections.remove(connection)
}
override def shutdown = {
connections.values.asScala.foreach(_ ! Stop)
exit
}
def receive = {
case _ => {} // ignore all messages
}
}
sealed trait AMQPMessage
private[akka] trait InternalAMQPMessage extends AMQPMessage
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
class Message(val payload: Array[Byte],
val routingKey: String,
val mandatory: Boolean,
@ -59,12 +155,15 @@ object AMQP extends Actor {
val properties: RabbitMQ.BasicProperties) extends AMQPMessage {
override def toString(): String =
"Message[payload=" + payload +
", routingKey=" + routingKey +
", mandatory=" + mandatory +
", immediate=" + immediate +
", properties=" + properties + "]"
", routingKey=" + routingKey +
", mandatory=" + mandatory +
", immediate=" + immediate +
", properties=" + properties + "]"
}
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
object Message {
def unapply(message: Message): Option[Tuple5[AnyRef, String, Boolean, Boolean, RabbitMQ.BasicProperties]] =
Some((message.payload, message.routingKey, message.mandatory, message.immediate, message.properties))
@ -76,28 +175,31 @@ object AMQP extends Actor {
new Message(payload, routingKey, false, false, null)
}
case class MessageConsumerListener(queueName: String,
routingKey: String,
isUsingExistingQueue: Boolean,
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
case class MessageConsumerListener(queueName: String,
routingKey: String,
isUsingExistingQueue: Boolean,
actor: Actor) extends AMQPMessage {
def this(queueName: String, routingKey: String, actor: Actor) = this(queueName, routingKey, false, actor)
def this(queueName: String, routingKey: String, actor: Actor) = this (queueName, routingKey, false, actor)
private[akka] var tag: Option[String] = None
override def toString() =
override def toString() =
"MessageConsumerListener[actor=" + actor +
", queue=" + queueName +
", routingKey=" + routingKey +
", tag=" + tag +
", isUsingExistingQueue=" + isUsingExistingQueue + "]"
", queue=" + queueName +
", routingKey=" + routingKey +
", tag=" + tag +
", isUsingExistingQueue=" + isUsingExistingQueue + "]"
def toString(exchangeName: String) =
def toString(exchangeName: String) =
"MessageConsumerListener[actor=" + actor +
", exchange=" + exchangeName +
", queue=" + queueName +
", routingKey=" + routingKey +
", tag=" + tag +
", isUsingExistingQueue=" + isUsingExistingQueue + "]"
", exchange=" + exchangeName +
", queue=" + queueName +
", routingKey=" + routingKey +
", tag=" + tag +
", isUsingExistingQueue=" + isUsingExistingQueue + "]"
/**
* Hash code should only be based on on queue name and routing key.
@ -114,31 +216,32 @@ object AMQP extends Actor {
*/
override def equals(that: Any): Boolean = synchronized {
that != null &&
that.isInstanceOf[MessageConsumerListener] &&
that.asInstanceOf[MessageConsumerListener].queueName== queueName &&
that.asInstanceOf[MessageConsumerListener].routingKey == routingKey
that.isInstanceOf[MessageConsumerListener] &&
that.asInstanceOf[MessageConsumerListener].queueName == queueName &&
that.asInstanceOf[MessageConsumerListener].routingKey == routingKey
}
}
object MessageConsumerListener {
def apply(queueName: String, routingKey: String, actor: Actor) = new MessageConsumerListener(queueName, routingKey, false, actor)
def apply(queueName: String, routingKey: String, actor: Actor) =
new MessageConsumerListener(queueName, routingKey, false, actor)
}
case object Stop extends AMQPMessage
private[akka] case class UnregisterMessageConsumerListener(consumer: MessageConsumerListener) extends InternalAMQPMessage
private[akka] case class Reconnect(delay: Long) extends InternalAMQPMessage
private[akka] case class Failure(cause: Throwable) extends InternalAMQPMessage
private[akka] class MessageNotDeliveredException(
val message: String,
val replyCode: Int,
val replyText: String,
val exchange: String,
val routingKey: String,
val properties: RabbitMQ.BasicProperties,
val body: Array[Byte]) extends RuntimeException(message)
val message: String,
val replyCode: Int,
val replyText: String,
val exchange: String,
val routingKey: String,
val properties: RabbitMQ.BasicProperties,
val body: Array[Byte]) extends RuntimeException(message)
sealed trait ExchangeType
object ExchangeType {
@ -156,84 +259,29 @@ object AMQP extends Actor {
}
}
def newProducer(
config: ConnectionParameters,
hostname: String,
port: Int,
exchangeName: String,
returnListener: Option[ReturnListener],
shutdownListener: Option[ShutdownListener],
initReconnectDelay: Long): Producer = {
val producer = new Producer(
new ConnectionFactory(config),
hostname, port,
exchangeName,
returnListener,
shutdownListener,
initReconnectDelay)
startLink(producer)
producer
}
def newConsumer(
config: ConnectionParameters,
hostname: String,
port: Int,
exchangeName: String,
exchangeType: ExchangeType,
shutdownListener: Option[ShutdownListener],
initReconnectDelay: Long,
passive: Boolean,
durable: Boolean,
configurationArguments: Map[String, AnyRef]): Consumer = {
val consumer = new Consumer(
new ConnectionFactory(config),
hostname, port,
exchangeName,
exchangeType,
shutdownListener,
initReconnectDelay,
passive,
durable,
configurationArguments)
startLink(consumer)
consumer
}
def stopConnection(connection: FaultTolerantConnectionActor) = {
connection ! Stop
unlink(connection)
connections.remove(connection)
}
override def shutdown = {
connections.values.asScala.foreach(_ ! Stop)
exit
}
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
class Producer private[amqp] (
val connectionFactory: ConnectionFactory,
val hostname: String,
val port: Int,
val exchangeName: String,
val returnListener: Option[ReturnListener],
val shutdownListener: Option[ShutdownListener],
val initReconnectDelay: Long)
extends FaultTolerantConnectionActor {
class Producer private[amqp](
val connectionFactory: ConnectionFactory,
val hostname: String,
val port: Int,
val exchangeName: String,
val returnListener: Option[ReturnListener],
val shutdownListener: Option[ShutdownListener],
val initReconnectDelay: Long)
extends FaultTolerantConnectionActor {
setupChannel
log.info("AMQP.Producer [%s] is started", toString)
def newRpcClient(routingKey: String): RpcClient = new RpcClient(channel, exchangeName, routingKey)
def receive = {
case message @ Message(payload, routingKey, mandatory, immediate, properties) =>
case message@Message(payload, routingKey, mandatory, immediate, properties) =>
log.debug("Sending message [%s]", message)
channel.basicPublish(exchangeName, routingKey, mandatory, immediate, properties, payload.asInstanceOf[Array[Byte]])
channel.basicPublish(
exchangeName, routingKey, mandatory, immediate, properties, payload.asInstanceOf[Array[Byte]])
case Stop =>
disconnect
exit
@ -246,18 +294,18 @@ object AMQP extends Actor {
case Some(listener) => channel.setReturnListener(listener)
case None => channel.setReturnListener(new ReturnListener() {
def handleBasicReturn(
replyCode: Int,
replyText: String,
exchange: String,
routingKey: String,
properties: RabbitMQ.BasicProperties,
body: Array[Byte]) = {
replyCode: Int,
replyText: String,
exchange: String,
routingKey: String,
properties: RabbitMQ.BasicProperties,
body: Array[Byte]) = {
throw new MessageNotDeliveredException(
"Could not deliver message [" + body +
"] with reply code [" + replyCode +
"] with reply text [" + replyText +
"] and routing key [" + routingKey +
"] to exchange [" + exchange + "]",
"] with reply code [" + replyCode +
"] with reply text [" + replyText +
"] and routing key [" + routingKey +
"] to exchange [" + exchange + "]",
replyCode, replyText, exchange, routingKey, properties, body)
}
})
@ -267,25 +315,26 @@ object AMQP extends Actor {
override def toString(): String =
"AMQP.Producer[hostname=" + hostname +
", port=" + port +
", exchange=" + exchangeName + "]"
", port=" + port +
", exchange=" + exchangeName + "]"
}
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
class Consumer private[amqp] (
val connectionFactory: ConnectionFactory,
val hostname: String,
val port: Int,
val exchangeName: String,
val exchangeType: ExchangeType,
val shutdownListener: Option[ShutdownListener],
val initReconnectDelay: Long,
val passive: Boolean,
val durable: Boolean,
val configurationArguments: Map[java.lang.String, Object])
extends FaultTolerantConnectionActor { consumer: Consumer =>
class Consumer private[amqp](
val connectionFactory: ConnectionFactory,
val hostname: String,
val port: Int,
val exchangeName: String,
val exchangeType: ExchangeType,
val shutdownListener: Option[ShutdownListener],
val initReconnectDelay: Long,
val passive: Boolean,
val durable: Boolean,
val configurationArguments: Map[java.lang.String, Object])
extends FaultTolerantConnectionActor {
consumer: Consumer =>
faultHandler = Some(OneForOneStrategy(5, 5000))
trapExit = List(classOf[Throwable])
@ -302,7 +351,7 @@ object AMQP extends Actor {
body(requestBody, replyProperties)
}
}
}
}
def receive = {
case listener: MessageConsumerListener =>
@ -312,32 +361,34 @@ object AMQP extends Actor {
case UnregisterMessageConsumerListener(listener) =>
unregisterListener(listener)
case Reconnect(delay) =>
case Reconnect(delay) =>
reconnect(delay)
case Failure(cause) =>
case Failure(cause) =>
log.error(cause, "")
throw cause
case Stop =>
case Stop =>
listeners.elements.toList.map(_._2).foreach(unregisterListener(_))
disconnect
exit
case message: Message =>
handleIllegalMessage("AMQP.Consumer [" + this + "] can't be used to send messages, ignoring message [" + message + "]")
case message: Message =>
handleIllegalMessage(
"AMQP.Consumer [" + this + "] can't be used to send messages, ignoring message [" + message + "]")
case unknown =>
handleIllegalMessage("Unknown message [" + unknown + "] to AMQP Consumer [" + this + "]")
case unknown =>
handleIllegalMessage(
"Unknown message [" + unknown + "] to AMQP Consumer [" + this + "]")
}
protected def setupChannel = {
connection = connectionFactory.newConnection(hostname, port)
channel = connection.createChannel
channel.exchangeDeclare(exchangeName.toString, exchangeType.toString,
passive, durable,
configurationArguments.asJava)
passive, durable,
configurationArguments.asJava)
listeners.elements.toList.map(_._2).foreach(registerListener)
if (shutdownListener.isDefined) connection.addShutdownListener(shutdownListener.get)
}
@ -352,12 +403,12 @@ object AMQP extends Actor {
}
log.debug("Binding new queue for MessageConsumerListener [%s]", listener.queueName)
channel.queueBind(listener.queueName, exchangeName, listener.routingKey)
channel.queueBind(listener.queueName, exchangeName, listener.routingKey)
val listenerTag = channel.basicConsume(listener.queueName, true, new DefaultConsumer(channel) with Logging {
override def handleDelivery(tag: String,
envelope: Envelope,
properties: RabbitMQ.BasicProperties,
override def handleDelivery(tag: String,
envelope: Envelope,
properties: RabbitMQ.BasicProperties,
payload: Array[Byte]) {
try {
val mandatory = false // FIXME: where to find out if it's mandatory?
@ -368,22 +419,27 @@ object AMQP extends Actor {
log.debug("Acking message with delivery tag [%s]", deliveryTag)
channel.basicAck(deliveryTag, false)
} catch {
case cause =>
log.error("Delivery of message to MessageConsumerListener [%s] failed due to [%s]", listener.toString(exchangeName), cause.toString)
case cause =>
log.error(
"Delivery of message to MessageConsumerListener [%s] failed due to [%s]",
listener.toString(exchangeName), cause.toString)
consumer ! Failure(cause) // pass on and re-throw exception in consumer actor to trigger restart and reconnect
}
}
override def handleShutdownSignal(listenerTag: String, signal: ShutdownSignalException) = {
def hasTag(listener: MessageConsumerListener, listenerTag: String): Boolean = {
if (listener.tag.isEmpty) throw new IllegalStateException("MessageConsumerListener [" + listener + "] does not have a tag")
if (listener.tag.isEmpty) throw new IllegalStateException(
"MessageConsumerListener [" + listener + "] does not have a tag")
listener.tag.get == listenerTag
}
listeners.elements.toList.map(_._2).find(hasTag(_, listenerTag)) match {
case None => log.error("Could not find message listener for tag [%s]; can't shut listener down", listenerTag)
case None => log.error(
"Could not find message listener for tag [%s]; can't shut listener down", listenerTag)
case Some(listener) =>
log.warning("MessageConsumerListener [%s] is being shutdown by [%s] due to [%s]",
listener.toString(exchangeName), signal.getReference, signal.getReason)
log.warning(
"MessageConsumerListener [%s] is being shutdown by [%s] due to [%s]",
listener.toString(exchangeName), signal.getReference, signal.getReason)
consumer ! UnregisterMessageConsumerListener(listener)
}
}
@ -393,11 +449,15 @@ object AMQP extends Actor {
private def unregisterListener(listener: MessageConsumerListener) = {
listeners.get(listener) match {
case None => log.warning("Can't unregister message consumer listener [%s]; no such listener", listener.toString(exchangeName))
case None => log.warning(
"Can't unregister message consumer listener [%s]; no such listener",
listener.toString(exchangeName))
case Some(listener) =>
listeners - listener
listener.tag match {
case None => log.warning("Can't unregister message consumer listener [%s]; no listener tag", listener.toString(exchangeName))
case None => log.warning(
"Can't unregister message consumer listener [%s]; no listener tag",
listener.toString(exchangeName))
case Some(tag) =>
channel.basicCancel(tag)
unlink(listener.actor)
@ -406,21 +466,24 @@ object AMQP extends Actor {
}
}
}
private def handleIllegalMessage(errorMessage: String) = {
log.error(errorMessage)
throw new IllegalArgumentException(errorMessage)
}
override def toString(): String =
"AMQP.Consumer[hostname=" + hostname +
", port=" + port +
", exchange=" + exchangeName +
", type=" + exchangeType +
", passive=" + passive +
", durable=" + durable + "]"
", port=" + port +
", exchange=" + exchangeName +
", type=" + exchangeType +
", passive=" + passive +
", durable=" + durable + "]"
}
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
trait FaultTolerantConnectionActor extends Actor {
val reconnectionTimer = new Timer
@ -435,29 +498,32 @@ object AMQP extends Actor {
protected def setupChannel
def createQueue: String = channel.queueDeclare("", false, false, true, true, null).getQueue
def createQueue: String =
channel.queueDeclare("", false, false, true, true, null).getQueue
def createQueue(name: String) = channel.queueDeclare(name, false, false, true, true, null).getQueue
def createQueue(name: String) =
channel.queueDeclare(name, false, false, true, true, null).getQueue
def createQueue(name: String, durable: Boolean) = channel.queueDeclare(name, false, durable, true, true, null).getQueue
def createQueue(name: String, durable: Boolean) =
channel.queueDeclare(name, false, durable, true, true, null).getQueue
def createBindQueue: String = {
def createBindQueue: String = {
val name = createQueue
channel.queueBind(name, exchangeName, name)
name
}
def createBindQueue(name: String) {
def createBindQueue(name: String) {
createQueue(name)
channel.queueBind(name, exchangeName, name)
}
def createBindQueue(name: String, durable: Boolean) {
def createBindQueue(name: String, durable: Boolean) {
channel.queueDeclare(name, durable)
channel.queueBind(name, exchangeName, name)
}
def deleteQueue(name: String) { channel.queueDelete(name) }
def deleteQueue(name: String) {channel.queueDelete(name)}
protected def disconnect = {
try {
@ -492,10 +558,7 @@ object AMQP extends Actor {
}
override def preRestart(reason: AnyRef, config: Option[AnyRef]) = disconnect
override def postRestart(reason: AnyRef, config: Option[AnyRef]) = reconnect(initReconnectDelay)
}
def receive = {
case _ => {} // ignore all messages
}
}

View file

@ -5,6 +5,7 @@
package se.scalablesolutions.akka.amqp
import se.scalablesolutions.akka.actor.Actor
import se.scalablesolutions.akka.actor.Actor.Sender.Self
import com.rabbitmq.client.ConnectionParameters

View file

@ -1,6 +1,6 @@
package se.scalablesolutions.akka
import akka.state.{MongoStorageSpec, MongoPersistentActorSpec, CassandraPersistentActorSpec}
import se.scalablesolutions.akka.state.{MongoStorageSpec, MongoPersistentActorSpec, CassandraPersistentActorSpec}
import junit.framework.Test
import junit.framework.TestCase
import junit.framework.TestSuite

View file

@ -18,10 +18,12 @@ see http://maven.apache.org/plugins/maven-changes-plugin/usage.html for full gui
<document>
<properties>
<title>Akka Release Notes</title>
<author></author>
<author>Jonas Bon&#233;r</author>
</properties>
<body>
<release version="0.6" date="" description="">
<release version="0.6" date="2009-12-14" description="
The goal with the 0.6 release is to bring together and harden all the new features, modules etc.
that have been developed since mid-summer 2009 into a production quality release.">
<action dev="Debasish Ghosh" type="add">MongoDB as Akka storage backend </action>
<action dev="Debasish Ghosh" type="add">Transparent JSON serialization of Scala objects based on SJSON </action>
<action dev="Debasish Ghosh" type="add">MongoDB backed actor example</action>
@ -30,17 +32,17 @@ see http://maven.apache.org/plugins/maven-changes-plugin/usage.html for full gui
<action dev="Viktor Klang" type="add">Support for using Scala XML tags in RESTful Actors (scala-jersey)</action>
<action dev="Viktor Klang" type="add">Support for Comet Actors using Atmosphere</action>
<action dev="Eckhart Hertzler" type="add">Kerberos/SPNEGO support for Security module</action>
<action dev="Jonas Bon&#233;r" type="add">AMQP integration; abstracted as actors in a supervisor hierarchy. Impl AMQP 0.9.1</action>
<action dev="Jonas Bon&#233;r" type="add">Rewritten STM, now integrated with Multiverse STM</action>
<action dev="Jonas Bon&#233;r" type="add">Added STM API for atomic {..} and run {..} orElse {..}</action>
<action dev="Jonas Bon&#233;r" type="add">Added STM retry</action>
<action dev="Jonas Bon&#233;r" type="add">Complete rewrite of the persistence transaction management, now based on Unit of Work and Multiverse STM</action>
<action dev="Jonas Bon&#233;r" type="add">Monadic API to TransactionalRef (use it in for-comprehension)</action>
<action dev="Jonas Bon&#233;r" type="add">Lightweight actor syntax 'actor { case _ => .. }'</action>
<action dev="Jonas Bon&#233;r" type="add">Lightweight actor syntax using one of the Actor.actor(..) methods. F.e: 'actor { case _ => .. }'</action>
<action dev="Jonas Bon&#233;r" type="add">New Scala JSON parser based on sjson</action>
<action dev="Jonas Bon&#233;r" type="add">Upgraded to Netty 3.2 and Protobuf 2.2</action>
<action dev="Jonas Bon&#233;r" type="add">Added zlib compression to remote actors</action>
<action dev="Jonas Bon&#233;r" type="add">Added implicit sender reference for fire-forget ('!') message sends</action>
<action dev="Jonas Bon&#233;r" type="add">Monadic API to TransactionalRef (use it in for-comprehension)</action>
<action dev="Jonas Bon&#233;r" type="add">Smoother web app integration; just add akka.conf to WEB-INF/classes, no need for AKKA_HOME</action>
<action dev="Jonas Bon&#233;r" type="add">Smoother web app integration; just add akka.conf to the classpath (WEB-INF/classes), no need for AKKA_HOME or -Dakka.conf=..</action>
<action dev="Jonas Bon&#233;r" type="add">Modularization of distribution into a thin core (actors, remoting and STM) and the rest in submodules</action>
<action dev="Jonas Bon&#233;r" type="add">JSON serialization for Java objects (using Jackson)</action>
<action dev="Jonas Bon&#233;r" type="add">JSON serialization for Scala objects (using SJSON)</action>
@ -48,22 +50,29 @@ see http://maven.apache.org/plugins/maven-changes-plugin/usage.html for full gui
<action dev="Jonas Bon&#233;r" type="add">Protobuf serialization for Java and Scala objects</action>
<action dev="Jonas Bon&#233;r" type="add">SBinary serialization for Scala objects</action>
<action dev="Jonas Bon&#233;r" type="add">Protobuf as remote protocol</action>
<action dev="Jonas Bon&#233;r" type="add">AMQP integration; abstracted as actors in a supervisor hierarchy. Impl AMQP 0.9.1</action>
<action dev="Jonas Bon&#233;r" type="add">Updated Cassandra integration and CassandraSession API to v0.4</action>
<action dev="Jonas Bon&#233;r" type="add">Added CassandraSession API (with socket pooling) wrapping Cassandra's Thrift API in Scala and Java APIs</action>
<action dev="Jonas Bon&#233;r" type="add">CassandraStorage is now works with external Cassandra cluster</action>
<action dev="Jonas Bon&#233;r" type="remove">Removed embedded Cassandra mode</action>
<action dev="Jonas Bon&#233;r" type="remove">Removed startup scripts and lib dir</action>
<action dev="Jonas Bon&#233;r" type="remove">Removed 'Transient' Actors and restart timeout</action>
<action dev="Jonas Bon&#233;r" type="add">ActorRegistry for retrieving Actor instances by class name and by id</action>
<action dev="Jonas Bon&#233;r" type="add">SchedulerActor for scheduling periodic tasks</action>
<action dev="Jonas Bon&#233;r" type="add">Now start up kernel with 'java -jar dist/akka-0.6.jar'</action>
<action dev="Jonas Bon&#233;r" type="fix">Concurrent mode is now per actor basis</action>
<action dev="Jonas Bon&#233;r" type="fix">Fixed dispatcher bug</action>
<action dev="Jonas Bon&#233;r" type="fix">Cleaned up Maven scripts and distribution in general</action>
<action dev="Jonas Bon&#233;r" type="add">Added mailing list: akka-user@googlegroups.com</action>
<action dev="Jonas Bon&#233;r" type="add">Improved and restructured documentation</action>
<action dev="Jonas Bon&#233;r" type="add">New URL: http://akkasource.org</action>
<action dev="Jonas Bon&#233;r" type="add">Fixed many many bugs and minor issues</action>
<action dev="Jonas Bon&#233;r" type="add">Enhanced trapping of failures: 'trapExit = List(classOf[..], classOf[..])'</action>
<action dev="Jonas Bon&#233;r" type="add">Upgraded to Netty 3.2, Protobuf 2.2, ScalaTest 1.0, Jersey 1.1.3, Atmosphere 0.4.1, Cassandra 0.4.1, Configgy 1.4</action>
<action dev="Jonas Bon&#233;r" type="fix">Concurrent mode is now per actor basis</action>
<action dev="Jonas Bon&#233;r" type="fix">Remote actors are now defined by their UUID (not class name)</action>
<action dev="Jonas Bon&#233;r" type="fix">Fixed dispatcher bug</action>
<action dev="Jonas Bon&#233;r" type="fix">Cleaned up Maven scripts and distribution in general</action>
<action dev="Jonas Bon&#233;r" type="fix">Fixed many many bugs and minor issues</action>
<action dev="Jonas Bon&#233;r" type="fix">Fixed inconsistencies and uglyness in Actors API</action>
<action dev="Jonas Bon&#233;r" type="remove">Removed embedded Cassandra mode</action>
<action dev="Jonas Bon&#233;r" type="remove">Removed the !? method in Actor (synchronous message send, since it's evil. Use !! with time-out instead.</action>
<action dev="Jonas Bon&#233;r" type="remove">Removed startup scripts and lib dir</action>
<action dev="Jonas Bon&#233;r" type="remove">Removed the 'Transient' life-cycle scope since to close to 'Temporary' in semantics.</action>
<action dev="Jonas Bon&#233;r" type="remove">Removed 'Transient' Actors and restart timeout</action>
</release>
<release version="0.5" date="2009-07-12" description="First public release" />
</body>