Skip to content

Loading…

Fixes potential hang in startup (actor system was not being shut down in the event of startup failure). #87

Merged
merged 3 commits into from

2 participants

Commits on Jan 20, 2013
  1. @nuttycom
Commits on Jan 30, 2013
  1. @nuttycom
  2. @nuttycom
View
2 akka_testing/src/main/scala/blueeyes/akka_testing/FutureMatchers.scala
@@ -32,6 +32,8 @@ trait FutureMatchers extends AkkaConversions {
implicit val defaultFutureTimeouts: FutureTimeouts = FutureTimeouts(10, 100L millis)
+ def awaited[A](duration: Duration)(matcher: Matcher[A]) = matcher ^^ { (future: Future[A]) => Await.result(future, duration) }
+
case class whenDelivered[A](matcher: Matcher[A])(implicit timeouts: FutureTimeouts) extends Matcher[Future[A]] with Expectations {
def apply[B <: Future[A]](expectable: Expectable[B]): MatchResult[B] = {
val (ok, okMessage, koMessage) = retry(expectable.evaluate.value, timeouts.retries, timeouts.retries)
View
15 core/src/main/scala/blueeyes/core/service/HttpServer.scala
@@ -1,12 +1,12 @@
package blueeyes.core.service
import blueeyes.bkka.Stoppable
+import akka.actor.ActorSystem
import akka.dispatch.Future
import akka.dispatch.Promise
import akka.dispatch.ExecutionContext
import akka.util.Timeout
-import blueeyes.bkka.AkkaDefaults
import blueeyes.core.data.ByteChunk
import blueeyes.core.http._
import blueeyes.core.service._
@@ -161,16 +161,15 @@ trait HttpServerMain extends HttpServerModule {
* It can be useful to replace the default configuration from the {@link HttpServerMain#main} method.
*/
def run(rootConfiguration: Configuration) = {
-
- // TODO: This should be made configurable (and drop AkkaDefaults)
- val executionContext: ExecutionContext = ExecutionContext.defaultExecutionContext(AkkaDefaults.actorSystem)
+ val actorSystem: ActorSystem = ActorSystem(rootConfiguration[String]("blueeyes.actor_system", "blueeyes-actors"))
+ val executionContext: ExecutionContext = ExecutionContext.defaultExecutionContext(actorSystem)
/** Retrieves the logger for the server, which is configured directly from
* the server's "log" configuration block.
*/
val log: Logger = Logger("blueeyes.server")
server(rootConfiguration, executionContext).start map {
- _.onSuccess { case (runningState, stoppable) =>
+ _ onSuccess { case (runningState, stoppable) =>
log.info("Services started.")
Runtime.getRuntime.addShutdownHook { new Thread {
override def start() {
@@ -184,8 +183,14 @@ trait HttpServerMain extends HttpServerModule {
}
doneSignal.await()
+ actorSystem.shutdown()
}
}}
+ } onFailure {
+ case ex =>
+ System.err.println("Startup of BlueEyes failed due to an unhandled exception.")
+ ex.printStackTrace(System.err)
+ actorSystem.shutdown()
}
}
}
View
51 core/src/test/scala/blueeyes/core/service/HttpServerSpec.scala
@@ -17,6 +17,8 @@ import blueeyes.akka_testing.FutureMatchers
import org.specs2.mutable.Specification
import org.specs2.specification.{Outside, Scope}
+import com.weiglewilczek.slf4s._
+
object TestServer extends HttpServerModule with TestAkkaDefaults {
import DefaultBijections._
@@ -143,3 +145,52 @@ class HttpServerSpec extends Specification with FutureMatchers {
}
}
}
+
+
+object FailServer extends HttpServerModule {
+ import DefaultBijections._
+
+ class HttpServer(rootConfig: Configuration, val executionContext: ExecutionContext)
+ extends HttpServerLike(rootConfig)
+ with BlueEyesServiceBuilder
+ with HttpRequestCombinators
+ with ReflectiveServiceList[ByteChunk] { enclosing =>
+
+ implicit val ctx = executionContext
+ val log = logger
+
+ lazy val testService = service("test", "1.0.7") {
+ context => {
+ startup {
+ sys.error("Error during startup should not hang.")
+ Future("Hello, world, I'm Unreachable!")
+ } ->
+ request { state: String =>
+ path("/dead") {
+ get { request: HttpRequest[ByteChunk] =>
+ akka.dispatch.Promise.failed[HttpResponse[ByteChunk]](new RuntimeException()): Future[HttpResponse[ByteChunk]]
+ }
+ }
+ } ->
+ shutdown { state: String =>
+ Future(())
+ }
+ }
+ }
+ }
+
+ def server(config: Configuration, executor: ExecutionContext): HttpServer = new HttpServer(config, executor)
+}
+
+class FailServerSpec extends Specification with FutureMatchers with TestAkkaDefaults {
+ import TestServer._
+ import DefaultBijections._
+ import akka.util.Duration._
+
+ "FailServer.start" should {
+ "not hang if an exception is thrown in the startup function" in {
+ val config = Configuration.parse("", BlockFormat)
+ Await.result(FailServer.server(config, defaultFutureDispatch).start.get, 10 seconds) must throwA[RuntimeException]
+ }
+ }
+}
Something went wrong with that request. Please try again.