diff --git a/akka-channels-tests/src/test/scala/akka/channels/ChannelSpec.scala b/akka-channels-tests/src/test/scala/akka/channels/ChannelSpec.scala index fbc7df33bf..c7828e50a6 100644 --- a/akka-channels-tests/src/test/scala/akka/channels/ChannelSpec.scala +++ b/akka-channels-tests/src/test/scala/akka/channels/ChannelSpec.scala @@ -1,5 +1,5 @@ /** - * Copyright (C) 2009-2012 Typesafe Inc. + * Copyright (C) 2009-2013 Typesafe Inc. */ package akka.channels diff --git a/akka-docs/rst/scala/code/docs/channels/ChannelDocSpec.scala b/akka-docs/rst/scala/code/docs/channels/ChannelDocSpec.scala new file mode 100644 index 0000000000..8d1553a72d --- /dev/null +++ b/akka-docs/rst/scala/code/docs/channels/ChannelDocSpec.scala @@ -0,0 +1,166 @@ +/** + * Copyright (C) 2009-2013 Typesafe Inc. + */ + +package docs.channels + +import akka.testkit.AkkaSpec +import akka.channels._ +import akka.actor.Actor +import scala.concurrent.forkjoin.ThreadLocalRandom +import scala.concurrent.Future +import scala.concurrent.duration._ +import akka.util.Timeout +import akka.testkit.TestProbe + +class ChannelDocSpec extends AkkaSpec { + + //#motivation0 + trait Request + case class Command(msg: String) extends Request + + trait Reply + case object CommandSuccess extends Reply + case class CommandFailure(msg: String) extends Reply + //#motivation0 + + class A + class B + class C + class D + + "demonstrate why Typed Channels" in { + def someActor = testActor + //#motivation1 + val requestProcessor = someActor + requestProcessor ! Command + //#motivation1 + expectMsg(Command) + + /* + //#motivation2 + val requestProcessor = new ChannelRef[(Request, Reply) :+: TNil](someActor) + requestProcessor <-!- Command // this does not compile + //#motivation2 + */ + + type Example = // + //#motivation-types + (A, B) :+: (C, D) :+: TNil + //#motivation-types + } + + // only compile test + "demonstrate channels creation" ignore { + //#declaring-channels + class AC extends Actor with Channels[TNil, (Request, Reply) :+: TNil] { + channel[Request] { (req, snd) ⇒ + req match { + case Command("ping") ⇒ snd <-!- CommandSuccess + case _ ⇒ + } + } + } + //#declaring-channels + + //#declaring-subchannels + class ACSub extends Actor with Channels[TNil, (Request, Reply) :+: TNil] { + channel[Command] { (cmd, snd) ⇒ snd <-!- CommandSuccess } + channel[Request] { (req, snd) ⇒ + if (ThreadLocalRandom.current.nextBoolean) snd <-!- CommandSuccess + else snd <-!- CommandFailure("no luck") + } + } + //#declaring-subchannels + } + + // only compile test + "demonstrating message sending" ignore { + import scala.concurrent.ExecutionContext.Implicits.global + //#sending + implicit val dummySender: ChannelRef[(Any, Nothing) :+: TNil] = ??? + implicit val timeout: Timeout = ??? // for the ask operations + + val channelA: ChannelRef[(A, B) :+: TNil] = ??? + val channelB: ChannelRef[(B, C) :+: TNil] = ??? + val channelC: ChannelRef[(C, D) :+: TNil] = ??? + + val a = new A + val fA = Future { new A } + + channelA <-!- a // send a to channelA + a -!-> channelA // same thing as above + + //channelA <-!- fA // eventually send the future’s value to channelA + fA -!-> channelA // same thing as above + + // ask the actor; return type given in full for illustration + val fB: Future[WrappedMessage[(B, Nothing) :+: TNil, B]] = channelA <-?- a + val fBunwrapped: Future[B] = fB.lub + + a -?-> channelA // same thing as above + + //channelA <-?- fA // eventually ask the actor, return the future + fA -?-> channelA // same thing as above + + // chaining works as well + a -?-> channelA -?-> channelB -!-> channelC + //#sending + } + + "demonstrate message forwarding" in { + //#forwarding + import scala.reflect.runtime.universe.TypeTag + + class Latch[T1: TypeTag, T2: TypeTag](target: ChannelRef[(T1, T2) :+: TNil]) + extends Actor with Channels[TNil, (Request, Reply) :+: (T1, T2) :+: TNil] { + + implicit val timeout = Timeout(5.seconds) + + //#become + channel[Request] { + + case (Command("close"), snd) ⇒ + channel[T1] { (t, s) ⇒ t -?-> target -!-> s } + snd <-!- CommandSuccess + + case (Command("open"), snd) ⇒ + channel[T1] { (_, _) ⇒ } + snd <-!- CommandSuccess + } + + //#become + channel[T1] { (t, snd) ⇒ t -?-> target -!-> snd } + } + //#forwarding + + val probe = TestProbe() + val _target = new ChannelRef[(String, Int) :+: TNil](probe.ref) + val _self = new ChannelRef[(Any, Nothing) :+: TNil](testActor) + //#usage + implicit val selfChannel: ChannelRef[(Any, Nothing) :+: TNil] = _self + val target: ChannelRef[(String, Int) :+: TNil] = _target // some actor + + // type given just for demonstration purposes + val latch: ChannelRef[(Request, Reply) :+: (String, Int) :+: TNil] = + ChannelExt(system).actorOf(new Latch(target)) + + "hello" -!-> latch + //#processing + probe.expectMsg("hello") + probe.reply(5) + //#processing + expectMsg(5) // this is a TestKit-based example + + Command("open") -!-> latch + expectMsg(CommandSuccess) + + "world" -!-> latch + //#processing + probe.expectNoMsg(500.millis) + //#processing + expectNoMsg(500.millis) + //#usage + } + +} \ No newline at end of file diff --git a/akka-docs/rst/scala/index.rst b/akka-docs/rst/scala/index.rst index ddceb9fcf8..c0ad9fe985 100644 --- a/akka-docs/rst/scala/index.rst +++ b/akka-docs/rst/scala/index.rst @@ -8,6 +8,7 @@ Scala API actors typed-actors + typed-channels logging event-bus scheduler diff --git a/akka-docs/rst/scala/typed-channels.rst b/akka-docs/rst/scala/typed-channels.rst new file mode 100644 index 0000000000..d998c216ca --- /dev/null +++ b/akka-docs/rst/scala/typed-channels.rst @@ -0,0 +1,471 @@ +.. _typed-channels: + +############## +Typed Channels +############## + +Motivation +========== + +Actors derive great strength from their strong encapsulation, which enables +internal restarts as well as changing behavior and also composition. The last +one is enabled by being able to inject an actor into a message exchange +transparently, because all either side ever sees is an :class:`ActorRef`. The +straight-forward way to implement this encapsulation is to keep the actor +references untyped, and before the advent of macros in Scala 2.10 this was the +only tractable way. + +As a motivation for change consider the following simple example: + +.. includecode:: code/docs/channels/ChannelDocSpec.scala#motivation0 + +.. includecode:: code/docs/channels/ChannelDocSpec.scala#motivation1 + +This is an error which is quite common, and the reason is that the compiler +does not catch it and cannot warn about it. Now if there were some type +restrictions on which messages the ``commandProcessor`` can process, that would +be a different story: + +.. includecode:: code/docs/channels/ChannelDocSpec.scala#motivation2 + +The :class:`ChannelRef` wraps a normal untyped :class:`ActorRef`, but it +expresses a type constraint, namely that this channel accepts only messages of +type :class:`Request`, to which it may reply with messages of type +:class:`Reply`. The types do not express any guarantees on how many messages +will be exchanged, whether they will be received or processed, or whether a +reply will actually be sent. They only restrict those actions which are known +to be doomed already at compile time. In this case the second line would flag +an error, since the companion object ``Command`` is not an instance of type +:class:`Request`. + +While this example looks pretty simple, the implications are profound. In order +to be useful, the system must be as reliable as you would expect a type system +to be. This means that unless you step outside of the it (i.e. doing the +equivalent of ``.asInstanceOf[_]``) you shall be protected, failures shall be +recognized and flagged. There are a number of challenges included in this +requirement, which are discussed in the following sections. If you are reading +this chapter for the first time and are not currently interested in exactly why +things are as they are, you may skip ahead to `Terminology`_. + +The Type Pollution Problem +-------------------------- + +What if an actor accepts two different types of messages? It might be a main +communications channel which is forwarded to worker actors for performing some +long-running and/or dangerous task, plus an administrative channel for the +routing of requests. Or it might be a generic message throttler which accepts a +generic channel for passing it through (which delay where appropriate) and a +management channel for setting the throttling rate. In the second case it is +especially easy to see that those two channels will probably not be related, +their types will not be derived from a meaningful common supertype; instead the +least upper bound will probably be :class:`AnyRef`. If a typed channel +reference only had the capability to express a single type, this type would +then be no restriction anymore. + +One solution to this is to never expose references describe more than one +channel at a time. But where would these references come from? It would be very +difficult to make this construction process type-safe, and it would also be an +inconvenient restriction, since message ordering guarantees only apply for the +same sender–receive pair, and if there are relations between the messages sent +on multiple channels those would need more boilerplate code to realize than if +all interaction were possible through a single reference. + +The other solution thus is to express multiple channel types by a single +channel reference, which requires the implementation of type lists and +computations on these. And as we will see below it also requires the +specification of possibly multiple reply channels per input type, hence a type +map. The implementation chosen uses type lists like this: + +.. includecode:: code/docs/channels/ChannelDocSpec.scala#motivation-types + +This type expresses two channels: type ``A`` may stimulate replies of type +``B``, while type ``C`` may evoke replies of type ``D``. The type operator +``:+:`` is a binary type which form a list of these channel definitions, and +like every good list it ends with an empty terminator ``TNil``. + +The Reply Problem +----------------- + +Akka actors have the power to reply to any message they receive, which is also +a message send and shall also be covered by typed channels. Since the sending +actor is the one which will also receive the reply, this needs to be verified. +The solution to this problem is that in addition to the ``self`` reference, +which is implicitly picked up as the sender for untyped actor interactions, +there is also a ``selfChannel`` which describes the typed channels handled by +this actor. Thus at the call site of the message send it must be verified that +this actor can actually handle the reply for that given message send. + +The Sender Ping-Pong Problem +---------------------------- + +After successfully sending a message to an actor over a typed channel, that +actor will have a reference to the message’s sender, because normal Akka +message processing rules apply. For this sender reference there must exist a +typed channel reference which describes the possible reply types which are +applicable for each of the incoming message channels. We will see below how +this reference is provided in the code, the problem we want to highlight here +is a different one: the nature of any sender reference is that it is highly +dynamic, the compiler cannot possibly know who sent the message we are +currently processing. + +But this does not mean that all hope is lost: the solution is to do *all* +type-checking at the call site of the message send. The receiving actor just +needs to declare its channel descriptions in its own type, and channel +references are derived at construction from this type (implying the existence +of a typed ``actorOf``). Then the actor knows for each received message type +which the allowed reply types are. The typed channel for the sender reference +hence has the reply types for the current input channel as its own input types, +but what should the reply types be? This is the ping-pong problem: + +* ActorA sends MsgA to ActorB + +* ActorB replies with MsgB + +* ActorA replies with MsgC + +Every “reply” uses the sender channel, which is dynamic and hence only known +partially. But ActorB did not know who sent the message it just replied to and +hence it cannot check that it can process the possible replies following that +message send. Only ActorA could have known, because it knows its own channels +as well as ActorB’s channels completely. The solution is thus to recursively +verify the message send, following all reply channels until all possible +message types to be sent have been verified. This sounds horribly complex, but +the algorithm for doing so actually has a worst-case complexity of O(N) where N +is the number of input channels of ActorA or ActorB, whoever has fewer. + +The Parent Problem +------------------ + +There is one other actor reference which is available to ever actor: its +parent. Since the child–parent relationship is established permanently when the +child is created by the parent, this problem is easily solvable by encoding the +requirements of the child for its parent channel in its type signature having +the typed variant of ``actorOf`` verify this against the ``selfChannel``. + +Anecdotally, since the guardian actor does not care at all about message sent +to it, top-level type channel actors must declare their parent channel to be +empty. + +The Exposure/Restriction Problem +-------------------------------- + +An actor may provide more than one service, either itself or by proxy, each +with their own set of channels. Only having references for the full set of +channels leads to a too wide spread of capabilities: in the example of the +message rate throttling actor its management channel is only meant to be used +by the actor which inserted it, not by the two actors between it was inserted. +Hence the manager will have to create a channel reference which excludes the +management channels before handing out the reference to other actors. + +Another variant of this problem is an actor which handles a channel whose input +type is a supertype for a number of derived channels. It should be allowed to +use the “superchannel” in place of any of the subchannels, but not the other +way around. The intuitive approach would be to model this by making the channel +reference contravariant in its channel types and define those channel types +accordingly. This does not work nicely, however, because Scala’s type system is +not well-suited to modeling such calculations on unordered type lists; it might +be possible but its implementation would be forbiddingly complex. + +Therefore this topic gained traction as macros became available: being able to +write down type calculations using standard collections and their +transformations reduces the implementation to a handful of lines. The “narrow” +operation implemented this way allows all narrowing of input channels and +widening of output channels down to ``(Nothing, Any)`` (which is to say: +removal). + +The Forwarding Problem +---------------------- + +One important feature of actors mentioned above is their composability which is +enabled by being able to forward or delegate messages. It is the nature of this +process that the sending party is not aware of the true destination of the +message, it only sees the façade in front of it. Above we have seen that the +sender ping-pong problem requires all verification to be performed at the +sender’s end, but if the sender does not know the final recipient, how can it +check that the message exchange is type-safe? + +The forwarding party—the middle-man—is also not in the position to make this +call, since all it has is the incomplete sender channel which is lacking reply +type information. The problem which arises lies precisely in these reply +sequences: the ping-pong scheme was verified against the middle-man, and if the +final recipient would reply to the forwarded request, that sender reference +would belong to a different channel and there is no single location in the +source code where all these pieces are known at compile time. + +The solution to this problem is not to allow forwarding in the normal untyped +:class:`ActorRef` sense. Replies must always be sent by the recipient of the +original message in order for the type checks at the sender site to be +effective. Since forwarding is an important communication pattern among actors, +support for it is thus provided in the form of the :meth:`ask` pattern combined +with the :meth:`pipe` pattern, which both are not add-ons but fully integrated +operations among typed channels. + +The JVM Erasure Problem +----------------------- + +When an actor with typed channels receives a message, this message needs to be +dispatched internally to the right channel, so that the right sender channel +can be presented and so on. This dispatch needs to work with the information +contained in the message, which due to the erasure of generic type information +is an incomplete image of the true channel types. Those full types exist only +at compile-time and reifying them into TypeTags at runtime for every message +send would be prohibitively expensive. This means that channels which erase to +the same JVM type cannot coexist within the same actor, message would not be +routable reliably in that case. + +The Actor Lookup Problem +------------------------ + +Everything up to this point has assumed that channel references are passed from +their point of creation to their point of use directly and in the regime of +strong, unerased types. This can also happen between actors by embedding them +in case classes with proper type information. But one particular useful feature +of Akka actors is that they have a stable identity by which they can be found, +a unique name. This name is represented as a :class:`String` and naturally does +not bear any type information concerning the actor’s channels. Thus, when +looking up an actor with ``system.actorFor(...)`` you will only get an untyped +:class:`ActorRef` and not a channel reference. This :class:`ActorRef` can of +course manually be wrapped in a channel reference bearing the desired channels, +but this is not a type-safe operation. + +The solution in this case must be a runtime check. There is an operation to +“narrow” an :class:`ActorRef` to a channel reference of given type, which +behind the scenes will send a message to the designated actor with a TypeTag +representing the requested channels. The actor will check these against its own +TypeTag and reply with the verification result. This check uses the same code +as the compile-time “narrow” operation introduced above. + +Terminology +=========== + +.. describe:: type Channel[I, O] = (I, O) + + A Channel is a pair of an input type and and output type. The input type is + the type of message accepted by the channel, the output type is the possible + reply type and may be ``Nothing`` to signify that no reply is sent. The input + type cannot be ``Nothing``. + +.. describe:: type ChannelList + + A ChannelList is an ordered collection of Channels, without further + restriction on the input or output types of these. This means that a single + input type may be associated with multiple output types within the same + ChannelList. + +.. describe:: type TNil <: ChannelList + + The empty ChannelList. + +.. describe:: type :+:[Channel, ChannelList] <: ChannelList + + This binary type constructor is used to build up lists of Channels, for which + infix notation will be most convenient: + + .. includecode:: code/docs/channels/ChannelDocSpec.scala#motivation-types + +.. describe:: class ChannelRef[T <: ChannelList] + + A ChannelRef is what is referred to above as the channel reference, it bears + the ChannelList which describes all input and output types and their relation + for the referenced actor. It also contains the underlying :class:`ActorRef`. + +.. describe:: trait Channels[P <: ChannelList, C <: ChannelList] + + A mixin for the :class:`Actor` trait which is parameterized in the channel + requirements this actor has for its parent (P) and its selfChannel (C). + +.. describe:: selfChannel + + An ``Actor with Channels[P, C]`` has a ``selfChannel`` of type + ``ChannelRef[C]``. This is the same type of channel reference which is + obtained by creating an instance of this actor. + +.. describe:: type ReplyChannels[T <: ChannelList] <: ChannelList + + Within an ``Actor with Channels[_, _]`` which takes a fully generic channel, + i.e. a type argument ``T <: ChannelList`` which is part of its selfChannel + type, this channel’s reply types are not known. The definition of this + channel uses the ReplyChannels type to abstractly refer to this unknown set + of channels in order to forward a reply from a ``ChannelRef[T]`` back to the + original sender. This operation’s type-safety is ensured at the sender’s site + by way of the ping-pong analysis described above. + +.. describe:: class WrappedMessage[T <: ChannelList, LUB] + + Scala’s type system cannot directly express type unions. Asking an actor with + a given input type may result in multiple possible reply types, hence the + :class:`Future` holding this reply will contain the value wrapped inside a + container which carries this type (only at compile-time). The type parameter + LUB is the least upper bound of all input channels contained in the + ChannelList T. + +Sending Messages across Channels +================================ + +Sending messages is best demonstrated in a quick overview of the basic operations: + +.. includecode:: code/docs/channels/ChannelDocSpec.scala#sending + +The first line is included so that the code compiles, since all message sends +including ``!`` will check the implicitly found selfChannel for compatibility +with the target channel’s reply types. In this case we want to demonstrate just +the syntax of sending, hence the dummy sender which accepts everything and +replies never. + +Presupposing three channel references of chainable types, an input value ``a`` +and a Future holding such a value, we demonstrate the two basic operations +which are well known from untyped actors: tell/! and ask/?. The type of the +Future returned by the ask operation may seem surprising at first, but as the +last line demonstrates, it is built in a way which makes building actor chains +very simple. What the last line does is the following: + +* it asks channelA, which returns a Future + +* a callback is installed on the Future which will use the reply value of + channelA and ask channelB with it, returning another Future + +* a callback is installed on that Future to send the reply value of channelB to + channelC, returning a Future with that previously sent value (using ``andThen``) + +This example also motivates the introduction of the “turned-around” syntax +where messages flow more naturally from left to right, instead of the standard +object-oriented view of having the tell method operate on the ActorRef given to +the left. + +This example informally introduced what is more precisely specified in the +following subsection. + +The Rules +--------- + +Operations on typed channels are composable and obey a few simple rules: + +* the message to be sent can be one of three things: + + * a :class:`Future[_]`, in which case the contained value will be sent once + available; the value will be unwrapped if it is a :class:`WrappedMessage[_, _]` + + * a :class:`WrappedMessage[_, _]`, which will be unwrapped (i.e. only the + value is sent) + + * everything else is sent as is + +* the operators are fully symmetric, i.e. ``-!->`` and ``<-!-`` do the same + thing provided the arguments also switch places + +* sending with ``-?->`` or ``<-?-`` always returns a + ``Future[WrappedMessage[_, _]]`` representing all possible reply channels, + even if there is only one (use ``.lub`` to get a :class:`Future[_]` with the + most precise single type for the value) + +* sending a :class:`Future[_]` with ``-!->`` or ``<-!-`` returns a new + :class:`Future[_]` which will be completed with the value after it has been + sent; sending a strict value returns that value + +Declaring an Actor with Channels +================================ + +The declaration of an Actor with Channels is done like this: + +.. includecode:: code/docs/channels/ChannelDocSpec.scala#declaring-channels + +It should be noted that it is impossible to declare channels which are not part +of the channel list given as the second type argument to the :class:`Channels` +trait. It is also checked—albeit at runtime—that when the actor’s construction +is complete (i.e. its constructor and ``preStart`` hook have run) every channel +listed in the selfChannel type parameter has been declared. This can in general +not be done at compile time, both due to the possibility of overriding +subclasses as well as the problem that the compiler cannot determine whether a +``channel[]`` statement will be called in the course of execution due to +external inputs (e.g. if conditionally executed). + +It should also be noted that the type of ``req`` in this example is +``Request``, hence it would be a compile-time error to try to match against the +``Command`` companion object. The ``snd`` reference is the sender channel +reference, which in this example is of type +``ChannelRef[(Reply, UnknownDoNotWriteMeDown) :+: TNil]``, meaning that sending +back a reply which is not of type ``Reply`` would be a compile-time error. + +The last thing to note is that an actor is not obliged to reply to an incoming +message, even if that was successfully delivered to it: it might not be +appropriate, or it might be impossible, the actor might have failed before +executing the replying message send, etc. And as always, the ``snd`` reference +may be used more than once, and even stored away for later. It must not leave +the actor within it was created, however, because that would defeat the +ping-pong check; this is the reason for the curious name of the fabricated +reply type ``UnknownDoNotWriteMeDown``, if you find yourself declaring that +type as part of a message or similar you know that you are cheating. + +Declaration of Subchannels +-------------------------- + +It can be convenient to carve out subchannels for special treatment like so: + +.. includecode:: code/docs/channels/ChannelDocSpec.scala#declaring-subchannels + +This means that all ``Command`` requests will be positively answered while all +others may or may not be lucky. This dispatching between the two declarations +does not depend on their order but is solely done based on which type is more +specific. + +Forwarding Messages +------------------- + +Forwarding messages has been hinted at in the last sample already, but here is +a more complete sample actor: + +.. includecode:: code/docs/channels/ChannelDocSpec.scala#forwarding + :exclude: become + +This actor declares a single-Channel parametric type which it forwards to a +target actor, handing replies back to the original sender using the ask/pipe +pattern. + +.. note:: + + It is important not to forget the ``TypeTag`` context bound for all type + arguments which are used in channel declarations, otherwise the not very + helpful error “Predef is not an enclosing class” will haunt you. + +Changing Behavior at Runtime +---------------------------- + +The actor from the previous example gets a lot more interesting when +implementing its control channel: + +.. includecode:: code/docs/channels/ChannelDocSpec.scala#forwarding + +This shows all elements of the toolkit in action: calling ``channel[T1]`` again +during the lifetime of the actor will alter its behavior on that channel. In +this case a latch or gate is modeled which when closed will permit the message +flow through and when not will drop the messages to the floor. + +Creating Actors with Channels +----------------------------- + +Creating top-level actors with channels is done using the ``ChannelExt`` extension: + +.. includecode:: code/docs/channels/ChannelDocSpec.scala + :include: usage + :exclude: processing + +Implementation Restrictions +--------------------------- + +The erasure-based dispatch of incoming messages requires all channels which are +declared to have unique JVM type representations, i.e. it is not possible to +have two channel declarations with types ``List[A]`` and ``List[B]`` because +both would at runtime only be known as ``List[_]``. + +The specific dispatch mechanism also require the declaration of all channels or +subchannels during the actor’s construction, independent of whether they shall +later change behavior or not. Changing behavior for a subchannel is only +possible if that subchannel was declared up-front. + +TypeTags are currently (Scala 2.10.0) not serializable, hence narrowing of +:class:`ActorRef` does not work for remote references. + +How to read The Types +===================== + + diff --git a/akka-remote/src/main/scala/akka/remote/transport/netty/NettyTransport.scala b/akka-remote/src/main/scala/akka/remote/transport/netty/NettyTransport.scala index 2f10dfb4f7..d600fbc2c9 100644 --- a/akka-remote/src/main/scala/akka/remote/transport/netty/NettyTransport.scala +++ b/akka-remote/src/main/scala/akka/remote/transport/netty/NettyTransport.scala @@ -340,7 +340,7 @@ class NettyTransport(private val settings: NettyTransportSettings, private val s Future.failed(e) }) onFailure { case t: ConnectException ⇒ statusPromise failure new NettyTransportException(t.getMessage, t.getCause) - case t ⇒ statusPromise failure t + case t ⇒ statusPromise failure t } statusPromise.future diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index c3e5c3fc38..7ea99e6b5a 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -364,7 +364,7 @@ object AkkaBuild extends Build { lazy val docs = Project( id = "akka-docs", base = file("akka-docs"), - dependencies = Seq(actor, testkit % "test->test", mailboxesCommon % "compile;test->test", + dependencies = Seq(actor, testkit % "test->test", mailboxesCommon % "compile;test->test", channels, remote, cluster, slf4j, agent, dataflow, transactor, fileMailbox, zeroMQ, camel, osgi, osgiAries), settings = defaultSettings ++ site.settings ++ site.sphinxSupport() ++ site.publishSite ++ sphinxPreprocessing ++ cpsPlugin ++ Seq( sourceDirectory in Sphinx <<= baseDirectory / "rst", @@ -720,10 +720,10 @@ object Dependencies { val osgiCore = "org.osgi" % "org.osgi.core" % "4.2.0" // ApacheV2 // Camel Sample - val camelJetty = "org.apache.camel" % "camel-jetty" % camelCore.revision // ApacheV2 + val camelJetty = "org.apache.camel" % "camel-jetty" % camelCore.revision // ApacheV2 // Cluster Sample - val sigar = "org.hyperic" % "sigar" % "1.6.4" // ApacheV2 + val sigar = "org.hyperic" % "sigar" % "1.6.4" // ApacheV2 // Test