|
Play Framework/Scala example source code file (ServerStartSpec.scala)
The ServerStartSpec.scala Play Framework example source code
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package play.core.server
import com.google.common.io.Files
import java.io.File
import java.nio.charset.Charset
import java.util.Properties
import org.specs2.mutable.Specification
import play.api.{ Mode, Play }
import play.core.ApplicationProvider
object ServerStartSpec extends Specification {
sequential
def withTempDir[T](block: File => T) = {
val temp = Files.createTempDir()
try {
block(temp)
} finally {
def rm(file: File): Unit = file match {
case dir if dir.isDirectory =>
dir.listFiles().foreach(rm)
dir.delete()
case f => f.delete()
}
rm(temp)
}
}
case class ExitException(message: String, cause: Option[Throwable] = None, returnCode: Int = -1) extends Exception(s"Exit with $message, $cause, $returnCode", cause.orNull)
def startResult[A](f: => A): Either[String,A] = try Right(f) catch {
case ServerStartException(message, _) => Left(message)
}
def exitResult[A](f: => A): Either[String,A] = try Right(f) catch {
case ExitException(message, _, _) => Left(message)
}
class FakeServerStart(val defaultServerProvider: ServerProvider) extends ServerStart {
override protected def createApplicationProvider(config: ServerConfig): ApplicationProvider = {
new FakeApplicationProvider(config.rootDir)
}
}
/** A mocked ServerProcess */
class FakeServerProcess(
val args: Seq[String] = Seq(),
propertyMap: Map[String,String] = Map(),
val pid: Option[String] = None
) extends ServerProcess {
val classLoader: ClassLoader = getClass.getClassLoader
val properties = new Properties()
for ((k, v) <- propertyMap) { properties.put(k, v) }
private var hooks = Seq.empty[() => Unit]
def addShutdownHook(hook: => Unit) = {
hooks = hooks :+ (() => hook)
}
def shutdown(): Unit = {
for (h <- hooks) h.apply()
}
def exit(message: String, cause: Option[Throwable] = None, returnCode: Int = -1): Nothing = {
throw new ExitException(message, cause, returnCode)
}
}
class FakeApplicationProvider(appPath: File) extends ApplicationProvider {
val path = appPath
def get = ??? // Never called, because we're not serving requests
}
// A family of fake servers for us to test
class FakeServer(config: ServerConfig, appProvider: ApplicationProvider) extends Server with ServerWithStop {
def applicationProvider = appProvider
def mode = config.mode
def mainAddress = ???
@volatile var stopCallCount = 0
override def stop() = {
stopCallCount += 1
Play.stop()
super.stop()
}
}
class FakeServerProvider extends ServerProvider {
override def createServer(config: ServerConfig, appProvider: ApplicationProvider) = new FakeServer(config, appProvider)
}
class FakeServer2(config: ServerConfig, appProvider: ApplicationProvider) extends FakeServer(config, appProvider)
class FakeServerProvider2 extends ServerProvider {
override def createServer(config: ServerConfig, appProvider: ApplicationProvider) = new FakeServer2(config, appProvider)
}
class InvalidCtorFakeServerProvider(foo: String) extends FakeServerProvider {
override def createServer(config: ServerConfig, appProvider: ApplicationProvider) = ???
}
class PrivateCtorFakeServer private () extends FakeServerProvider {
override def createServer(config: ServerConfig, appProvider: ApplicationProvider) = ???
}
"serverStart.readServerConfigSettings" should {
"read settings from the current process (root dir in args, default HTTP port)" in withTempDir { tempDir =>
val serverStart = new FakeServerStart(new FakeServerProvider)
val process = new FakeServerProcess(Seq(tempDir.getAbsolutePath))
startResult(serverStart.readServerConfigSettings(process)) must_== Right(ServerConfig(
rootDir = tempDir,
port = Some(9000),
sslPort = None,
mode = Mode.Prod,
properties = process.properties
))
}
"read settings from the current process (root dir, HTTP port and HTTPS port in props)" in withTempDir { tempDir =>
val serverStart = new FakeServerStart(new FakeServerProvider)
val process = new FakeServerProcess(
propertyMap = Map(
"user.dir" -> tempDir.getAbsolutePath,
"http.port" -> "80",
"https.port" -> "443"
)
)
startResult(serverStart.readServerConfigSettings(process)) must_== Right(ServerConfig(
rootDir = tempDir,
port = Some(80),
sslPort = Some(443),
mode = Mode.Prod,
properties = process.properties
))
}
"require a root dir path when reading settings" in {
val serverStart = new FakeServerStart(new FakeServerProvider)
val process = new FakeServerProcess()
startResult(serverStart.readServerConfigSettings(process)) must_== Left("No root server path supplied")
}
"require an HTTP or HTTPS port when reading settings" in withTempDir { tempDir =>
val serverStart = new FakeServerStart(new FakeServerProvider)
val process = new FakeServerProcess(
args = Seq(tempDir.getAbsolutePath),
propertyMap = Map("http.port" -> "disabled")
)
startResult(serverStart.readServerConfigSettings(process)) must_== Left("Must provide either an HTTP or HTTPS port")
}
"require an integer HTTP port when reading settings" in withTempDir { tempDir =>
val serverStart = new FakeServerStart(new FakeServerProvider)
val process = new FakeServerProcess(
args = Seq(tempDir.getAbsolutePath),
propertyMap = Map("http.port" -> "xyz")
)
startResult(serverStart.readServerConfigSettings(process)) must_== Left("Invalid HTTP port: xyz")
}
"require an integer HTTPS port when reading settings" in withTempDir { tempDir =>
val serverStart = new FakeServerStart(new FakeServerProvider)
val process = new FakeServerProcess(
args = Seq(tempDir.getAbsolutePath),
propertyMap = Map("https.port" -> "xyz")
)
startResult(serverStart.readServerConfigSettings(process)) must_== Left("Invalid HTTPS port: xyz")
}
}
"serverStart.readServerProviderSetting" should {
"return default by default" in {
val defaultServerProvider = new FakeServerProvider
val serverStart = new FakeServerStart(defaultServerProvider)
val process = new FakeServerProcess()
serverStart.readServerProviderSetting(process) must be(defaultServerProvider)
}
"create a custom provider when the server.provider property is supplied" in {
val serverStart = new FakeServerStart(new FakeServerProvider)
val serverProviderClass = classOf[FakeServerProvider]
val process = new FakeServerProcess(
propertyMap = Map("server.provider" -> serverProviderClass.getName)
)
serverStart.readServerProviderSetting(process).getClass must_== serverProviderClass
}
"fail if the class doesn't exist" in {
val serverStart = new FakeServerStart(new FakeServerProvider)
val process = new FakeServerProcess(
propertyMap = Map("server.provider" -> "garble.barble.Phnarble")
)
startResult(serverStart.readServerProviderSetting(process)) must_== Left("Couldn't find ServerProvider class 'garble.barble.Phnarble'")
}
"fail if the class doesn't implement ServerProvider" in {
val serverStart = new FakeServerStart(new FakeServerProvider)
val serverProvider = classOf[String].getName
val process = new FakeServerProcess(
propertyMap = Map("server.provider" -> serverProvider)
)
startResult(serverStart.readServerProviderSetting(process)) must_== Left(s"Class $serverProvider must implement ServerProvider interface")
}
"fail if the class doesn't have a default constructor" in {
val serverStart = new FakeServerStart(new FakeServerProvider)
val serverProvider = classOf[InvalidCtorFakeServerProvider].getName
val process = new FakeServerProcess(
propertyMap = Map("server.provider" -> serverProvider)
)
startResult(serverStart.readServerProviderSetting(process)) must_== Left(s"ServerProvider class $serverProvider must have a public default constructor")
}
"fail if the class has a private constructor" in {
val serverStart = new FakeServerStart(new FakeServerProvider)
val serverProvider = classOf[PrivateCtorFakeServer].getName
val process = new FakeServerProcess(
propertyMap = Map("server.provider" -> serverProvider)
)
startResult(serverStart.readServerProviderSetting(process)) must_== Left(s"ServerProvider class $serverProvider must have a public default constructor")
}
}
"serverStart.createPidFile" should {
"create a pid file with the current id, then remove it on process shutdown" in withTempDir { tempDir =>
val serverStart = new FakeServerStart(new FakeServerProvider)
val pid = "12345"
val process = new FakeServerProcess(pid = Some(pid))
startResult(serverStart.createPidFile(process, tempDir)) must_== Right(())
val pidFile = new File(tempDir, "RUNNING_PID")
try {
pidFile.exists must beTrue
Files.toString(pidFile, Charset.forName("US-ASCII")) must_== pid
} finally {
process.shutdown()
}
pidFile.exists must beFalse
}
"fail to create a pid file if it can't get the process pid" in withTempDir { tempDir =>
val serverStart = new FakeServerStart(new FakeServerProvider)
val process = new FakeServerProcess(pid = None)
startResult(serverStart.createPidFile(process, tempDir)) must_== Left("Couldn't determine current process's pid")
}
"fail to create a pid file if the pid file already exists" in withTempDir { tempDir =>
val serverStart = new FakeServerStart(new FakeServerProvider)
val process = new FakeServerProcess(pid = Some("123"))
Files.write("x".getBytes, new File(tempDir, "RUNNING_PID"))
startResult(serverStart.createPidFile(process, tempDir)) must_== Left(s"This application is already running (Or delete ${tempDir.getAbsolutePath}/RUNNING_PID file).")
}
}
"serverStart.start" should {
"read settings, create a pid file, start the the server and register shutdown hooks" in withTempDir { tempDir =>
val serverStart = new FakeServerStart(new FakeServerProvider)
val process = new FakeServerProcess(
args = Seq(tempDir.getAbsolutePath),
pid = Some("999")
)
val server = serverStart.start(process)
val pidFile = new File(tempDir, "RUNNING_PID")
try {
server.getClass must_== classOf[FakeServer]
pidFile.exists must beTrue
server.asInstanceOf[FakeServer].stopCallCount must_== 0
} finally {
process.shutdown()
}
pidFile.exists must beFalse
server.asInstanceOf[FakeServer].stopCallCount must_== 1
}
"read settings, create custom ServerProvider, create a pid file, start the the server and register shutdown hooks" in withTempDir { tempDir =>
val serverStart = new FakeServerStart(new FakeServerProvider2)
val process = new FakeServerProcess(
args = Seq(tempDir.getAbsolutePath),
propertyMap = Map("server.provider" -> classOf[FakeServerProvider2].getName),
pid = Some("999")
)
val server = serverStart.start(process)
val pidFile = new File(tempDir, "RUNNING_PID")
try {
server.getClass must_== classOf[FakeServer2]
pidFile.exists must beTrue
server.asInstanceOf[FakeServer2].stopCallCount must_== 0
} finally {
process.shutdown()
}
pidFile.exists must beFalse
server.asInstanceOf[FakeServer2].stopCallCount must_== 1
}
"exit with an error if settings are wrong" in withTempDir { tempDir =>
val serverStart = new FakeServerStart(new FakeServerProvider)
val process = new FakeServerProcess()
exitResult {
serverStart.start(process)
} must_== (Left("No root server path supplied"))
}
}
}
Other Play Framework source code examplesHere is a short list of links related to this Play Framework ServerStartSpec.scala source code file: |
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.