SlideShare a Scribd company logo
Playframework
1
1.
2.
3.
4.
3 4
4
3 20:00
2
3
@tanacasino
Scala 2 Eucalytpus
OpenStack
GitBucket (Committer)
4
GitHub
5
6
7
documentation
framework
templates
Play
Lightbend HP
play-scala
8
framework/src
build-link play-functional play-netty-utils
fork-run play-integration-test play-server
fork-run-protocol play-java play-specs2
iteratees play-java-jdbc play-streams
play play-java-jpa play-test
play-akka-http-server play-java-ws play-ws
play-cache play-jdbc routes-compiler
play-datacommons play-jdbc-api run-support
play-docs play-jdbc-evolutions sbt-fork-run-plugin
play-docs-sbt-plugin play-json sbt-plugin
play-exceptions play-logback
play-filters-helpers play-netty-server
9
10
1.
2. Action Controller
3. Json
4. Applicative
11
12
main
def main(args: Array[String]): Unit
13
main
ag 'def main(' src
3
fork-run sbt
src/fork-run/src/main/scala/play/forkrun/ForkRun.scala
28: def main(args: Array[String]): Unit = {
src/play-netty-server/src/main/scala/play/core/server/NettyServer.scala
298: def main(args: Array[String]) {
src/play-server/src/main/scala/play/core/server/ProdServerStart.scala
20: def main(args: Array[String]) {
14
NettyServer
def main(args: Array[String]) {
System.err.println(
s"NettyServer.main is deprecated. 
Please start your Play server with the 
${ProdServerStart.getClass.getName}.main."
)
ProdServerStart.main(args)
}
ProdServerStart
15
main grep
play-scala
sbt dist
ProdServerStart
declare -a app_mainclass=("play.core.server.ProdServerStart")
# java -jar play.core.server.ProdServerStart
16
framework/src/play-
server/src/main/scala/play/core/server/ProdServe
rStart.scala
2
object ProdServerStart {
/**
* Start a prod mode server from the command line.
*/
def main(args: Array[String]) {
val process = new RealServerProcess(args)
start(process)
}
// ...
} 17
RealServerProcess
ServerProcess JVM
/**
* Abstracts a JVM process so it can be mocked for testing or to
* isolate pseudo-processes within a VM. Code using this class
* should use the methods in this class instead of methods like
* `System.getProperties()`, `System.exit()`, etc.
*/
trait ServerProcess
class RealServerProcess(
val args: Seq[String]
) extends ServerProcess
18
ServerProcess
trait ServerProcess {
/** The ClassLoader that should be used */
def classLoader: ClassLoader
/** The command line arguments the process as invoked with */
def args: Seq[String]
/** The process's system properties */
def properties: Properties
/** Helper for getting properties */
final def prop(name: String): Option[String] = Option(properties.getProp
/** The process's id */
def pid: Option[String]
/** Add a hook to run when the process shuts down */
def addShutdownHook(hook: => Unit): Unit
/** Exit the process with a message and optional cause and return code *
def exit(message: String, cause: Option[Throwable] = None, returnCode:
}
19
RealServerProcess
pid
class RealServerProcess(val args: Seq[String]) extends ServerProcess
def classLoader: ClassLoader =
Thread.currentThread.getContextClassLoader
def properties: Properties = System.getProperties
def pid: Option[String] = ManagementFactory.getRuntimeMXBean
.getName.split('@').headOption
def addShutdownHook(hook: => Unit): Unit = {
Runtime.getRuntime.addShutdownHook(new Thread {
override def run() = hook
})
}
def exit(message: String, cause: Option[Throwable] = None, returnCode:
/* ... */
}
}
20
ProdServerStart#start
def main(args: Array[String]): Unit = {
val process = new RealServerProcess(args)
//
start(process)
}
// 4
def start(process: ServerProcess): ServerWithStop = {
try {
// 1. Read settings
val config: ServerConfig = readServerConfigSettings(process)
// 2. Create a PID file before we do any real work
val pidFile = createPidFile(process, config.configuration)
// 3. Start the application
val application: Application = { /* */ }
Play.start(application)
// 4. Start the server
val serverProvider: ServerProvider = ServerProvider.fromConfiguration(
val server = serverProvider.createServer(config, application)
// ...
}
21
ProdServerStart#start
1.
2. PID(Process ID)
3. play.api.Application
4. HTTP
22
1. src
!(option).isDefined (option).isEmpty
def readServerConfigSettings(process: ServerProcess): ServerConfig = {
val configuration: Configuration = { /* */ }
val rootDir: File = { /* */ }
def parsePort(portType: String): Option[Int] = {
// configuration http/https parse
}
val httpPort = parsePort("http")
val httpsPort = parsePort("https")
if (!(httpPort orElse httpsPort).isDefined) throw ServerStartException
val address = configuration.getString("play.server.http.address")
.getOrElse("0.0.0.0")
ServerConfig(/* */)
}
23
{} rootDirArg
val configuration: Configuration = {
// rootDir Some(java.io.File)
val rootDirArg: Option[File] =
process.args.headOption.map(new File(_))
// Map.empty
// play.server.dir
// "play.server.dir" -> "${dir.getAbsolutePath}" Map
val rootDirConfig = rootDirArg.fold(Map.empty[String, String])
(dir => ServerConfig.rootDirConfig(dir))
// Configuration .
Configuration.load(
process.classLoader, process.properties, rootDirConfig, true
)
}
24
play.api.Con guration
object Configuration {
private[play] def load(
classLoader: ClassLoader,
properties: Properties,
directSettings: Map[String, AnyRef],
allowMissingApplicationConf: Boolean): Configuration = {
try {
// 1. SystemProperty
val systemPropertyConfig = /* */
// 2. Map[String, String]
val directConfig: Config = ConfigFactory.parseMap(directSettings.asJ
// 3. conf/application.conf
val applicationConfig: Config = /* */
// 4. play overrides conf
val playOverridesConfig: Config = ConfigFactory.parseResources(class
// 5. reference.conf
val referenceConfig: Config = ConfigFactory.parseResources(classLoad
// ... 25
5
1. SystemProperty (System.getProperty)
2. DirectCon g:
3. conf/application.conf )
4. play overrides con g
Akka Play
5. reference.conf
Play
26
Combine
reduceLeft(_ withFallback _)
// Combine all the config together into one big config
val combinedConfig: Config = Seq(
systemPropertyConfig,
directConfig,
applicationConfig,
playOverridesConfig,
referenceConfig
).reduceLeft(_ withFallback _)
// resolve ${foo.bar}
//
val resolvedConfig = combinedConfig.resolve
Configuration(resolvedConfig)
} catch {
case e: ConfigException => throw configError(e.origin, e.getMessage,
}
27
rootDir
Option.get
getOrElse(throw new Exception)
val rootDir: File = {
// play.server.dir
val path = configuration
.getString("play.server.dir")
.getOrElse(throw ServerStartException("No root server path supplied"
val file = new File(path)
if (!(file.exists && file.isDirectory)) {
throw ServerStartException(s"Bad root server path: $path")
}
file
}
28
http/https port
http/https
def parsePort(portType: String): Option[Int] = {
configuration.getString(s"play.server.${portType}.port").flatMap {
case "disabled" => None
case str =>
val i = try Integer.parseInt(str) catch {
case _: NumberFormatException => throw ServerStartException(
}
Some(i)
}
}
val httpPort = parsePort("http")
val httpsPort = parsePort("https")
if (!(httpPort orElse httpsPort).isDefined) throw ServerStartException
29
TypeSafe Con g
5
reduceLeft Combine
{}
Option.get getOrElse(throw new
Exception)
30
2. PID src
// 2. Create a PID file before we do any real work
val pidFile = createPidFile(process, config.configuration)
def createPidFile(process: ServerProcess, configuration: Configuration
// "play.server.pidfile.path"
val pidFilePath = configuration
.getString("play.server.pidfile.path")
.getOrElse(throw ServerStartException("Pid file path
// "play.server.pidfile.path" "/dev/null"
if (pidFilePath == "/dev/null") None else {
val pidFile = new File(pidFilePath).getAbsoluteFile
// PID
if (pidFile.exists) {
throw ServerStartException(s"This application is already running (Or
}
val pid = process.pid getOrElse (throw ServerStartException("Couldn't
val out = new FileOutputStream(pidFile)
try out.write(pid.getBytes) finally out.close()
Some(pidFile)
}
} 31
3. Application src
play.api.Application UT
// 3. Start the application
val application: Application = {
val environment = Environment(config.rootDir,
process.classLoader,
Mode.Prod)
val context = ApplicationLoader.createContext(environment)
val loader = ApplicationLoader(context)
loader.load(context)
}
Play.start(application)
32
play.api.Environment
case class
/**
* The environment for the application.
*
* Captures concerns relating to the classloader and
* the filesystem for the application.
*
* @param
* @param classLoader
* @param mode Prod/Dev/Test 3 )
*/
case class Environment(
rootPath: File,
classLoader: ClassLoader,
mode: Mode.Mode
)
33
ApplicationLoader#createContext
SourceMapper:
WebCommands: Evolutions
/**
* Create an application loading context.
* Locates and loads the necessary configuration files for the application
*/
def createContext(environment: Environment,
initialSettings: Map[String, AnyRef] = Map.empty[String, AnyRef],
sourceMapper: Option[SourceMapper] = None,
webCommands: WebCommands = new DefaultWebCommands) = {
val configuration = Configuration.load(environment, initialSettings)
Context(environment, sourceMapper, webCommands, configuration)
}
34
ApplicationLoader#apply
Scala/Java
// Locate and instantiate the ApplicationLoader.
def apply(context: Context): ApplicationLoader = {
Reflect.configuredClass[
ApplicationLoader, play.ApplicationLoader, GuiceApplicationLoader
](
context.environment,
PlayConfig(context.initialConfiguration),
"play.application.loader",
classOf[GuiceApplicationLoader].getName
) match {
case None => /* */
new GuiceApplicationLoader /* */
case Some(Left(scalaClass)) => /* Scala */
scalaClass.newInstance
case Some(Right(javaClass)) => /* Java */
javaClass.newInstance
}
} 35
ApplicationLoader
ApplicationLoader
Play Runtime DI Compile time DI
play.application.loader ApplicationLoader
Compile time DI
Scala trait Java interface
Guice Runtime DI
36
play.api.inject.guice.GuiceApplicationLoader
Guice
def this()
/**
* An ApplicationLoader that uses Guice to bootstrap the application.
*
* Subclasses can override the `builder` and `overrides` methods.
*/
class GuiceApplicationLoader(
protected val initialBuilder: GuiceApplicationBuilder
) extends ApplicationLoader {
// empty constructor needed for instantiating via reflection
def this() = this(new GuiceApplicationBuilder)
}
37
GuiceApplicationBuilder
Application Guice
final case class GuiceApplicationBuilder(
environment: Environment = Environment.simple(),
configuration: Configuration = Configuration.empty,
modules: Seq[GuiceableModule] = Seq.empty,
overrides: Seq[GuiceableModule] = Seq.empty,
disabled: Seq[Class[_]] = Seq.empty,
binderOptions: Set[BinderOption] = BinderOption.defaults,
eagerly: Boolean = false,
loadConfiguration: Environment => Configuration = Configuration.load,
global: Option[GlobalSettings.Deprecated] = None,
loadModules: (Environment, Configuration) => Seq[GuiceableModule] =
) extends GuiceBuilder[GuiceApplicationBuilder](
environment, configuration, modules, overrides, disabled, binderOptions,
)
38
src
loader GuiceApplicationLoader
loader GuiceApplicationBuilder
loader.load
// Start the application
val application: Application = {
val environment = Environment(config.rootDir, process.classLoader,
val context = ApplicationLoader.createContext(environment)
val loader = ApplicationLoader(context)
//
loader.load(context)
}
39
GuiceApplicationLoader#load
GuiceApplicationBuilder build
builder Con g Environment
override final def load(
context: ApplicationLoader.Context
): Application = {
builder(context).build
}
protected def builder(
context: ApplicationLoader.Context
): GuiceApplicationBuilder = {
initialBuilder.disableCircularProxies()
.in(context.environment)
.loadConfig(context.initialConfiguration)
.overrides(overrides(context): _*)
}
protected def overrides(context: ApplicationLoader.Context): Seq[Guiceable
GuiceApplicationLoader.defaultOverrides(context)
} 40
GuiceApplicationLoader#defaultOverrides
Guice Module
object GuiceApplicationLoader {
/**
* The default overrides provided by the Scala and Java GuiceApplication
*/
def defaultOverrides(
context: ApplicationLoader.Context
): Seq[GuiceableModule] = {
Seq(
bind[OptionalSourceMapper] to new OptionalSourceMapper(context.sourc
bind[WebCommands] to context.webCommands,
bind[DefaultApplicationLifecycle] to context.lifecycle)
}
}
41
GuiceApplicationBuilder#build
injector Application
injector(): play.api.injector.PlayInjector
def injector Guice Injector
override final def load(
context: ApplicationLoader.Context
): Application = {
builder(context).build //
}
/**
* Create a new Play Application using this configured builder.
*/
def build(): Application = injector().instanceOf[Application]
42
GuiceBuilder#injector
GuiceApplicationBuilder GuiceBuilder
Guice Injector
/**
* Create a Play Injector backed by Guice using this configured builder.
*/
def injector(): PlayInjector = {
try {
val stage = /* */
// Injector(com.google.inject.Injector)
val guiceInjector =
Guice.createInjector(stage, applicationModule())
// Injector PlayInjector
guiceInjector.getInstance(classOf[PlayInjector])
} catch {
// ...
}
} 43
GuiceBuilder#applicationModule
PlayInjector GuiceInjector
conf Module
// Create a Play Injector backed by Guice using this configured builder.
def applicationModule(): GuiceModule = createModule()
def createModule(): GuiceModule = {
import scala.collection.JavaConverters._
val injectorModule = GuiceableModule.guice(Seq(
bind[PlayInjector].to[GuiceInjector],
bind[play.inject.Injector].to[play.inject.DelegateInjector]
), binderOptions)
val enabledModules = modules.map(_.disable(disabled))
val bindingModules = GuiceableModule.guiced(environment, configuration
val overrideModules = GuiceableModule.guiced(environment, configuratio
GuiceModules.`override`(bindingModules.asJava).`with`(overrideModules.
}
44
GuiceInjector
GuiceInjector Guice Injector
instanceOf[T]
injector().instanceOf[Application]
class GuiceInjector @Inject() (
injector: com.google.inject.Injector
) extends PlayInjector {
def instanceOf[T](implicit ct: ClassTag[T]) = instanceOf(ct.runtimeClass
def instanceOf[T](clazz: Class[T]) = injector.getInstance(clazz)
def instanceOf[T](key: BindingKey[T]) = injector.getInstance(GuiceKey
}
45
Application
Guice Injector con g
framework/src/play/src/main/resources/reference
.conf
play {
modules {
# The enabled modules that should be automatically loaded.
enabled += "play.api.inject.BuiltinModule" #
enabled += "play.api.i18n.I18nModule"
# A way to disable modules that are automatically enabled
disabled = []
}
}
46
play.api.inject.BuiltinModule
Provider
class BuiltinModule extends Module {
def bindings(env: Environment, configuration: Configuration): Seq[
// ... ...
Seq(
bind[Environment] to env,
bind[Configuration].toProvider[ConfigurationProvider],
bind[DefaultApplicationLifecycle].toSelf,
bind[ApplicationLifecycle].to(bind[DefaultApplicationLifecycle
bind[Application].to[DefaultApplication], //
bind[play.Application].to[play.DefaultApplication],
bind[ActorSystem].toProvider[ActorSystemProvider],
bind[Materializer].toProvider[MaterializerProvider],
bind[ExecutionContext].to[ExecutionContextExecutor],
// ... ...
}
47
DefaultApplication
Inject
@Singleton
class DefaultApplication @Inject() (
environment: Environment,
applicationLifecycle: DefaultApplicationLifecycle,
override val injector: Injector,
override val configuration: Configuration,
override val requestHandler: HttpRequestHandler,
override val errorHandler: HttpErrorHandler,
override val actorSystem: ActorSystem,
override val materializer: Materializer) extends Application {
def path = environment.rootPath
def classloader = environment.classLoader
def mode = environment.mode
def stop() = applicationLifecycle.stop()
} 48
src
// 3. Start the application
val application: Application = {
val environment = Environment(config.rootDir,
process.classLoader,
Mode.Prod)
val context = ApplicationLoader.createContext(environment)
val loader = ApplicationLoader(context)
loader.load(context)
}
//
Play.start(application)
49
play.api.Play#start
@volatile private[play] var _currentApp: Application = _
def start(app: Application) {
stop(_currentApp)
_currentApp = app
Threads.withContextClassLoader(app.classloader) {
app.global.beforeStart(app)
app.routes
app.global.onStart(app)
}
app.mode match {
case Mode.Test =>
// started
case mode => logger.info("Application started (" + mode + ")")
}
}
50
Application
ApplicationLoader Application
ApplicationLoader
Guice
GuiceApplicationLoader
GuiceApplicationBuilder
BuiltinModules
DefaultApplication
51
HTTP
netty src
ServerProvider Server
hook
PID
val serverProvider: ServerProvider =
ServerProvider.fromConfiguration(
process.classLoader, config.configuration
)
val server = serverProvider.createServer(config, application)
process.addShutdownHook {
server.stop()
pidFile.foreach(_.delete())
assert(!pidFile.exists(_.exists), "PID file should not exist!")
}
server
52
ServerProvider#fromCon guration
play.server.provider
new
framework/src/play-netty-
server/src/main/resources/reference.conf
play.core.server.NettyServerProvider
akka-http experimental
def fromConfiguration(classLoader: ClassLoader, configuration: Configurati
val ClassNameConfigKey = "play.server.provider"
val className: String = configuration.getString(ClassNameConfigKey
val clazz = try classLoader.loadClass(className) catch { // }
val ctor = try clazz.getConstructor() catch { // }
ctor.newInstance().asInstanceOf[ServerProvider]
}
53
NettyServerProvider#createServer
play.core.server.NettyServer new
def createServer(context: ServerProvider.Context) =
new NettyServer(
context.config,
context.appProvider,
context.stopHook,
context.actorSystem
)(
context.materializer
)
54
NettyServer
val bind
new src
class NettyServer(/* */) {
// Maybe the HTTP server channel
private val httpChannel =
config.port.map(bindChannel(_, secure = false))
// Maybe the HTTPS server channel
private val httpsChannel =
config.sslPort.map(bindChannel(_, secure = true))
private def bindChannel(port: Int, secure: Boolean): Channel = {
val protocolName = if (secure) "HTTPS" else "HTTP"
val address = new InetSocketAddress(config.address, port)
val (serverChannel, channelSource) = bind(address)
channelSource.runWith(channelSink(port = port, secure = secure))
/* */
}
}
55
NettyServer#bind
netty akka-stream Source
HTTP Source
OK
Source => Sink
private def bind(address: InetSocketAddress): (Channel, Source[Channel
val serverChannelEventLoop = eventLoop.next
// Watches for channel events, and pushes them through a reactive stre
val channelPublisher = new HandlerPublisher(serverChannelEventLoop, cl
/* */
val channel = bootstrap.bind.await().channel()
allChannels.add(channel)
(channel, Source.fromPublisher(channelPublisher))
}
56
NettyServer#channelSink
input(Source) Sink
PlayRequestHandler
val (serverChannel, channelSource) = bind(address)
//
channelSource.runWith(channelSink(port = port, secure = secure))
// :
private def channelSink(port: Int, secure: Boolean): Sink[Channel, Future
Sink.foreach[Channel] { (connChannel: Channel) =>
val pipeline = connChannel.pipeline() /* ... */
pipeline.addLast("decoder", new HttpRequestDecoder(maxInitialLineLengt
val requestHandler = new PlayRequestHandler(this) //
pipeline.addLast("http-handler", new HttpStreamsServerHandler(Seq
pipeline.addLast("request-handler", requestHandler)     
/* ... */
}
} 57
NettyServer
akka-stream netty
(Source/Sink)
play.server.netty.transport native
epoll
(Linux)
PlayRequestHandler (
private lazy val transport = conf.getString("transport") match {
case "native" => Native
case "jdk" => Jdk
}
58
59
ProdServerStart#main
Con guration
ApplicationLoader/GuiceApplicationLoader/Guice
ApplicationBuilder Application
Play.start Application
NettyServerProvider NettyServer
HTTP
PlayRequestHandler
60
Typsafe Con g/Google Guice/JBoss
Netty/Akka/Akka-Stream
sbt run
ProdServer
61
62
63
PlayRequestHandler
ChannelInboundHandlerAdapter netty class
Netty
def channelRead def handle
private[play] class PlayRequestHandler(
val server: NettyServer
) extends ChannelInboundHandlerAdapter {
// ...
override def channelRead(ctx: ChannelHandlerContext, msg: Object):
// Handle the given request.
def handle(
channel: Channel, request: HttpRequest
): Future[HttpResponse] = { /* */ }
} 64
PlayRequestHandler.channelRead
Netty HttpRequest handle
Write
trampoline ExecutionContext
override def channelRead(ctx: ChannelHandlerContext, msg: Object): Unit
logger.trace(s"channelRead: ctx = ${ctx}, msg = ${msg}")
msg match {
case req: HttpRequest =>
requestsInFlight.incrementAndGet()
/* handle */
val future: Future[HttpResponse] = handle(ctx.channel(), req)
import play.api.libs.iteratee.Execution.Implicits.trampoline
lastResponseSent = lastResponseSent.flatMap { /* */
ctx.writeAndFlush(httpResponse) /* Write */
}
}
}
65
PlayRequesthHandler.handle
NettyModelConversion Netty
io.netty.handler.codec.http.HttpRequest Play
play.api.mvc.RequestHeader
Try[RequestHeader]
def handle(
channel: Channel, request: HttpRequest
): Future[HttpResponse] = {
import play.api.libs.iteratee.Execution.Implicits.trampoline
val requestId = RequestIdProvider.requestIDs.incrementAndGet()
//
val tryRequest = modelConversion.convertRequest(
requestId,
channel.remoteAddress().asInstanceOf[InetSocketAddress],
Option(channel.pipeline().get(classOf[SslHandler])),
request
)
} 66
PlayRequestHandler.handle
Success server.getHandlerFor
Right Application handler
Left Result
val (requestHeader, resultOrHandler) = tryRequest match {
case Failure(exception: TooLongFrameException) => clientError(Status
case Failure(exception) => clientError(Status.BAD_REQUEST, exception.g
case Success(untagged) =>
server.getHandlerFor(untagged) match {
case Left(directResult) =>
untagged -> Left(directResult)
case Right((taggedRequestHeader, handler, application)) =>
taggedRequestHeader -> Right((handler, application))
}
}
67
play.core.server.Server#getHandleFor
Handler
Right Handler
Left Result
WebCommand Left Result
DefaultHttpRequestHandler handleForRequest
def getHandlerFor(
request: RequestHeader
): Either[Future[Result], (RequestHeader, Handler, Application)] = {
/* */
application.requestHandler.handlerForRequest(request) match {
case (requestHeader, handler) => Right((requestHeader, handler, applic
}
} 68
DefaultHttpRequestHandler#handleForReque
st
HEAD GET
def handlerForRequest(request: RequestHeader) = {
/* ... */
val (routedRequest, handler) = routeRequest(request) map {
case handler: RequestTaggingHandler =>
(handler.tagRequest(request), handler)
case otherHandler => (request, otherHandler)
} getOrElse {
// We automatically permit HEAD requests against any GETs without the
// add an explicit mapping in Routes
request.method match {
case HttpVerbs.HEAD =>
routeRequest(request.copy(method = HttpVerbs.GET)) match {
/* */
/* */
(routedRequest, filterHandler(rh => handler)(routedRequest))
}
69
routeRequest, Router, RoutesProvider
class DefaultHttpRequestHandler {
def routeRequest(request: RequestHeader): Option[Handler] = {
router.handlerFor(request)
}
}
trait Router {
def handlerFor(request: RequestHeader): Option[Handler] = {
routes.lift(request)
}
}
class RoutesProvider { /* */
lazy val get = {
val prefix = httpConfig.context
val router = Router.load(environment, configuration)
.fold[Router](Router.empty)(injector.instanceOf(_))
router.withPrefix(prefix)
}
}
70
routeRequest
Router BuiltinModule
bind[Router].toProvider[RoutesProvider]
RoutesProvider#get routes-compiler
Routes routes
Gist
71
routes.Routes
conf/routes routes-compiler
target/scala-2.11/routes/main/router/Routes.scala
// Scala
class Routes(
override val errorHandler: play.api.http.HttpErrorHandler,
// @LINE:6
HomeController_0: controllers.HomeController,
// @LINE:8
CountController_3: controllers.CountController,
// @LINE:10
AsyncController_2: controllers.AsyncController,
// @LINE:13
Assets_1: controllers.Assets,
val prefix: String
) extends GeneratedRouter
72
Rourtes.routes
RequestHeader Handler
PartialFunction
def routes: PartialFunction[RequestHeader, Handler] = {
// @LINE:6
case controllers_HomeController_index0_route(params) =>
call {
controllers_HomeController_index0_invoker.call(
HomeController_0.index
)
}
// @LINE:8
case controllers_CountController_count1_route(params) =>
call {
controllers_CountController_count1_invoker.call(
CountController_3.count
)
}
/* */
} 73
Handler src
val (requestHeader, resultOrHandler) = /* */
resultOrHandler match {
//execute normal action
case Right((action: EssentialAction, app)) =>
val recovered = EssentialAction { rh =>
import play.api.libs.iteratee.Execution.Implicits.trampoline
action(rh).recoverWith {
case error => app.errorHandler.onServerError(rh, error)
}
}
handleAction(recovered, requestHeader, request, Some(app))
/* */
}
EssentialAction
EssentialAction { rh => action(rh)
74
EssentialAction
app/controllers/HomeController.scala
package controllers
import javax.inject.{ Inject, Singleton }
import play.api.mvc.{ Controller, Action, AnyContent }
@Singleton
class HomeController @Inject() extends Controller {
def index: Action[AnyContent] = Action { req =>
Ok(views.html.index("Your new application is ready."))
}
}
conf/routes
GET / controllers.HomeController.index
GET /count controllers.CountController.count
GET /message controllers.AsyncController.message
75
Controller Action
def index: Action[AnyContent] = Action { req =>
Ok(views.html.index("Your new application is ready."))
}
Action { req => object Action apply
trait Action[A]
A Action
Action[JsValue] JSON
req Request[A] A
Request Result
76
Action BodyParser
Action[A] BodyParser[A]
BodyParser[A] A
RequestHeader -> RequestBody(Array[Byte]) ->
Result
RequestBody Asynchornous Stream
InputStream Akka-
stream
77
Accumulator[ByteString, Result]
Action RequestHeader => Body => Result
Accumulator
RequestHeader => Accumulator[ByteString,
Result]
Accumulator Akka-stream(Source)
ByteString Array[Byte] (
Akka )
Async
Async FW
78
Action[A]
Action[A] Result
RequestHeader => Accumulator[ByteString, Result]
Play
What is EssentialAction
What is BodyParser
79
EssentialAction
EssentialAction Handler MixIn
Action EssentialAction
EssentialAction
RequestHeader => Accumulator[ByteString, Result]
trait EssentialAction
extends (RequestHeader => Accumulator[ByteString, Result])
with Handler { self =>
def apply() = this
}
trait Action[A] extends EssentialAction
80
: Function
Function
trait Function1[-T1, +R]
RequestHeader => Accumulator[ByteString, Result]
Funtion1[RequestHeader, Accumulator[ByteString,
Result]]
// Intellij -T1 => R
trait EssentialAction
extends Function1[RequestHeader, Accumulator[ByteString, Result]]
81
Action#apply
BodyParser
def apply(request: Request[A]): Future[Result] = {
/* */
}
//
def apply(rh: RequestHeader): Accumulator[ByteString, Result] =
parser(rh).mapFuture { // parser BodyParser
case Left(r) => /* ... (T_T) */
case Right(a) => /* */
val request = Request(rh, a)
apply(request) // apply
}(executionContext)
82
PlayRequestHandler
resultOrHandler match {
//execute normal action
case Right((action: EssentialAction, app)) =>
val recovered = EssentialAction { rh =>
import play.api.libs.iteratee.Execution.Implicits.trampoline
// action(rh) Accumulator
action(rh).recoverWith {
case error => app.errorHandler.onServerError(rh, error)
}
}
//
handleAction(recovered, requestHeader, request, Some(app))
/* */
}
83
handleAction
Action
private def handleAction(action: EssentialAction, requestHeader: RequestHe
request: HttpRequest, app: Option[Application]): Future[HttpResponse
for {
bodyParser <- Future(action(requestHeader))(mat.executionContext)
actionResult <- {
val body = modelConversion.convertRequestBody(request)
(body match {
case None => bodyParser.run()
case Some(source) => bodyParser.run(source)
}).recoverWith { /* ... */ }
}
validatedResult <- { /* Clean and validate the action's result */
convertedResult <- { /* Netty HttpResponse */
modelConversion.convertResult(validatedResult, requestHeader, reques
}
} yield convertedResult
}
84
handleAction
bodyParser Action apply
Accumulator[ByteString, Result]
body Netty
Accumulator run
actionResult Result
Ok
Result Netty HttpResponse
85
src
HttpResponse writeAndFlush
override def channelRead(ctx: ChannelHandlerContext, msg: Object): Unit
msg match {
case req: HttpRequest =>
requestsInFlight.incrementAndGet()
val future: Future[HttpResponse] = handle(ctx.channel(), req)
/* handle */
/* */
import play.api.libs.iteratee.Execution.Implicits.trampoline
lastResponseSent = lastResponseSent.flatMap { /* */
ctx.writeAndFlush(httpResponse)
}
}
}
86
87
88

More Related Content

PDF
Kubernete Meetup Tokyo #18 - Kubebuilder/controller-runtime 入門
Preferred Networks
 
PDF
Maxim Fateev - Beyond the Watermark- On-Demand Backfilling in Flink
Flink Forward
 
PDF
ここが良かったDatadog
tyamane
 
PDF
テストエンジニアの品格 #automatornight
kyon mm
 
PDF
Kafka Streams State Stores Being Persistent
confluent
 
PDF
本当にあったApache Spark障害の話
x1 ichi
 
PDF
大規模Node.jsを支える ロードバランスとオートスケールの独自実装
kidach1
 
PDF
golang profiling の基礎
yuichiro nakazawa
 
Kubernete Meetup Tokyo #18 - Kubebuilder/controller-runtime 入門
Preferred Networks
 
Maxim Fateev - Beyond the Watermark- On-Demand Backfilling in Flink
Flink Forward
 
ここが良かったDatadog
tyamane
 
テストエンジニアの品格 #automatornight
kyon mm
 
Kafka Streams State Stores Being Persistent
confluent
 
本当にあったApache Spark障害の話
x1 ichi
 
大規模Node.jsを支える ロードバランスとオートスケールの独自実装
kidach1
 
golang profiling の基礎
yuichiro nakazawa
 

What's hot (20)

PPTX
FIWARE Orion Context Broker コンテキスト情報管理 (Orion 3.5.0対応)
fisuda
 
PPTX
「関心の分離」と「疎結合」 ソフトウェアアーキテクチャのひとかけら
Atsushi Nakamura
 
PDF
Fess/Elasticsearchを使った業務で使える?全文検索への道
Shinsuke Sugaya
 
PDF
Linuxカーネルを読んで改めて知るプロセスとスレッドの違い
Retrieva inc.
 
PDF
WebSocketのキホン
You_Kinjoh
 
PDF
実践 NestJS
Ayumi Goto
 
PPTX
NGINXをBFF (Backend for Frontend)として利用した話
Hitachi, Ltd. OSS Solution Center.
 
PDF
JCBの Payment as a Service 実現にむけたゼロベースの組織変革とテクニカル・イネーブラー(NTTデータ テクノロジーカンファレンス ...
NTT DATA Technology & Innovation
 
PDF
Apache Arrow - データ処理ツールの次世代プラットフォーム
Kouhei Sutou
 
PDF
Javaのログ出力: 道具と考え方
Taku Miyakawa
 
PDF
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
Hironobu Isoda
 
PDF
DockerとPodmanの比較
Akihiro Suda
 
PDF
Amazon Aurora - Auroraの止まらない進化とその中身
Amazon Web Services Japan
 
PDF
ksqlDB - Stream Processing simplified!
Guido Schmutz
 
PDF
Windowsフォームで大丈夫か?一番良いのを頼む。
Yuya Yamaki
 
PDF
「GraphDB徹底入門」〜構造や仕組み理解から使いどころ・種々のGraphDBの比較まで幅広く〜
Takahiro Inoue
 
PDF
Open Policy Agent (OPA) 入門
Motonori Shindo
 
PDF
イミュータブルデータモデル(入門編)
Yoshitaka Kawashima
 
PDF
形式手法と AWS のおいしい関係。- モデル検査器 Alloy によるインフラ設計技法 #jawsfesta
y_taka_23
 
PPTX
トランザクションをSerializableにする4つの方法
Kumazaki Hiroki
 
FIWARE Orion Context Broker コンテキスト情報管理 (Orion 3.5.0対応)
fisuda
 
「関心の分離」と「疎結合」 ソフトウェアアーキテクチャのひとかけら
Atsushi Nakamura
 
Fess/Elasticsearchを使った業務で使える?全文検索への道
Shinsuke Sugaya
 
Linuxカーネルを読んで改めて知るプロセスとスレッドの違い
Retrieva inc.
 
WebSocketのキホン
You_Kinjoh
 
実践 NestJS
Ayumi Goto
 
NGINXをBFF (Backend for Frontend)として利用した話
Hitachi, Ltd. OSS Solution Center.
 
JCBの Payment as a Service 実現にむけたゼロベースの組織変革とテクニカル・イネーブラー(NTTデータ テクノロジーカンファレンス ...
NTT DATA Technology & Innovation
 
Apache Arrow - データ処理ツールの次世代プラットフォーム
Kouhei Sutou
 
Javaのログ出力: 道具と考え方
Taku Miyakawa
 
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
Hironobu Isoda
 
DockerとPodmanの比較
Akihiro Suda
 
Amazon Aurora - Auroraの止まらない進化とその中身
Amazon Web Services Japan
 
ksqlDB - Stream Processing simplified!
Guido Schmutz
 
Windowsフォームで大丈夫か?一番良いのを頼む。
Yuya Yamaki
 
「GraphDB徹底入門」〜構造や仕組み理解から使いどころ・種々のGraphDBの比較まで幅広く〜
Takahiro Inoue
 
Open Policy Agent (OPA) 入門
Motonori Shindo
 
イミュータブルデータモデル(入門編)
Yoshitaka Kawashima
 
形式手法と AWS のおいしい関係。- モデル検査器 Alloy によるインフラ設計技法 #jawsfesta
y_taka_23
 
トランザクションをSerializableにする4つの方法
Kumazaki Hiroki
 
Ad

Viewers also liked (20)

PDF
並行実行制御の最適化手法
Sho Nakazono
 
PDF
ハイブリッド言語Scalaを使う
bpstudy
 
PDF
アクセシブルなモーダルダイアログの作り方 #scripty05
Yahoo!デベロッパーネットワーク
 
PDF
Functional and Algebraic Domain Modeling
Debasish Ghosh
 
PDF
やさしいIteratee入門
Takashi Kawachi
 
PDF
PlayFramework 2.0 Javaと WebSocketでつくる リアルタイムMVC Webアプリケーション
Kazuhiro Hara
 
PDF
とにかく対立したい子ども達へのクラウドを使った対処法
広告制作会社
 
PPTX
akka-streamのマイクロサービスへの適用
Nyle Inc.(ナイル株式会社)
 
PDF
ScalaMatsuri 2016
Yoshitaka Fujii
 
PDF
Scala profiling
Filippo Pacifici
 
PPTX
Scala Refactoring for Fun and Profit (Japanese subtitles)
Tomer Gabel
 
PDF
Functional Programming For All - Scala Matsuri 2016
Zachary Abbott
 
PDF
Contributing to Scala OSS from East Asia #ScalaMatsuri
Kazuhiro Sera
 
PPTX
Rubyからscalaに変えるべき15の理由
Yukishige Nakajo
 
PDF
Go Workshop Day 1
Hiroaki Kamei
 
PDF
The Play Framework at LinkedIn
Yevgeniy Brikman
 
PDF
あなたのScalaを爆速にする7つの方法(日本語版)
x1 ichi
 
PDF
Javaアプリケーションサーバ 構築・運用の勘所
Takahiro YAMADA
 
PDF
Pythonによるwebアプリケーション入門 - Django編-
Hironori Sekine
 
PDF
1.1.7 Система огнестойких проходок Vulcan
Igor Golovin
 
並行実行制御の最適化手法
Sho Nakazono
 
ハイブリッド言語Scalaを使う
bpstudy
 
アクセシブルなモーダルダイアログの作り方 #scripty05
Yahoo!デベロッパーネットワーク
 
Functional and Algebraic Domain Modeling
Debasish Ghosh
 
やさしいIteratee入門
Takashi Kawachi
 
PlayFramework 2.0 Javaと WebSocketでつくる リアルタイムMVC Webアプリケーション
Kazuhiro Hara
 
とにかく対立したい子ども達へのクラウドを使った対処法
広告制作会社
 
akka-streamのマイクロサービスへの適用
Nyle Inc.(ナイル株式会社)
 
ScalaMatsuri 2016
Yoshitaka Fujii
 
Scala profiling
Filippo Pacifici
 
Scala Refactoring for Fun and Profit (Japanese subtitles)
Tomer Gabel
 
Functional Programming For All - Scala Matsuri 2016
Zachary Abbott
 
Contributing to Scala OSS from East Asia #ScalaMatsuri
Kazuhiro Sera
 
Rubyからscalaに変えるべき15の理由
Yukishige Nakajo
 
Go Workshop Day 1
Hiroaki Kamei
 
The Play Framework at LinkedIn
Yevgeniy Brikman
 
あなたのScalaを爆速にする7つの方法(日本語版)
x1 ichi
 
Javaアプリケーションサーバ 構築・運用の勘所
Takahiro YAMADA
 
Pythonによるwebアプリケーション入門 - Django編-
Hironori Sekine
 
1.1.7 Система огнестойких проходок Vulcan
Igor Golovin
 
Ad

Similar to こわくないよ❤️ Playframeworkソースコードリーディング入門 (20)

PDF
Go Web Development
Cheng-Yi Yu
 
PDF
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
ConFoo
 
PDF
TDC2018SP | Trilha Go - Processando analise genetica em background com Go
tdc-globalcode
 
PPTX
Taking Jenkins Pipeline to the Extreme
yinonavraham
 
PPT
nodejs tutorial foor free download from academia
rani marri
 
PPTX
FP - Découverte de Play Framework Scala
Kévin Margueritte
 
PDF
OSCON 2014 - API Ecosystem with Scala, Scalatra, and Swagger at Netflix
Manish Pandit
 
PDF
WebTalk - Implementing Web Services with a dedicated Java daemon
Geert Van Pamel
 
PDF
Node intro
cloudhead
 
PDF
Rntb20200805
t k
 
PDF
Akka Cluster in Java - JCConf 2015
Jiayun Zhou
 
PDF
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Tsuyoshi Yamamoto
 
PPT
Play!ng with scala
Siarzh Miadzvedzeu
 
PDF
Dev ninja -> vagrant + virtualbox + chef-solo + git + ec2
Yros
 
PDF
soft-shake.ch - Hands on Node.js
soft-shake.ch
 
PPTX
Binary Studio Academy: Concurrency in C# 5.0
Binary Studio
 
PDF
Construire une application JavaFX 8 avec gradle
Thierry Wasylczenko
 
KEY
Railsconf2011 deployment tips_for_slideshare
tomcopeland
 
KEY
Writing robust Node.js applications
Tom Croucher
 
PPTX
Debugging & profiling node.js
tomasperezv
 
Go Web Development
Cheng-Yi Yu
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
ConFoo
 
TDC2018SP | Trilha Go - Processando analise genetica em background com Go
tdc-globalcode
 
Taking Jenkins Pipeline to the Extreme
yinonavraham
 
nodejs tutorial foor free download from academia
rani marri
 
FP - Découverte de Play Framework Scala
Kévin Margueritte
 
OSCON 2014 - API Ecosystem with Scala, Scalatra, and Swagger at Netflix
Manish Pandit
 
WebTalk - Implementing Web Services with a dedicated Java daemon
Geert Van Pamel
 
Node intro
cloudhead
 
Rntb20200805
t k
 
Akka Cluster in Java - JCConf 2015
Jiayun Zhou
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Tsuyoshi Yamamoto
 
Play!ng with scala
Siarzh Miadzvedzeu
 
Dev ninja -> vagrant + virtualbox + chef-solo + git + ec2
Yros
 
soft-shake.ch - Hands on Node.js
soft-shake.ch
 
Binary Studio Academy: Concurrency in C# 5.0
Binary Studio
 
Construire une application JavaFX 8 avec gradle
Thierry Wasylczenko
 
Railsconf2011 deployment tips_for_slideshare
tomcopeland
 
Writing robust Node.js applications
Tom Croucher
 
Debugging & profiling node.js
tomasperezv
 

Recently uploaded (20)

PPTX
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
PDF
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
PDF
Tea4chat - another LLM Project by Kerem Atam
a0m0rajab1
 
PPTX
Introduction to Flutter by Ayush Desai.pptx
ayushdesai204
 
PDF
Peak of Data & AI Encore - Real-Time Insights & Scalable Editing with ArcGIS
Safe Software
 
PDF
Get More from Fiori Automation - What’s New, What Works, and What’s Next.pdf
Precisely
 
PDF
NewMind AI Weekly Chronicles - July'25 - Week IV
NewMind AI
 
PDF
Using Anchore and DefectDojo to Stand Up Your DevSecOps Function
Anchore
 
PDF
SparkLabs Primer on Artificial Intelligence 2025
SparkLabs Group
 
PDF
Brief History of Internet - Early Days of Internet
sutharharshit158
 
PPTX
Dev Dives: Automate, test, and deploy in one place—with Unified Developer Exp...
AndreeaTom
 
PDF
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
PPTX
IT Runs Better with ThousandEyes AI-driven Assurance
ThousandEyes
 
PPTX
New ThousandEyes Product Innovations: Cisco Live June 2025
ThousandEyes
 
PDF
Orbitly Pitch Deck|A Mission-Driven Platform for Side Project Collaboration (...
zz41354899
 
PPTX
OA presentation.pptx OA presentation.pptx
pateldhruv002338
 
PDF
How Open Source Changed My Career by abdelrahman ismail
a0m0rajab1
 
PPTX
Agile Chennai 18-19 July 2025 Ideathon | AI Powered Microfinance Literacy Gui...
AgileNetwork
 
PDF
OFFOFFBOX™ – A New Era for African Film | Startup Presentation
ambaicciwalkerbrian
 
PDF
Doc9.....................................
SofiaCollazos
 
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
Tea4chat - another LLM Project by Kerem Atam
a0m0rajab1
 
Introduction to Flutter by Ayush Desai.pptx
ayushdesai204
 
Peak of Data & AI Encore - Real-Time Insights & Scalable Editing with ArcGIS
Safe Software
 
Get More from Fiori Automation - What’s New, What Works, and What’s Next.pdf
Precisely
 
NewMind AI Weekly Chronicles - July'25 - Week IV
NewMind AI
 
Using Anchore and DefectDojo to Stand Up Your DevSecOps Function
Anchore
 
SparkLabs Primer on Artificial Intelligence 2025
SparkLabs Group
 
Brief History of Internet - Early Days of Internet
sutharharshit158
 
Dev Dives: Automate, test, and deploy in one place—with Unified Developer Exp...
AndreeaTom
 
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
IT Runs Better with ThousandEyes AI-driven Assurance
ThousandEyes
 
New ThousandEyes Product Innovations: Cisco Live June 2025
ThousandEyes
 
Orbitly Pitch Deck|A Mission-Driven Platform for Side Project Collaboration (...
zz41354899
 
OA presentation.pptx OA presentation.pptx
pateldhruv002338
 
How Open Source Changed My Career by abdelrahman ismail
a0m0rajab1
 
Agile Chennai 18-19 July 2025 Ideathon | AI Powered Microfinance Literacy Gui...
AgileNetwork
 
OFFOFFBOX™ – A New Era for African Film | Startup Presentation
ambaicciwalkerbrian
 
Doc9.....................................
SofiaCollazos
 

こわくないよ❤️ Playframeworkソースコードリーディング入門

  • 3. 3
  • 6. 6
  • 7. 7
  • 9. framework/src build-link play-functional play-netty-utils fork-run play-integration-test play-server fork-run-protocol play-java play-specs2 iteratees play-java-jdbc play-streams play play-java-jpa play-test play-akka-http-server play-java-ws play-ws play-cache play-jdbc routes-compiler play-datacommons play-jdbc-api run-support play-docs play-jdbc-evolutions sbt-fork-run-plugin play-docs-sbt-plugin play-json sbt-plugin play-exceptions play-logback play-filters-helpers play-netty-server 9
  • 10. 10
  • 11. 1. 2. Action Controller 3. Json 4. Applicative 11
  • 12. 12
  • 14. main ag 'def main(' src 3 fork-run sbt src/fork-run/src/main/scala/play/forkrun/ForkRun.scala 28: def main(args: Array[String]): Unit = { src/play-netty-server/src/main/scala/play/core/server/NettyServer.scala 298: def main(args: Array[String]) { src/play-server/src/main/scala/play/core/server/ProdServerStart.scala 20: def main(args: Array[String]) { 14
  • 15. NettyServer def main(args: Array[String]) { System.err.println( s"NettyServer.main is deprecated. Please start your Play server with the ${ProdServerStart.getClass.getName}.main." ) ProdServerStart.main(args) } ProdServerStart 15
  • 16. main grep play-scala sbt dist ProdServerStart declare -a app_mainclass=("play.core.server.ProdServerStart") # java -jar play.core.server.ProdServerStart 16
  • 17. framework/src/play- server/src/main/scala/play/core/server/ProdServe rStart.scala 2 object ProdServerStart { /** * Start a prod mode server from the command line. */ def main(args: Array[String]) { val process = new RealServerProcess(args) start(process) } // ... } 17
  • 18. RealServerProcess ServerProcess JVM /** * Abstracts a JVM process so it can be mocked for testing or to * isolate pseudo-processes within a VM. Code using this class * should use the methods in this class instead of methods like * `System.getProperties()`, `System.exit()`, etc. */ trait ServerProcess class RealServerProcess( val args: Seq[String] ) extends ServerProcess 18
  • 19. ServerProcess trait ServerProcess { /** The ClassLoader that should be used */ def classLoader: ClassLoader /** The command line arguments the process as invoked with */ def args: Seq[String] /** The process's system properties */ def properties: Properties /** Helper for getting properties */ final def prop(name: String): Option[String] = Option(properties.getProp /** The process's id */ def pid: Option[String] /** Add a hook to run when the process shuts down */ def addShutdownHook(hook: => Unit): Unit /** Exit the process with a message and optional cause and return code * def exit(message: String, cause: Option[Throwable] = None, returnCode: } 19
  • 20. RealServerProcess pid class RealServerProcess(val args: Seq[String]) extends ServerProcess def classLoader: ClassLoader = Thread.currentThread.getContextClassLoader def properties: Properties = System.getProperties def pid: Option[String] = ManagementFactory.getRuntimeMXBean .getName.split('@').headOption def addShutdownHook(hook: => Unit): Unit = { Runtime.getRuntime.addShutdownHook(new Thread { override def run() = hook }) } def exit(message: String, cause: Option[Throwable] = None, returnCode: /* ... */ } } 20
  • 21. ProdServerStart#start def main(args: Array[String]): Unit = { val process = new RealServerProcess(args) // start(process) } // 4 def start(process: ServerProcess): ServerWithStop = { try { // 1. Read settings val config: ServerConfig = readServerConfigSettings(process) // 2. Create a PID file before we do any real work val pidFile = createPidFile(process, config.configuration) // 3. Start the application val application: Application = { /* */ } Play.start(application) // 4. Start the server val serverProvider: ServerProvider = ServerProvider.fromConfiguration( val server = serverProvider.createServer(config, application) // ... } 21
  • 22. ProdServerStart#start 1. 2. PID(Process ID) 3. play.api.Application 4. HTTP 22
  • 23. 1. src !(option).isDefined (option).isEmpty def readServerConfigSettings(process: ServerProcess): ServerConfig = { val configuration: Configuration = { /* */ } val rootDir: File = { /* */ } def parsePort(portType: String): Option[Int] = { // configuration http/https parse } val httpPort = parsePort("http") val httpsPort = parsePort("https") if (!(httpPort orElse httpsPort).isDefined) throw ServerStartException val address = configuration.getString("play.server.http.address") .getOrElse("0.0.0.0") ServerConfig(/* */) } 23
  • 24. {} rootDirArg val configuration: Configuration = { // rootDir Some(java.io.File) val rootDirArg: Option[File] = process.args.headOption.map(new File(_)) // Map.empty // play.server.dir // "play.server.dir" -> "${dir.getAbsolutePath}" Map val rootDirConfig = rootDirArg.fold(Map.empty[String, String]) (dir => ServerConfig.rootDirConfig(dir)) // Configuration . Configuration.load( process.classLoader, process.properties, rootDirConfig, true ) } 24
  • 25. play.api.Con guration object Configuration { private[play] def load( classLoader: ClassLoader, properties: Properties, directSettings: Map[String, AnyRef], allowMissingApplicationConf: Boolean): Configuration = { try { // 1. SystemProperty val systemPropertyConfig = /* */ // 2. Map[String, String] val directConfig: Config = ConfigFactory.parseMap(directSettings.asJ // 3. conf/application.conf val applicationConfig: Config = /* */ // 4. play overrides conf val playOverridesConfig: Config = ConfigFactory.parseResources(class // 5. reference.conf val referenceConfig: Config = ConfigFactory.parseResources(classLoad // ... 25
  • 26. 5 1. SystemProperty (System.getProperty) 2. DirectCon g: 3. conf/application.conf ) 4. play overrides con g Akka Play 5. reference.conf Play 26
  • 27. Combine reduceLeft(_ withFallback _) // Combine all the config together into one big config val combinedConfig: Config = Seq( systemPropertyConfig, directConfig, applicationConfig, playOverridesConfig, referenceConfig ).reduceLeft(_ withFallback _) // resolve ${foo.bar} // val resolvedConfig = combinedConfig.resolve Configuration(resolvedConfig) } catch { case e: ConfigException => throw configError(e.origin, e.getMessage, } 27
  • 28. rootDir Option.get getOrElse(throw new Exception) val rootDir: File = { // play.server.dir val path = configuration .getString("play.server.dir") .getOrElse(throw ServerStartException("No root server path supplied" val file = new File(path) if (!(file.exists && file.isDirectory)) { throw ServerStartException(s"Bad root server path: $path") } file } 28
  • 29. http/https port http/https def parsePort(portType: String): Option[Int] = { configuration.getString(s"play.server.${portType}.port").flatMap { case "disabled" => None case str => val i = try Integer.parseInt(str) catch { case _: NumberFormatException => throw ServerStartException( } Some(i) } } val httpPort = parsePort("http") val httpsPort = parsePort("https") if (!(httpPort orElse httpsPort).isDefined) throw ServerStartException 29
  • 30. TypeSafe Con g 5 reduceLeft Combine {} Option.get getOrElse(throw new Exception) 30
  • 31. 2. PID src // 2. Create a PID file before we do any real work val pidFile = createPidFile(process, config.configuration) def createPidFile(process: ServerProcess, configuration: Configuration // "play.server.pidfile.path" val pidFilePath = configuration .getString("play.server.pidfile.path") .getOrElse(throw ServerStartException("Pid file path // "play.server.pidfile.path" "/dev/null" if (pidFilePath == "/dev/null") None else { val pidFile = new File(pidFilePath).getAbsoluteFile // PID if (pidFile.exists) { throw ServerStartException(s"This application is already running (Or } val pid = process.pid getOrElse (throw ServerStartException("Couldn't val out = new FileOutputStream(pidFile) try out.write(pid.getBytes) finally out.close() Some(pidFile) } } 31
  • 32. 3. Application src play.api.Application UT // 3. Start the application val application: Application = { val environment = Environment(config.rootDir, process.classLoader, Mode.Prod) val context = ApplicationLoader.createContext(environment) val loader = ApplicationLoader(context) loader.load(context) } Play.start(application) 32
  • 33. play.api.Environment case class /** * The environment for the application. * * Captures concerns relating to the classloader and * the filesystem for the application. * * @param * @param classLoader * @param mode Prod/Dev/Test 3 ) */ case class Environment( rootPath: File, classLoader: ClassLoader, mode: Mode.Mode ) 33
  • 34. ApplicationLoader#createContext SourceMapper: WebCommands: Evolutions /** * Create an application loading context. * Locates and loads the necessary configuration files for the application */ def createContext(environment: Environment, initialSettings: Map[String, AnyRef] = Map.empty[String, AnyRef], sourceMapper: Option[SourceMapper] = None, webCommands: WebCommands = new DefaultWebCommands) = { val configuration = Configuration.load(environment, initialSettings) Context(environment, sourceMapper, webCommands, configuration) } 34
  • 35. ApplicationLoader#apply Scala/Java // Locate and instantiate the ApplicationLoader. def apply(context: Context): ApplicationLoader = { Reflect.configuredClass[ ApplicationLoader, play.ApplicationLoader, GuiceApplicationLoader ]( context.environment, PlayConfig(context.initialConfiguration), "play.application.loader", classOf[GuiceApplicationLoader].getName ) match { case None => /* */ new GuiceApplicationLoader /* */ case Some(Left(scalaClass)) => /* Scala */ scalaClass.newInstance case Some(Right(javaClass)) => /* Java */ javaClass.newInstance } } 35
  • 36. ApplicationLoader ApplicationLoader Play Runtime DI Compile time DI play.application.loader ApplicationLoader Compile time DI Scala trait Java interface Guice Runtime DI 36
  • 37. play.api.inject.guice.GuiceApplicationLoader Guice def this() /** * An ApplicationLoader that uses Guice to bootstrap the application. * * Subclasses can override the `builder` and `overrides` methods. */ class GuiceApplicationLoader( protected val initialBuilder: GuiceApplicationBuilder ) extends ApplicationLoader { // empty constructor needed for instantiating via reflection def this() = this(new GuiceApplicationBuilder) } 37
  • 38. GuiceApplicationBuilder Application Guice final case class GuiceApplicationBuilder( environment: Environment = Environment.simple(), configuration: Configuration = Configuration.empty, modules: Seq[GuiceableModule] = Seq.empty, overrides: Seq[GuiceableModule] = Seq.empty, disabled: Seq[Class[_]] = Seq.empty, binderOptions: Set[BinderOption] = BinderOption.defaults, eagerly: Boolean = false, loadConfiguration: Environment => Configuration = Configuration.load, global: Option[GlobalSettings.Deprecated] = None, loadModules: (Environment, Configuration) => Seq[GuiceableModule] = ) extends GuiceBuilder[GuiceApplicationBuilder]( environment, configuration, modules, overrides, disabled, binderOptions, ) 38
  • 39. src loader GuiceApplicationLoader loader GuiceApplicationBuilder loader.load // Start the application val application: Application = { val environment = Environment(config.rootDir, process.classLoader, val context = ApplicationLoader.createContext(environment) val loader = ApplicationLoader(context) // loader.load(context) } 39
  • 40. GuiceApplicationLoader#load GuiceApplicationBuilder build builder Con g Environment override final def load( context: ApplicationLoader.Context ): Application = { builder(context).build } protected def builder( context: ApplicationLoader.Context ): GuiceApplicationBuilder = { initialBuilder.disableCircularProxies() .in(context.environment) .loadConfig(context.initialConfiguration) .overrides(overrides(context): _*) } protected def overrides(context: ApplicationLoader.Context): Seq[Guiceable GuiceApplicationLoader.defaultOverrides(context) } 40
  • 41. GuiceApplicationLoader#defaultOverrides Guice Module object GuiceApplicationLoader { /** * The default overrides provided by the Scala and Java GuiceApplication */ def defaultOverrides( context: ApplicationLoader.Context ): Seq[GuiceableModule] = { Seq( bind[OptionalSourceMapper] to new OptionalSourceMapper(context.sourc bind[WebCommands] to context.webCommands, bind[DefaultApplicationLifecycle] to context.lifecycle) } } 41
  • 42. GuiceApplicationBuilder#build injector Application injector(): play.api.injector.PlayInjector def injector Guice Injector override final def load( context: ApplicationLoader.Context ): Application = { builder(context).build // } /** * Create a new Play Application using this configured builder. */ def build(): Application = injector().instanceOf[Application] 42
  • 43. GuiceBuilder#injector GuiceApplicationBuilder GuiceBuilder Guice Injector /** * Create a Play Injector backed by Guice using this configured builder. */ def injector(): PlayInjector = { try { val stage = /* */ // Injector(com.google.inject.Injector) val guiceInjector = Guice.createInjector(stage, applicationModule()) // Injector PlayInjector guiceInjector.getInstance(classOf[PlayInjector]) } catch { // ... } } 43
  • 44. GuiceBuilder#applicationModule PlayInjector GuiceInjector conf Module // Create a Play Injector backed by Guice using this configured builder. def applicationModule(): GuiceModule = createModule() def createModule(): GuiceModule = { import scala.collection.JavaConverters._ val injectorModule = GuiceableModule.guice(Seq( bind[PlayInjector].to[GuiceInjector], bind[play.inject.Injector].to[play.inject.DelegateInjector] ), binderOptions) val enabledModules = modules.map(_.disable(disabled)) val bindingModules = GuiceableModule.guiced(environment, configuration val overrideModules = GuiceableModule.guiced(environment, configuratio GuiceModules.`override`(bindingModules.asJava).`with`(overrideModules. } 44
  • 45. GuiceInjector GuiceInjector Guice Injector instanceOf[T] injector().instanceOf[Application] class GuiceInjector @Inject() ( injector: com.google.inject.Injector ) extends PlayInjector { def instanceOf[T](implicit ct: ClassTag[T]) = instanceOf(ct.runtimeClass def instanceOf[T](clazz: Class[T]) = injector.getInstance(clazz) def instanceOf[T](key: BindingKey[T]) = injector.getInstance(GuiceKey } 45
  • 46. Application Guice Injector con g framework/src/play/src/main/resources/reference .conf play { modules { # The enabled modules that should be automatically loaded. enabled += "play.api.inject.BuiltinModule" # enabled += "play.api.i18n.I18nModule" # A way to disable modules that are automatically enabled disabled = [] } } 46
  • 47. play.api.inject.BuiltinModule Provider class BuiltinModule extends Module { def bindings(env: Environment, configuration: Configuration): Seq[ // ... ... Seq( bind[Environment] to env, bind[Configuration].toProvider[ConfigurationProvider], bind[DefaultApplicationLifecycle].toSelf, bind[ApplicationLifecycle].to(bind[DefaultApplicationLifecycle bind[Application].to[DefaultApplication], // bind[play.Application].to[play.DefaultApplication], bind[ActorSystem].toProvider[ActorSystemProvider], bind[Materializer].toProvider[MaterializerProvider], bind[ExecutionContext].to[ExecutionContextExecutor], // ... ... } 47
  • 48. DefaultApplication Inject @Singleton class DefaultApplication @Inject() ( environment: Environment, applicationLifecycle: DefaultApplicationLifecycle, override val injector: Injector, override val configuration: Configuration, override val requestHandler: HttpRequestHandler, override val errorHandler: HttpErrorHandler, override val actorSystem: ActorSystem, override val materializer: Materializer) extends Application { def path = environment.rootPath def classloader = environment.classLoader def mode = environment.mode def stop() = applicationLifecycle.stop() } 48
  • 49. src // 3. Start the application val application: Application = { val environment = Environment(config.rootDir, process.classLoader, Mode.Prod) val context = ApplicationLoader.createContext(environment) val loader = ApplicationLoader(context) loader.load(context) } // Play.start(application) 49
  • 50. play.api.Play#start @volatile private[play] var _currentApp: Application = _ def start(app: Application) { stop(_currentApp) _currentApp = app Threads.withContextClassLoader(app.classloader) { app.global.beforeStart(app) app.routes app.global.onStart(app) } app.mode match { case Mode.Test => // started case mode => logger.info("Application started (" + mode + ")") } } 50
  • 52. HTTP netty src ServerProvider Server hook PID val serverProvider: ServerProvider = ServerProvider.fromConfiguration( process.classLoader, config.configuration ) val server = serverProvider.createServer(config, application) process.addShutdownHook { server.stop() pidFile.foreach(_.delete()) assert(!pidFile.exists(_.exists), "PID file should not exist!") } server 52
  • 53. ServerProvider#fromCon guration play.server.provider new framework/src/play-netty- server/src/main/resources/reference.conf play.core.server.NettyServerProvider akka-http experimental def fromConfiguration(classLoader: ClassLoader, configuration: Configurati val ClassNameConfigKey = "play.server.provider" val className: String = configuration.getString(ClassNameConfigKey val clazz = try classLoader.loadClass(className) catch { // } val ctor = try clazz.getConstructor() catch { // } ctor.newInstance().asInstanceOf[ServerProvider] } 53
  • 54. NettyServerProvider#createServer play.core.server.NettyServer new def createServer(context: ServerProvider.Context) = new NettyServer( context.config, context.appProvider, context.stopHook, context.actorSystem )( context.materializer ) 54
  • 55. NettyServer val bind new src class NettyServer(/* */) { // Maybe the HTTP server channel private val httpChannel = config.port.map(bindChannel(_, secure = false)) // Maybe the HTTPS server channel private val httpsChannel = config.sslPort.map(bindChannel(_, secure = true)) private def bindChannel(port: Int, secure: Boolean): Channel = { val protocolName = if (secure) "HTTPS" else "HTTP" val address = new InetSocketAddress(config.address, port) val (serverChannel, channelSource) = bind(address) channelSource.runWith(channelSink(port = port, secure = secure)) /* */ } } 55
  • 56. NettyServer#bind netty akka-stream Source HTTP Source OK Source => Sink private def bind(address: InetSocketAddress): (Channel, Source[Channel val serverChannelEventLoop = eventLoop.next // Watches for channel events, and pushes them through a reactive stre val channelPublisher = new HandlerPublisher(serverChannelEventLoop, cl /* */ val channel = bootstrap.bind.await().channel() allChannels.add(channel) (channel, Source.fromPublisher(channelPublisher)) } 56
  • 57. NettyServer#channelSink input(Source) Sink PlayRequestHandler val (serverChannel, channelSource) = bind(address) // channelSource.runWith(channelSink(port = port, secure = secure)) // : private def channelSink(port: Int, secure: Boolean): Sink[Channel, Future Sink.foreach[Channel] { (connChannel: Channel) => val pipeline = connChannel.pipeline() /* ... */ pipeline.addLast("decoder", new HttpRequestDecoder(maxInitialLineLengt val requestHandler = new PlayRequestHandler(this) // pipeline.addLast("http-handler", new HttpStreamsServerHandler(Seq pipeline.addLast("request-handler", requestHandler)      /* ... */ } } 57
  • 58. NettyServer akka-stream netty (Source/Sink) play.server.netty.transport native epoll (Linux) PlayRequestHandler ( private lazy val transport = conf.getString("transport") match { case "native" => Native case "jdk" => Jdk } 58
  • 59. 59
  • 61. Typsafe Con g/Google Guice/JBoss Netty/Akka/Akka-Stream sbt run ProdServer 61
  • 62. 62
  • 63. 63
  • 64. PlayRequestHandler ChannelInboundHandlerAdapter netty class Netty def channelRead def handle private[play] class PlayRequestHandler( val server: NettyServer ) extends ChannelInboundHandlerAdapter { // ... override def channelRead(ctx: ChannelHandlerContext, msg: Object): // Handle the given request. def handle( channel: Channel, request: HttpRequest ): Future[HttpResponse] = { /* */ } } 64
  • 65. PlayRequestHandler.channelRead Netty HttpRequest handle Write trampoline ExecutionContext override def channelRead(ctx: ChannelHandlerContext, msg: Object): Unit logger.trace(s"channelRead: ctx = ${ctx}, msg = ${msg}") msg match { case req: HttpRequest => requestsInFlight.incrementAndGet() /* handle */ val future: Future[HttpResponse] = handle(ctx.channel(), req) import play.api.libs.iteratee.Execution.Implicits.trampoline lastResponseSent = lastResponseSent.flatMap { /* */ ctx.writeAndFlush(httpResponse) /* Write */ } } } 65
  • 66. PlayRequesthHandler.handle NettyModelConversion Netty io.netty.handler.codec.http.HttpRequest Play play.api.mvc.RequestHeader Try[RequestHeader] def handle( channel: Channel, request: HttpRequest ): Future[HttpResponse] = { import play.api.libs.iteratee.Execution.Implicits.trampoline val requestId = RequestIdProvider.requestIDs.incrementAndGet() // val tryRequest = modelConversion.convertRequest( requestId, channel.remoteAddress().asInstanceOf[InetSocketAddress], Option(channel.pipeline().get(classOf[SslHandler])), request ) } 66
  • 67. PlayRequestHandler.handle Success server.getHandlerFor Right Application handler Left Result val (requestHeader, resultOrHandler) = tryRequest match { case Failure(exception: TooLongFrameException) => clientError(Status case Failure(exception) => clientError(Status.BAD_REQUEST, exception.g case Success(untagged) => server.getHandlerFor(untagged) match { case Left(directResult) => untagged -> Left(directResult) case Right((taggedRequestHeader, handler, application)) => taggedRequestHeader -> Right((handler, application)) } } 67
  • 68. play.core.server.Server#getHandleFor Handler Right Handler Left Result WebCommand Left Result DefaultHttpRequestHandler handleForRequest def getHandlerFor( request: RequestHeader ): Either[Future[Result], (RequestHeader, Handler, Application)] = { /* */ application.requestHandler.handlerForRequest(request) match { case (requestHeader, handler) => Right((requestHeader, handler, applic } } 68
  • 69. DefaultHttpRequestHandler#handleForReque st HEAD GET def handlerForRequest(request: RequestHeader) = { /* ... */ val (routedRequest, handler) = routeRequest(request) map { case handler: RequestTaggingHandler => (handler.tagRequest(request), handler) case otherHandler => (request, otherHandler) } getOrElse { // We automatically permit HEAD requests against any GETs without the // add an explicit mapping in Routes request.method match { case HttpVerbs.HEAD => routeRequest(request.copy(method = HttpVerbs.GET)) match { /* */ /* */ (routedRequest, filterHandler(rh => handler)(routedRequest)) } 69
  • 70. routeRequest, Router, RoutesProvider class DefaultHttpRequestHandler { def routeRequest(request: RequestHeader): Option[Handler] = { router.handlerFor(request) } } trait Router { def handlerFor(request: RequestHeader): Option[Handler] = { routes.lift(request) } } class RoutesProvider { /* */ lazy val get = { val prefix = httpConfig.context val router = Router.load(environment, configuration) .fold[Router](Router.empty)(injector.instanceOf(_)) router.withPrefix(prefix) } } 70
  • 72. routes.Routes conf/routes routes-compiler target/scala-2.11/routes/main/router/Routes.scala // Scala class Routes( override val errorHandler: play.api.http.HttpErrorHandler, // @LINE:6 HomeController_0: controllers.HomeController, // @LINE:8 CountController_3: controllers.CountController, // @LINE:10 AsyncController_2: controllers.AsyncController, // @LINE:13 Assets_1: controllers.Assets, val prefix: String ) extends GeneratedRouter 72
  • 73. Rourtes.routes RequestHeader Handler PartialFunction def routes: PartialFunction[RequestHeader, Handler] = { // @LINE:6 case controllers_HomeController_index0_route(params) => call { controllers_HomeController_index0_invoker.call( HomeController_0.index ) } // @LINE:8 case controllers_CountController_count1_route(params) => call { controllers_CountController_count1_invoker.call( CountController_3.count ) } /* */ } 73
  • 74. Handler src val (requestHeader, resultOrHandler) = /* */ resultOrHandler match { //execute normal action case Right((action: EssentialAction, app)) => val recovered = EssentialAction { rh => import play.api.libs.iteratee.Execution.Implicits.trampoline action(rh).recoverWith { case error => app.errorHandler.onServerError(rh, error) } } handleAction(recovered, requestHeader, request, Some(app)) /* */ } EssentialAction EssentialAction { rh => action(rh) 74
  • 75. EssentialAction app/controllers/HomeController.scala package controllers import javax.inject.{ Inject, Singleton } import play.api.mvc.{ Controller, Action, AnyContent } @Singleton class HomeController @Inject() extends Controller { def index: Action[AnyContent] = Action { req => Ok(views.html.index("Your new application is ready.")) } } conf/routes GET / controllers.HomeController.index GET /count controllers.CountController.count GET /message controllers.AsyncController.message 75
  • 76. Controller Action def index: Action[AnyContent] = Action { req => Ok(views.html.index("Your new application is ready.")) } Action { req => object Action apply trait Action[A] A Action Action[JsValue] JSON req Request[A] A Request Result 76
  • 77. Action BodyParser Action[A] BodyParser[A] BodyParser[A] A RequestHeader -> RequestBody(Array[Byte]) -> Result RequestBody Asynchornous Stream InputStream Akka- stream 77
  • 78. Accumulator[ByteString, Result] Action RequestHeader => Body => Result Accumulator RequestHeader => Accumulator[ByteString, Result] Accumulator Akka-stream(Source) ByteString Array[Byte] ( Akka ) Async Async FW 78
  • 79. Action[A] Action[A] Result RequestHeader => Accumulator[ByteString, Result] Play What is EssentialAction What is BodyParser 79
  • 80. EssentialAction EssentialAction Handler MixIn Action EssentialAction EssentialAction RequestHeader => Accumulator[ByteString, Result] trait EssentialAction extends (RequestHeader => Accumulator[ByteString, Result]) with Handler { self => def apply() = this } trait Action[A] extends EssentialAction 80
  • 81. : Function Function trait Function1[-T1, +R] RequestHeader => Accumulator[ByteString, Result] Funtion1[RequestHeader, Accumulator[ByteString, Result]] // Intellij -T1 => R trait EssentialAction extends Function1[RequestHeader, Accumulator[ByteString, Result]] 81
  • 82. Action#apply BodyParser def apply(request: Request[A]): Future[Result] = { /* */ } // def apply(rh: RequestHeader): Accumulator[ByteString, Result] = parser(rh).mapFuture { // parser BodyParser case Left(r) => /* ... (T_T) */ case Right(a) => /* */ val request = Request(rh, a) apply(request) // apply }(executionContext) 82
  • 83. PlayRequestHandler resultOrHandler match { //execute normal action case Right((action: EssentialAction, app)) => val recovered = EssentialAction { rh => import play.api.libs.iteratee.Execution.Implicits.trampoline // action(rh) Accumulator action(rh).recoverWith { case error => app.errorHandler.onServerError(rh, error) } } // handleAction(recovered, requestHeader, request, Some(app)) /* */ } 83
  • 84. handleAction Action private def handleAction(action: EssentialAction, requestHeader: RequestHe request: HttpRequest, app: Option[Application]): Future[HttpResponse for { bodyParser <- Future(action(requestHeader))(mat.executionContext) actionResult <- { val body = modelConversion.convertRequestBody(request) (body match { case None => bodyParser.run() case Some(source) => bodyParser.run(source) }).recoverWith { /* ... */ } } validatedResult <- { /* Clean and validate the action's result */ convertedResult <- { /* Netty HttpResponse */ modelConversion.convertResult(validatedResult, requestHeader, reques } } yield convertedResult } 84
  • 85. handleAction bodyParser Action apply Accumulator[ByteString, Result] body Netty Accumulator run actionResult Result Ok Result Netty HttpResponse 85
  • 86. src HttpResponse writeAndFlush override def channelRead(ctx: ChannelHandlerContext, msg: Object): Unit msg match { case req: HttpRequest => requestsInFlight.incrementAndGet() val future: Future[HttpResponse] = handle(ctx.channel(), req) /* handle */ /* */ import play.api.libs.iteratee.Execution.Implicits.trampoline lastResponseSent = lastResponseSent.flatMap { /* */ ctx.writeAndFlush(httpResponse) } } } 86
  • 87. 87
  • 88. 88