2014-04-01 15:19:42 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2014 Typesafe Inc. <http://www.typesafe.com>
|
|
|
|
|
*/
|
|
|
|
|
package akka.stream
|
|
|
|
|
|
|
|
|
|
import scala.concurrent.duration.FiniteDuration
|
|
|
|
|
import akka.actor.ActorRefFactory
|
2014-04-08 13:37:55 +02:00
|
|
|
import akka.stream.impl.ActorBasedFlowMaterializer
|
2014-04-01 15:19:42 +02:00
|
|
|
import akka.stream.impl.Ast
|
2014-07-22 12:21:53 +02:00
|
|
|
import org.reactivestreams.{ Publisher, Subscriber }
|
2014-04-01 15:19:42 +02:00
|
|
|
import scala.concurrent.duration._
|
2014-05-13 17:17:33 +02:00
|
|
|
import akka.actor.Deploy
|
2014-08-21 12:35:38 +02:00
|
|
|
import akka.actor.ExtendedActorSystem
|
|
|
|
|
import akka.actor.ActorContext
|
|
|
|
|
import akka.stream.impl.StreamSupervisor
|
|
|
|
|
import akka.stream.impl.FlowNameCounter
|
2014-04-01 15:19:42 +02:00
|
|
|
|
2014-04-08 13:37:55 +02:00
|
|
|
object FlowMaterializer {
|
2014-05-08 19:34:58 +02:00
|
|
|
|
2014-04-01 19:35:56 +02:00
|
|
|
/**
|
2014-04-23 10:05:09 +02:00
|
|
|
* Scala API: Creates a FlowMaterializer which will execute every step of a transformation
|
2014-04-01 19:35:56 +02:00
|
|
|
* pipeline within its own [[akka.actor.Actor]]. The required [[akka.actor.ActorRefFactory]]
|
2014-04-02 09:03:59 +02:00
|
|
|
* (which can be either an [[akka.actor.ActorSystem]] or an [[akka.actor.ActorContext]])
|
2014-04-01 19:35:56 +02:00
|
|
|
* will be used to create these actors, therefore it is *forbidden* to pass this object
|
2014-04-01 20:59:49 +02:00
|
|
|
* to another actor if the factory is an ActorContext.
|
2014-05-08 19:34:58 +02:00
|
|
|
*
|
|
|
|
|
* The `namePrefix` is used as the first part of the names of the actors running
|
|
|
|
|
* the processing steps. The default `namePrefix` is `"flow"`. The actor names are built up of
|
|
|
|
|
* `namePrefix-flowNumber-flowStepNumber-stepName`.
|
2014-04-01 19:35:56 +02:00
|
|
|
*/
|
2014-08-21 12:35:38 +02:00
|
|
|
def apply(settings: MaterializerSettings, namePrefix: Option[String] = None)(implicit context: ActorRefFactory): FlowMaterializer = {
|
|
|
|
|
val system = context match {
|
|
|
|
|
case s: ExtendedActorSystem ⇒ s
|
|
|
|
|
case c: ActorContext ⇒ c.system
|
|
|
|
|
case null ⇒ throw new IllegalArgumentException("ActorRefFactory context must be defined")
|
|
|
|
|
case _ ⇒ throw new IllegalArgumentException(s"ActorRefFactory context must be a ActorSystem or ActorContext, " +
|
|
|
|
|
"got [${_contex.getClass.getName}]")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new ActorBasedFlowMaterializer(
|
|
|
|
|
settings,
|
|
|
|
|
context.actorOf(StreamSupervisor.props(settings).withDispatcher(settings.dispatcher)),
|
|
|
|
|
FlowNameCounter(system).counter,
|
|
|
|
|
namePrefix.getOrElse("flow"))
|
|
|
|
|
}
|
2014-05-08 19:34:58 +02:00
|
|
|
|
2014-04-23 10:05:09 +02:00
|
|
|
/**
|
|
|
|
|
* Java API: Creates a FlowMaterializer which will execute every step of a transformation
|
|
|
|
|
* pipeline within its own [[akka.actor.Actor]]. The required [[akka.actor.ActorRefFactory]]
|
|
|
|
|
* (which can be either an [[akka.actor.ActorSystem]] or an [[akka.actor.ActorContext]])
|
|
|
|
|
* will be used to create these actors, therefore it is *forbidden* to pass this object
|
|
|
|
|
* to another actor if the factory is an ActorContext.
|
|
|
|
|
*/
|
|
|
|
|
def create(settings: MaterializerSettings, context: ActorRefFactory): FlowMaterializer =
|
|
|
|
|
apply(settings)(context)
|
2014-04-01 15:19:42 +02:00
|
|
|
}
|
|
|
|
|
|
2014-04-01 19:35:56 +02:00
|
|
|
/**
|
2014-04-08 13:37:55 +02:00
|
|
|
* A FlowMaterializer takes the list of transformations comprising a
|
2014-04-01 19:35:56 +02:00
|
|
|
* [[akka.stream.scaladsl.Flow]] and materializes them in the form of
|
2014-07-22 12:21:53 +02:00
|
|
|
* [[org.reactivestreams.Processor]] instances. How transformation
|
2014-04-01 19:35:56 +02:00
|
|
|
* steps are split up into asynchronous regions is implementation
|
|
|
|
|
* dependent.
|
|
|
|
|
*/
|
2014-05-22 08:40:41 +02:00
|
|
|
abstract class FlowMaterializer(val settings: MaterializerSettings) {
|
2014-05-08 19:34:58 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The `namePrefix` is used as the first part of the names of the actors running
|
|
|
|
|
* the processing steps.
|
|
|
|
|
*/
|
|
|
|
|
def withNamePrefix(name: String): FlowMaterializer
|
|
|
|
|
|
2014-04-01 15:19:42 +02:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
* ops are stored in reverse order
|
|
|
|
|
*/
|
2014-07-22 12:21:53 +02:00
|
|
|
private[akka] def toPublisher[I, O](publisherNode: Ast.PublisherNode[I], ops: List[Ast.AstNode]): Publisher[O]
|
2014-04-02 08:07:05 +02:00
|
|
|
|
2014-05-07 15:56:02 +02:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
2014-07-22 12:21:53 +02:00
|
|
|
private[akka] def ductProduceTo[In, Out](subscriber: Subscriber[Out], ops: List[Ast.AstNode]): Subscriber[In]
|
2014-05-07 15:56:02 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
2014-07-22 12:21:53 +02:00
|
|
|
private[akka] def ductBuild[In, Out](ops: List[Ast.AstNode]): (Subscriber[In], Publisher[Out])
|
2014-05-07 15:56:02 +02:00
|
|
|
|
2014-04-01 15:19:42 +02:00
|
|
|
}
|
|
|
|
|
|
2014-04-23 10:05:09 +02:00
|
|
|
object MaterializerSettings {
|
|
|
|
|
private val defaultSettings = new MaterializerSettings
|
|
|
|
|
/**
|
|
|
|
|
* Java API: Default settings.
|
|
|
|
|
* Refine the settings using [[MaterializerSettings#withBuffer]],
|
|
|
|
|
* [[MaterializerSettings#withFanOut]], [[MaterializerSettings#withSubscriptionTimeout]]
|
|
|
|
|
*/
|
|
|
|
|
def create(): MaterializerSettings = defaultSettings
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-01 19:35:56 +02:00
|
|
|
/**
|
|
|
|
|
* The buffers employed by the generated Processors can be configured by
|
|
|
|
|
* creating an appropriate instance of this class.
|
2014-04-02 09:03:59 +02:00
|
|
|
*
|
2014-04-01 19:35:56 +02:00
|
|
|
* This will likely be replaced in the future by auto-tuning these values at runtime.
|
|
|
|
|
*/
|
2014-04-08 13:37:55 +02:00
|
|
|
case class MaterializerSettings(
|
2014-04-01 15:19:42 +02:00
|
|
|
initialFanOutBufferSize: Int = 4,
|
|
|
|
|
maxFanOutBufferSize: Int = 16,
|
|
|
|
|
initialInputBufferSize: Int = 4,
|
|
|
|
|
maximumInputBufferSize: Int = 16,
|
2014-05-13 17:17:33 +02:00
|
|
|
dispatcher: String = Deploy.NoDispatcherGiven) {
|
2014-04-01 15:19:42 +02:00
|
|
|
|
|
|
|
|
private def isPowerOfTwo(n: Integer): Boolean = (n & (n - 1)) == 0
|
|
|
|
|
require(initialFanOutBufferSize > 0, "initialFanOutBufferSize must be > 0")
|
|
|
|
|
require(maxFanOutBufferSize > 0, "maxFanOutBufferSize must be > 0")
|
|
|
|
|
require(initialFanOutBufferSize <= maxFanOutBufferSize,
|
|
|
|
|
s"initialFanOutBufferSize($initialFanOutBufferSize) must be <= maxFanOutBufferSize($maxFanOutBufferSize)")
|
|
|
|
|
|
|
|
|
|
require(initialInputBufferSize > 0, "initialInputBufferSize must be > 0")
|
|
|
|
|
require(isPowerOfTwo(initialInputBufferSize), "initialInputBufferSize must be a power of two")
|
|
|
|
|
require(maximumInputBufferSize > 0, "maximumInputBufferSize must be > 0")
|
|
|
|
|
require(isPowerOfTwo(maximumInputBufferSize), "initialInputBufferSize must be a power of two")
|
|
|
|
|
require(initialInputBufferSize <= maximumInputBufferSize,
|
|
|
|
|
s"initialInputBufferSize($initialInputBufferSize) must be <= maximumInputBufferSize($maximumInputBufferSize)")
|
2014-04-23 10:05:09 +02:00
|
|
|
|
|
|
|
|
def withBuffer(initialInputBufferSize: Int, maximumInputBufferSize: Int): MaterializerSettings =
|
|
|
|
|
copy(initialInputBufferSize = initialInputBufferSize, maximumInputBufferSize = maximumInputBufferSize)
|
|
|
|
|
|
|
|
|
|
def withFanOut(initialFanOutBufferSize: Int, maxFanOutBufferSize: Int): MaterializerSettings =
|
|
|
|
|
copy(initialFanOutBufferSize = initialFanOutBufferSize, maxFanOutBufferSize = maxFanOutBufferSize)
|
|
|
|
|
|
2014-05-13 17:17:33 +02:00
|
|
|
def withDispatcher(dispatcher: String): MaterializerSettings = copy(dispatcher = dispatcher)
|
|
|
|
|
|
2014-04-01 15:19:42 +02:00
|
|
|
}
|
|
|
|
|
|