2014-04-01 15:19:42 +02:00
/* *
* Copyright ( C ) 2014 Typesafe Inc . < http : //www.typesafe.com>
*/
package akka.stream
2014-11-03 15:29:02 +01:00
import java.util.Locale
import java.util.concurrent.TimeUnit
2015-01-28 14:19:50 +01:00
import akka.actor. { ActorContext , ActorRef , ActorRefFactory , ActorSystem , ExtendedActorSystem , Props }
2014-11-09 21:09:50 +01:00
import akka.stream.impl._
2015-01-28 14:19:50 +01:00
import akka.stream.scaladsl.RunnableFlow
2014-08-26 09:03:48 +02:00
import com.typesafe.config.Config
2014-11-03 15:29:02 +01:00
import scala.concurrent.duration._
2015-01-27 18:29:20 +01:00
import akka.actor.Props
import akka.actor.ActorRef
2015-02-04 09:26:32 +01:00
import akka.stream.javadsl.japi
2015-02-26 11:58:29 +01:00
import scala.concurrent.ExecutionContextExecutor
2014-11-03 15:29:02 +01:00
2015-01-27 18:29:20 +01:00
object ActorFlowMaterializer {
2014-05-08 19:34:58 +02:00
2014-04-01 19:35:56 +02:00
/* *
2015-01-27 18:29:20 +01:00
* Scala API : Creates a ActorFlowMaterializer 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-09-01 13:12:18 +02:00
* will be used to create one actor that in turn creates actors for the transformation steps .
2014-05-08 19:34:58 +02:00
*
2015-01-27 18:29:20 +01:00
* The materializer 's [ [ akka . stream . ActorFlowMaterializerSettings ] ] will be obtained from the
2014-08-26 09:03:48 +02:00
* configuration of the `context` 's underlying [ [ akka . actor . ActorSystem ] ] .
*
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
*/
2015-01-28 14:19:50 +01:00
def apply ( materializerSettings : Option [ ActorFlowMaterializerSettings ] = None , namePrefix : Option [ String ] = None , optimizations : Optimizations = Optimizations . none ) ( implicit context : ActorRefFactory ) : ActorFlowMaterializer = {
2014-08-26 09:03:48 +02:00
val system = actorSystemOf ( context )
2015-01-27 18:29:20 +01:00
val settings = materializerSettings getOrElse ActorFlowMaterializerSettings ( system )
2015-01-28 14:19:50 +01:00
apply ( settings , namePrefix . getOrElse ( "flow" ) , optimizations ) ( context )
2014-08-26 09:03:48 +02:00
}
/* *
2015-01-27 18:29:20 +01:00
* Scala API : Creates a ActorFlowMaterializer which will execute every step of a transformation
2014-08-26 09:03:48 +02:00
* 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 .
*
* 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` .
*/
2015-01-28 14:19:50 +01:00
def apply ( materializerSettings : ActorFlowMaterializerSettings , namePrefix : String , optimizations : Optimizations ) ( implicit context : ActorRefFactory ) : ActorFlowMaterializer = {
2014-08-26 09:03:48 +02:00
val system = actorSystemOf ( context )
2014-08-21 12:35:38 +02:00
2015-01-27 18:29:20 +01:00
new ActorFlowMaterializerImpl (
2014-08-26 09:03:48 +02:00
materializerSettings ,
2014-11-17 22:50:15 +01:00
system . dispatchers ,
2014-08-26 09:03:48 +02:00
context . actorOf ( StreamSupervisor . props ( materializerSettings ) . withDispatcher ( materializerSettings . dispatcher ) ) ,
2014-08-21 12:35:38 +02:00
FlowNameCounter ( system ) . counter ,
2015-01-28 14:19:50 +01:00
namePrefix ,
optimizations )
2014-08-21 12:35:38 +02:00
}
2014-05-08 19:34:58 +02:00
2014-08-26 09:03:48 +02:00
/* *
2015-01-27 18:29:20 +01:00
* Scala API : Creates a ActorFlowMaterializer which will execute every step of a transformation
2014-08-26 09:03:48 +02:00
* 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 .
*
* 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` .
*/
2015-01-27 18:29:20 +01:00
def apply ( materializerSettings : ActorFlowMaterializerSettings ) ( implicit context : ActorRefFactory ) : ActorFlowMaterializer =
2014-08-26 09:03:48 +02:00
apply ( Some ( materializerSettings ) , None )
/* *
2015-01-27 18:29:20 +01:00
* Java API : Creates a ActorFlowMaterializer which will execute every step of a transformation
2014-08-26 09:03:48 +02:00
* 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 .
*
* Defaults the actor name prefix used to name actors running the processing steps to `"flow"` .
* The actor names are built up of `namePrefix-flowNumber-flowStepNumber-stepName` .
*/
2015-01-27 18:29:20 +01:00
def create ( context : ActorRefFactory ) : ActorFlowMaterializer =
2014-08-26 09:03:48 +02:00
apply ( ) ( context )
2014-04-23 10:05:09 +02:00
/* *
2015-01-27 18:29:20 +01:00
* Java API : Creates a ActorFlowMaterializer which will execute every step of a transformation
2014-04-23 10:05:09 +02:00
* 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 ] ] )
2014-09-01 13:12:18 +02:00
* will be used to create one actor that in turn creates actors for the transformation steps .
2014-04-23 10:05:09 +02:00
*/
2015-01-27 18:29:20 +01:00
def create ( settings : ActorFlowMaterializerSettings , context : ActorRefFactory ) : ActorFlowMaterializer =
2014-08-26 09:03:48 +02:00
apply ( Option ( settings ) , None ) ( context )
/* *
2015-01-27 18:29:20 +01:00
* Java API : Creates a ActorFlowMaterializer which will execute every step of a transformation
2014-08-26 09:03:48 +02:00
* 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 .
*
* 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` .
*/
2015-01-27 18:29:20 +01:00
def create ( settings : ActorFlowMaterializerSettings , context : ActorRefFactory , namePrefix : String ) : ActorFlowMaterializer =
2014-08-26 09:03:48 +02:00
apply ( Option ( settings ) , Option ( namePrefix ) ) ( context )
private def actorSystemOf ( context : ActorRefFactory ) : ActorSystem = {
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 [ ${ context . getClass . getName } ] " )
}
system
}
2014-04-01 15:19:42 +02:00
}
2014-04-01 19:35:56 +02:00
/* *
2015-01-27 18:29:20 +01:00
* A ActorFlowMaterializer 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 .
*/
2015-01-27 18:29:20 +01:00
abstract class ActorFlowMaterializer extends FlowMaterializer {
def settings : ActorFlowMaterializerSettings
/* *
* INTERNAL API
*/
private [ akka ] def actorOf ( props : Props , name : String ) : ActorRef
}
/* *
* This exception or subtypes thereof should be used to signal materialization
* failures .
*/
class MaterializationException ( msg : String , cause : Throwable = null ) extends RuntimeException ( msg , cause )
object ActorFlowMaterializerSettings {
2014-04-23 10:05:09 +02:00
/* *
2015-01-27 18:29:20 +01:00
* Create [ [ ActorFlowMaterializerSettings ] ] .
2014-08-26 09:03:48 +02:00
*
2015-01-27 18:29:20 +01:00
* You can refine the configuration based settings using [ [ ActorFlowMaterializerSettings # withInputBuffer ] ] ,
* [ [ ActorFlowMaterializerSettings # withDispatcher ] ]
2014-04-23 10:05:09 +02:00
*/
2015-01-27 18:29:20 +01:00
def apply ( system : ActorSystem ) : ActorFlowMaterializerSettings =
2014-08-26 09:03:48 +02:00
apply ( system . settings . config . getConfig ( "akka.stream.materializer" ) )
/* *
2015-01-27 18:29:20 +01:00
* Create [ [ ActorFlowMaterializerSettings ] ] .
2014-08-26 09:03:48 +02:00
*
2015-01-27 18:29:20 +01:00
* You can refine the configuration based settings using [ [ ActorFlowMaterializerSettings # withInputBuffer ] ] ,
* [ [ ActorFlowMaterializerSettings # withDispatcher ] ]
2014-08-26 09:03:48 +02:00
*/
2015-01-27 18:29:20 +01:00
def apply ( config : Config ) : ActorFlowMaterializerSettings =
ActorFlowMaterializerSettings (
initialInputBufferSize = config . getInt ( "initial-input-buffer-size" ) ,
maxInputBufferSize = config . getInt ( "max-input-buffer-size" ) ,
dispatcher = config . getString ( "dispatcher" ) ,
2015-02-04 09:26:32 +01:00
supervisionDecider = Supervision . stoppingDecider ,
2015-01-27 18:29:20 +01:00
subscriptionTimeoutSettings = StreamSubscriptionTimeoutSettings ( config ) ,
debugLogging = config . getBoolean ( "debug-logging" ) ,
2015-03-03 21:05:53 +01:00
outputBurstLimit = config . getInt ( "output-burst-limit" ) ,
2015-01-27 18:29:20 +01:00
optimizations = Optimizations . none )
2014-08-26 09:03:48 +02:00
/* *
* Java API
*
2015-01-27 18:29:20 +01:00
* You can refine the configuration based settings using [ [ ActorFlowMaterializerSettings # withInputBuffer ] ] ,
* [ [ ActorFlowMaterializerSettings # withDispatcher ] ]
2014-08-26 09:03:48 +02:00
*/
2015-01-27 18:29:20 +01:00
def create ( system : ActorSystem ) : ActorFlowMaterializerSettings =
2014-08-26 09:03:48 +02:00
apply ( system )
/* *
* Java API
*
2015-01-27 18:29:20 +01:00
* You can refine the configuration based settings using [ [ ActorFlowMaterializerSettings # withInputBuffer ] ] ,
* [ [ ActorFlowMaterializerSettings # withDispatcher ] ]
2014-08-26 09:03:48 +02:00
*/
2015-01-27 18:29:20 +01:00
def create ( config : Config ) : ActorFlowMaterializerSettings =
2014-08-26 09:03:48 +02:00
apply ( config )
2014-04-23 10:05:09 +02:00
}
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 .
*/
2015-01-27 18:29:20 +01:00
final case class ActorFlowMaterializerSettings (
2014-08-26 09:03:48 +02:00
initialInputBufferSize : Int ,
maxInputBufferSize : Int ,
2014-11-03 15:29:02 +01:00
dispatcher : String ,
2015-02-04 09:26:32 +01:00
supervisionDecider : Supervision . Decider ,
2014-11-07 15:00:50 +01:00
subscriptionTimeoutSettings : StreamSubscriptionTimeoutSettings ,
2015-01-27 18:29:20 +01:00
debugLogging : Boolean ,
2015-03-03 21:05:53 +01:00
outputBurstLimit : Int ,
2015-01-27 18:29:20 +01:00
optimizations : Optimizations ) {
2014-08-26 09:03:48 +02:00
require ( initialInputBufferSize > 0 , "initialInputBufferSize must be > 0" )
2015-01-28 14:19:50 +01:00
requirePowerOfTwo ( maxInputBufferSize , "maxInputBufferSize" )
2014-08-26 09:03:48 +02:00
require ( initialInputBufferSize <= maxInputBufferSize , s" initialInputBufferSize( $initialInputBufferSize ) must be <= maxInputBufferSize( $maxInputBufferSize ) " )
2014-04-01 15:19:42 +02:00
2015-01-27 18:29:20 +01:00
def withInputBuffer ( initialSize : Int , maxSize : Int ) : ActorFlowMaterializerSettings =
2014-08-26 09:03:48 +02:00
copy ( initialInputBufferSize = initialSize , maxInputBufferSize = maxSize )
2014-04-23 10:05:09 +02:00
2015-01-27 18:29:20 +01:00
def withDispatcher ( dispatcher : String ) : ActorFlowMaterializerSettings =
2014-08-26 09:03:48 +02:00
copy ( dispatcher = dispatcher )
2014-05-13 17:17:33 +02:00
2015-02-04 09:26:32 +01:00
/* *
* Scala API : Decides how exceptions from application code are to be handled , unless
2015-04-09 15:16:59 +02:00
* overridden for specific flows of the stream operations with
2015-02-04 09:26:32 +01:00
* [ [ akka . stream . scaladsl . OperationAttributes # supervisionStrategy ] ] .
*/
def withSupervisionStrategy ( decider : Supervision . Decider ) : ActorFlowMaterializerSettings =
copy ( supervisionDecider = decider )
/* *
* Java API : Decides how exceptions from application code are to be handled , unless
2015-04-09 15:16:59 +02:00
* overridden for specific flows of the stream operations with
2015-02-04 09:26:32 +01:00
* [ [ akka . stream . javadsl . OperationAttributes # supervisionStrategy ] ] .
*/
2015-02-23 11:54:02 +01:00
def withSupervisionStrategy ( decider : japi.Function [ Throwable , Supervision . Directive ] ) : ActorFlowMaterializerSettings = {
import Supervision._
copy ( supervisionDecider = decider match {
2015-02-26 22:42:34 +01:00
case `resumingDecider` ⇒ resumingDecider
case `restartingDecider` ⇒ restartingDecider
case `stoppingDecider` ⇒ stoppingDecider
case other ⇒ other . apply _
2015-02-23 11:54:02 +01:00
} )
}
2015-02-04 09:26:32 +01:00
2015-01-27 18:29:20 +01:00
def withDebugLogging ( enable : Boolean ) : ActorFlowMaterializerSettings =
2015-01-27 13:36:13 +01:00
copy ( debugLogging = enable )
2015-01-27 18:29:20 +01:00
def withOptimizations ( optimizations : Optimizations ) : ActorFlowMaterializerSettings =
copy ( optimizations = optimizations )
2015-01-28 14:19:50 +01:00
private def requirePowerOfTwo ( n : Integer , name : String ) : Unit = {
require ( n > 0 , s" $name must be > 0 " )
require ( ( n & ( n - 1 ) ) == 0 , s" $name must be a power of two " )
}
2014-04-01 15:19:42 +02:00
}
2014-11-03 15:29:02 +01:00
object StreamSubscriptionTimeoutSettings {
2015-01-28 14:19:50 +01:00
import akka.stream.StreamSubscriptionTimeoutTerminationMode._
2014-11-03 15:29:02 +01:00
/* * Java API */
def create ( config : Config ) : StreamSubscriptionTimeoutSettings =
apply ( config )
def apply ( config : Config ) : StreamSubscriptionTimeoutSettings = {
val c = config . getConfig ( "subscription-timeout" )
StreamSubscriptionTimeoutSettings (
mode = c . getString ( "mode" ) . toLowerCase ( Locale . ROOT ) match {
case "no" | "off" | "false" | "noop" ⇒ NoopTermination
case "warn" ⇒ WarnTermination
case "cancel" ⇒ CancelTermination
} ,
2014-11-12 13:05:57 +01:00
timeout = c . getDuration ( "timeout" , TimeUnit . MILLISECONDS ) . millis )
2014-11-03 15:29:02 +01:00
}
}
2014-11-12 13:05:57 +01:00
final case class StreamSubscriptionTimeoutSettings ( mode : StreamSubscriptionTimeoutTerminationMode , timeout : FiniteDuration )
2014-11-03 15:29:02 +01:00
sealed abstract class StreamSubscriptionTimeoutTerminationMode
2014-11-12 13:05:57 +01:00
2014-11-03 15:29:02 +01:00
object StreamSubscriptionTimeoutTerminationMode {
2014-11-12 13:05:57 +01:00
case object NoopTermination extends StreamSubscriptionTimeoutTerminationMode
case object WarnTermination extends StreamSubscriptionTimeoutTerminationMode
case object CancelTermination extends StreamSubscriptionTimeoutTerminationMode
2014-11-03 15:29:02 +01:00
/* * Java API */
def noop = NoopTermination
/* * Java API */
def warn = WarnTermination
/* * Java API */
def cancel = CancelTermination
2014-11-12 13:05:57 +01:00
2014-11-03 15:29:02 +01:00
}
2015-01-27 18:29:20 +01:00
final object Optimizations {
val none : Optimizations = Optimizations ( collapsing = false , elision = false , simplification = false , fusion = false )
val all : Optimizations = Optimizations ( collapsing = true , elision = true , simplification = true , fusion = true )
}
final case class Optimizations ( collapsing : Boolean , elision : Boolean , simplification : Boolean , fusion : Boolean ) {
def isEnabled : Boolean = collapsing || elision || simplification || fusion
}