Merge pull request #361 from jboner/wip-1878-hocon-classloader-√

#1878 - whippin' up some badass ClassLoader magicks
This commit is contained in:
viktorklang 2012-03-01 08:39:50 -08:00
commit 5c3dcb7fdf
5 changed files with 91 additions and 40 deletions

View file

@ -11,7 +11,7 @@ import akka.util.Timeout
import akka.util.duration._
@deprecated("use ActorSystem instead", "2.0")
object GlobalActorSystem extends ActorSystemImpl("GlobalSystem", OldConfigurationLoader.defaultConfig) {
object GlobalActorSystem extends ActorSystemImpl("GlobalSystem", OldConfigurationLoader.defaultConfig, OldConfigurationLoader.oldClassLoader) {
start()
/**
@ -26,11 +26,12 @@ object GlobalActorSystem extends ActorSystemImpl("GlobalSystem", OldConfiguratio
*/
@deprecated("use default config location or write your own configuration loader", "2.0")
object OldConfigurationLoader {
val oldClassLoader: ClassLoader = ActorSystem.findClassLoader()
val defaultConfig: Config = {
val cfg = fromProperties orElse fromClasspath orElse fromHome getOrElse emptyConfig
val config = cfg.withFallback(ConfigFactory.defaultReference)
config.checkValid(ConfigFactory.defaultReference, "akka")
val config = cfg.withFallback(ConfigFactory.defaultReference(oldClassLoader))
config.checkValid(ConfigFactory.defaultReference(oldClassLoader), "akka")
config
}

View file

@ -9,9 +9,10 @@ import com.typesafe.config.ConfigFactory
import scala.collection.JavaConverters._
import akka.util.duration._
import akka.util.Duration
import akka.actor.ActorSystem
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference) {
class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference(ActorSystem.findClassLoader())) {
"The default configuration file (i.e. reference.conf)" must {
"contain all configuration properties for akka-actor that are used in code with their correct defaults" in {

View file

@ -10,7 +10,6 @@ import akka.routing._
import akka.AkkaException
import akka.util.{ Switch, Helpers }
import akka.event._
import com.typesafe.config.ConfigFactory
/**
* Interface for all ActorRef providers to implement.

View file

@ -37,27 +37,76 @@ object ActorSystem {
val GlobalHome = SystemHome orElse EnvHome
def create(name: String, config: Config): ActorSystem = apply(name, config)
def apply(name: String, config: Config): ActorSystem = new ActorSystemImpl(name, config).start()
/**
* Creates a new ActorSystem with the name "default",
* obtains the current ClassLoader by first inspecting the current threads' getContextClassLoader,
* then tries to walk the stack to find the callers class loader, then falls back to the ClassLoader
* associated with the ActorSystem class.
* Then it loads the default reference configuration using the ClassLoader.
*/
def create(): ActorSystem = apply()
/**
* Uses the standard default Config from ConfigFactory.load(), since none is provided.
* Creates a new ActorSystem with the specified name,
* obtains the current ClassLoader by first inspecting the current threads' getContextClassLoader,
* then tries to walk the stack to find the callers class loader, then falls back to the ClassLoader
* associated with the ActorSystem class.
* Then it loads the default reference configuration using the ClassLoader.
*/
def create(name: String): ActorSystem = apply(name)
/**
* Uses the standard default Config from ConfigFactory.load(), since none is provided.
* Creates a new ActorSystem with the name "default", and the specified Config, then
* obtains the current ClassLoader by first inspecting the current threads' getContextClassLoader,
* then tries to walk the stack to find the callers class loader, then falls back to the ClassLoader
* associated with the ActorSystem class.
*/
def apply(name: String): ActorSystem = apply(name, ConfigFactory.load())
def create(name: String, config: Config): ActorSystem = apply(name, config)
def create(): ActorSystem = apply()
/**
* Creates a new ActorSystem with the name "default", the specified Config, and specified ClassLoader
*/
def create(name: String, config: Config, classLoader: ClassLoader): ActorSystem = apply(name, config, classLoader)
/**
* Creates a new ActorSystem with the name "default",
* obtains the current ClassLoader by first inspecting the current threads' getContextClassLoader,
* then tries to walk the stack to find the callers class loader, then falls back to the ClassLoader
* associated with the ActorSystem class.
* Then it loads the default reference configuration using the ClassLoader.
*/
def apply(): ActorSystem = apply("default")
class Settings(cfg: Config, final val name: String) {
/**
* Creates a new ActorSystem with the specified name,
* obtains the current ClassLoader by first inspecting the current threads' getContextClassLoader,
* then tries to walk the stack to find the callers class loader, then falls back to the ClassLoader
* associated with the ActorSystem class.
* Then it loads the default reference configuration using the ClassLoader.
*/
def apply(name: String): ActorSystem = {
val classLoader = findClassLoader()
apply(name, ConfigFactory.load(classLoader), classLoader)
}
/**
* Creates a new ActorSystem with the name "default", and the specified Config, then
* obtains the current ClassLoader by first inspecting the current threads' getContextClassLoader,
* then tries to walk the stack to find the callers class loader, then falls back to the ClassLoader
* associated with the ActorSystem class.
*/
def apply(name: String, config: Config): ActorSystem = apply(name, config, findClassLoader())
/**
* Creates a new ActorSystem with the name "default", the specified Config, and specified ClassLoader
*/
def apply(name: String, config: Config, classLoader: ClassLoader): ActorSystem = new ActorSystemImpl(name, config, classLoader).start()
class Settings(classLoader: ClassLoader, cfg: Config, final val name: String) {
final val config: Config = {
val config = cfg.withFallback(ConfigFactory.defaultReference)
config.checkValid(ConfigFactory.defaultReference, "akka")
val config = cfg.withFallback(ConfigFactory.defaultReference(classLoader))
config.checkValid(ConfigFactory.defaultReference(classLoader), "akka")
config
}
@ -98,6 +147,27 @@ object ActorSystem {
override def toString: String = config.root.render
}
/**
* INTERNAL
*/
private[akka] 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
}
}
/**
@ -344,14 +414,14 @@ abstract class ExtendedActorSystem extends ActorSystem {
def dynamicAccess: DynamicAccess
}
class ActorSystemImpl protected[akka] (val name: String, applicationConfig: Config) extends ExtendedActorSystem {
class ActorSystemImpl protected[akka] (val name: String, applicationConfig: Config, classLoader: ClassLoader) extends ExtendedActorSystem {
if (!name.matches("""^\w+$"""))
throw new IllegalArgumentException("invalid ActorSystem name [" + name + "], must contain only word characters (i.e. [a-zA-Z_0-9])")
import ActorSystem._
final val settings: Settings = new Settings(applicationConfig, name)
final val settings: Settings = new Settings(classLoader, applicationConfig, name)
protected def uncaughtExceptionHandler: Thread.UncaughtExceptionHandler =
new Thread.UncaughtExceptionHandler() {
@ -366,33 +436,13 @@ class ActorSystemImpl protected[akka] (val name: String, applicationConfig: Conf
}
final val threadFactory: MonitorableThreadFactory =
MonitorableThreadFactory(name, settings.Daemonicity, Option(Thread.currentThread.getContextClassLoader), uncaughtExceptionHandler)
MonitorableThreadFactory(name, settings.Daemonicity, Option(classLoader), uncaughtExceptionHandler)
/**
* This is an extension point: by overriding this method, subclasses can
* control all reflection activities of an actor system.
*/
protected def createDynamicAccess(): DynamicAccess = new ReflectiveDynamicAccess(findClassLoader)
protected def findClassLoader: ClassLoader = {
def findCaller(get: Int Class[_]): ClassLoader = {
val frames = Iterator.from(2).map(get)
frames 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
}
protected def createDynamicAccess(): DynamicAccess = new ReflectiveDynamicAccess(classLoader)
private val _pm: DynamicAccess = createDynamicAccess()
def dynamicAccess: DynamicAccess = _pm

View file

@ -5,12 +5,12 @@
package akka.remote
import akka.testkit._
import akka.actor.ActorSystemImpl
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import com.typesafe.config.ConfigResolveOptions
import java.io.File
import akka.actor.{ActorSystem, ActorSystemImpl}
object AkkaRemoteSpec {
private def configParseOptions = ConfigParseOptions.defaults.setAllowMissing(false)
@ -21,7 +21,7 @@ object AkkaRemoteSpec {
case location
ConfigFactory.systemProperties
.withFallback(ConfigFactory.parseFileAnySyntax(new File(location), configParseOptions))
.withFallback(ConfigFactory.defaultReference).resolve(ConfigResolveOptions.defaults)
.withFallback(ConfigFactory.defaultReference(ActorSystem.findClassLoader())).resolve(ConfigResolveOptions.defaults)
}
}