pekko/akka-actor/src/main/scala/akka/actor/Channel.scala
2011-08-26 17:25:18 +02:00

161 lines
4.6 KiB
Scala

/**
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
/*
* This package is just used to hide the tryTell method from the Scala parts:
* it will be public to javac's eyes by virtue of §5.2 of the SLS.
*/
package japi {
trait Channel[-T] { self: akka.actor.Channel[T]
private[japi] def tryTell(msg: T): Boolean = {
try {
self.!(msg)(NullChannel)
true
} catch {
case _: Exception false
}
}
}
}
/**
* Abstraction for unification of sender and senderFuture for later reply.
* Can be stored away and used at a later point in time.
*
* The possible reply channel which can be passed into ! and tryTell is always
* untyped, as there is no way to utilize its real static type without
* requiring runtime-costly manifests.
*/
trait Channel[-T] extends japi.Channel[T] {
/**
* Scala API. <p/>
* Sends the specified message to the channel.
*/
def !(msg: T)(implicit sender: UntypedChannel): Unit
/**
* Scala and Java API. <p/>
* Try to send the specified message to the channel, i.e. fire-and-forget
* semantics, including the sender reference if possible (not supported on
* all channels).<p/>
* From Java:
* <pre>
* actor.tryTell(message);
* actor.tryTell(message, context);
* </pre>
* <p/>
* From Scala:
* <pre>
* actor tryTell message
* actor.tryTell(message)(sender)
* </pre>
*/
def tryTell(msg: T)(implicit sender: UntypedChannel): Boolean = {
try {
this.!(msg)(sender)
true
} catch {
case _: Exception false
}
}
/**
* Try to send an exception. Not all channel types support this, one notable
* positive example is Future. Failure to send is silent.
*
* @return whether sending was successful
*/
def sendException(ex: Throwable): Boolean = false
/**
* Sends the specified message to the channel, i.e. fire-and-forget semantics.<p/>
* <pre>
* actor.tell(message);
* </pre>
*/
def tell(msg: T): Unit = this.!(msg)
/**
* Java API. <p/>
* Sends the specified message to the channel, i.e. fire-and-forget
* semantics, including the sender reference if possible (not supported on
* all channels).<p/>
* <pre>
* actor.tell(message, context);
* </pre>
*/
def tell(msg: T, sender: UntypedChannel): Unit = this.!(msg)(sender)
}
/**
* This trait marks a channel that a priori does have sending capability,
* i.e. ! is not guaranteed to fail (e.g. NullChannel would be a
* counter-example).
*/
trait AvailableChannel[-T] extends Channel[T]
/**
* This trait marks a channel which is capable of sending exceptions.
*/
trait ExceptionChannel[-T] extends AvailableChannel[T]
/**
* This trait marks a channel which carries reply information when tell()ing.
*/
trait ReplyChannel[-T] extends AvailableChannel[T]
/**
* All channels used in conjunction with MessageInvocation are untyped by
* design, so make this explicit.
*/
trait UntypedChannel extends Channel[Any]
object UntypedChannel {
implicit def senderOption2Channel(sender: Option[ActorRef]): UntypedChannel =
sender match {
case Some(actor) actor
case None NullChannel
}
implicit final val default: UntypedChannel = NullChannel
}
/**
* Default channel when none available.
*/
case object NullChannel extends UntypedChannel {
def !(msg: Any)(implicit channel: UntypedChannel) {
throw new IllegalActorStateException("""
No sender in scope, can't reply.
You have probably:
1. Sent a message to an Actor from an instance that is NOT an Actor.
2. Invoked a method on an TypedActor from an instance NOT an TypedActor.
You may want to have a look at tryTell for a variant returning a Boolean""")
}
override def tryTell(msg: Any)(implicit channel: UntypedChannel): Boolean = false
}
/**
* A channel which may be forwarded: a message received with such a reply
* channel attached can be passed on transparently such that a reply from a
* later processing stage is sent directly back to the origin. Keep in mind
* that not all channels can be used multiple times.
*/
trait ForwardableChannel extends UntypedChannel with AvailableChannel[Any] {
/**
* Get channel by which this channel would reply (ActorRef.forward takes an
* implicit ForwardableChannel and uses its .channel as message origin)
*/
def channel: UntypedChannel
}
object ForwardableChannel {
implicit def someS2FC(sender: Some[SelfActorRef]): ForwardableChannel = sender.get
implicit def someIS2FC(implicit sender: Some[SelfActorRef]): ForwardableChannel = sender.get
}