Skip to content

Instantly share code, notes, and snippets.

@kevinwright
Created May 28, 2012 18:38
Show Gist options
  • Save kevinwright/2820571 to your computer and use it in GitHub Desktop.
Save kevinwright/2820571 to your computer and use it in GitHub Desktop.
Building a better ActorSystem (for spray)
package com.zeebox.starwatch
import akka.actor.ActorSystem
import akka.util.Reflect
import java.net.InetAddress
import com.typesafe.config.ConfigFactory
import org.slf4j.LoggerFactory
object ActorSystemFactory {
val log = LoggerFactory.getLogger(this.getClass)
def mkActorSystem(): ActorSystem = {
val configLoader = findClassLoader()
val config = loadConfig(configLoader)
val systemName = config.getString("zeebox.actor-system-name")
ActorSystem(systemName, config, configLoader)
}
private[this] def loadConfig(loader: ClassLoader) = {
//start by seeding some system props
System.setProperty("env.hostname", InetAddress.getLocalHost.getHostName)
val default = ConfigFactory.load(loader)
val test = ConfigFactory.load(loader, "application-test")
val config = test withFallback default
config
}
private[this] def findClassLoader(): ClassLoader = {
def findCaller(get: Int ⇒ Class[_]): ClassLoader =
Iterator.from(2 /*is the magic number, promise*/ ).map(get) dropWhile { c ⇒
c != null &&
(c.getName.startsWith("akka.actor.ActorSystem") ||
c.getName.startsWith("scala.Option") ||
c.getName.startsWith("scala.collection.Iterator") ||
c.getName.startsWith("akka.util.Reflect"))
} next () match {
case null ⇒ getClass.getClassLoader
case c ⇒ c.getClassLoader
}
Option(Thread.currentThread.getContextClassLoader) orElse
(Reflect.getCallerClass map findCaller) getOrElse
getClass.getClassLoader
}
}
package com.zeebox.starwatch
import javax.servlet.{ServletContextEvent, ServletContextListener}
import java.net.InetAddress
import akka.actor.ActorSystem
import cc.spray.SprayServletSettings
import akka.config.ConfigurationException
import akka.util.Switch
class Initializer extends ServletContextListener {
private val booted = new Switch(false)
private[this] var actorSystem: Option[ActorSystem] = None
def contextInitialized(e: ServletContextEvent) {
booted switchOn {
println("Starting spray application ...")
val ctx = e.getServletContext
val loader = getClass.getClassLoader
Thread.currentThread.setContextClassLoader(loader)
val system = ActorSystemFactory.mkActorSystem()
actorSystem = Some(system)
ctx.setAttribute(Initializer.SystemAttrName, system)
SprayServletSettings.BootClasses match {
case Nil =>
val e = new ConfigurationException("No boot classes configured. Please specify at least one boot class " +
"in the spray.servlet.boot-classes config setting.")
ctx.log(e.getMessage, e)
case classes => {
for (className <- classes) {
try {
loader
.loadClass(className)
.getConstructor(classOf[ActorSystem])
.newInstance(system)
} catch {
case e: ClassNotFoundException =>
ctx.log("Configured boot class " + className + " cannot be found", e)
case e: NoSuchMethodException =>
ctx.log("Configured boot class " + className + " does not define required constructor " +
"with one parameter of type `akka.actor.ActorSystem`", e)
case e: Exception =>
ctx.log("Could not create instance of boot class " + className, e)
}
}
}
}
}
}
def contextDestroyed(e: ServletContextEvent) {
booted switchOff {
println("Shutting down spray application ...")
actorSystem foreach {_.shutdown()}
actorSystem = None
}
}
}
object Initializer {
private[starwatch] val SystemAttrName = "spray.servlet.system"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment