Updated microkernel
- no config files used by microkernel - boot classes are specified as main arguments - actor system creation is left to user in Bootable - added on-out-of-memory handler to java args - updated docs
This commit is contained in:
parent
0772d018fb
commit
ad8a050d05
11 changed files with 151 additions and 116 deletions
|
|
@ -5,4 +5,32 @@
|
|||
Microkernel
|
||||
#############
|
||||
|
||||
The Akka Spring module has not been migrated to Akka 2.0-SNAPSHOT yet.
|
||||
The Akka Microkernel is included in the Akka download found at `downloads`_.
|
||||
|
||||
.. _downloads: http://akka.io/downloads
|
||||
|
||||
To run an application with the microkernel you need to create a Bootable class
|
||||
that handles the startup and shutdown the application. An example is included below.
|
||||
|
||||
Put your application jar in the ``deploy`` directory to have it automatically
|
||||
loaded.
|
||||
|
||||
To start the kernel use the scripts in the ``bin`` directory, passing the boot
|
||||
classes for your application.
|
||||
|
||||
There is a simple example of an application setup for running with the
|
||||
microkernel included in the akka download. This can be run with the following
|
||||
command (on a unix-based system):
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
bin/akka sample.kernel.hello.HelloKernel
|
||||
|
||||
Use Ctrl-C to interrupt and exit the microkernel.
|
||||
|
||||
On a Windows machine you can also use the bin/akka.bat script.
|
||||
|
||||
The code for the Hello Kernel example (see the HelloKernel class for an example
|
||||
of creating a Bootable):
|
||||
|
||||
.. includecode:: ../../akka-samples/akka-sample-hello-kernel/src/main/scala/sample/kernel/hello/HelloKernel.scala
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
#####################################
|
||||
# Akka Kernel Reference Config File #
|
||||
#####################################
|
||||
|
||||
# This reference config file has all the default settings
|
||||
# Make your edits/overrides in your akka.conf
|
||||
|
||||
|
||||
akka {
|
||||
|
||||
kernel {
|
||||
# The name of the actor system created by the Akka Microkernel
|
||||
system.name = "default"
|
||||
|
||||
# Boot classes are loaded and created automatically when the Akka Microkernel starts up
|
||||
# A list of FQNs (Fully Qualified Names) of classes that implement akka.kernel.Bootable
|
||||
boot = []
|
||||
}
|
||||
}
|
||||
|
|
@ -5,92 +5,134 @@
|
|||
package akka.kernel
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import java.io.File
|
||||
import java.lang.Boolean.getBoolean
|
||||
import java.net.{ URL, URLClassLoader }
|
||||
import java.util.jar.JarFile
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
/**
|
||||
* To use the microkernel at least one 'boot class' needs to be specified.
|
||||
* A boot class implements this interface ([[akka.kernel.Bootable]]) and
|
||||
* must have an empty default constructor.
|
||||
*
|
||||
* ActorSystems can be created within the boot class.
|
||||
*
|
||||
* An example of a simple boot class:
|
||||
* {{{
|
||||
* class BootApp extends Bootable {
|
||||
* val system = ActorSystem("app")
|
||||
*
|
||||
* def startup = {
|
||||
* system.actorOf(Props[FirstActor]) ! FirstMessage
|
||||
* }
|
||||
*
|
||||
* def shutdown = {
|
||||
* system.shutdown()
|
||||
* }
|
||||
* }
|
||||
* }}}
|
||||
*
|
||||
* Boot classes are specified as main arguments to the microkernel.
|
||||
*
|
||||
* For example, using the akka script an application can be started with
|
||||
* the following at the command line:
|
||||
* {{{
|
||||
* bin/akka org.app.BootApp
|
||||
* }}}
|
||||
*/
|
||||
trait Bootable {
|
||||
def startup(system: ActorSystem): Unit
|
||||
def shutdown(system: ActorSystem): Unit
|
||||
/**
|
||||
* Callback run on microkernel startup.
|
||||
* Create initial actors and messages here.
|
||||
*/
|
||||
def startup(): Unit
|
||||
|
||||
/**
|
||||
* Callback run on microkernel shutdown.
|
||||
* Shutdown actor systems here.
|
||||
*/
|
||||
def shutdown(): Unit
|
||||
}
|
||||
|
||||
/**
|
||||
* Main class for running the microkernel.
|
||||
*/
|
||||
object Main {
|
||||
val quiet = getBoolean("akka.kernel.quiet")
|
||||
|
||||
def log(s: String) = if (!quiet) println(s)
|
||||
|
||||
def main(args: Array[String]) = {
|
||||
if (args.isEmpty) {
|
||||
log("[error] No boot classes specified")
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
log(banner)
|
||||
log("Starting Akka...")
|
||||
log("Running Akka " + ActorSystem.Version)
|
||||
|
||||
val config = ConfigFactory.load("akka.conf")
|
||||
val name = config.getString("akka.kernel.system.name")
|
||||
val system = ActorSystem(name, config)
|
||||
val classLoader = deployJars(system)
|
||||
|
||||
log("Created actor system '%s'" format name)
|
||||
val classLoader = createClassLoader()
|
||||
|
||||
Thread.currentThread.setContextClassLoader(classLoader)
|
||||
|
||||
val bootClasses: Seq[String] = system.settings.config.getStringList("akka.kernel.boot").asScala
|
||||
val bootClasses: Seq[String] = args.toSeq
|
||||
val bootables: Seq[Bootable] = bootClasses map { c ⇒ classLoader.loadClass(c).newInstance.asInstanceOf[Bootable] }
|
||||
|
||||
for (bootable ← bootables) {
|
||||
log("Starting up " + bootable.getClass.getName)
|
||||
bootable.startup(system)
|
||||
bootable.startup()
|
||||
}
|
||||
|
||||
addShutdownHook(system, bootables)
|
||||
addShutdownHook(bootables)
|
||||
|
||||
log("Successfully started Akka")
|
||||
}
|
||||
|
||||
def deployJars(system: ActorSystem): ClassLoader = {
|
||||
if (system.settings.Home.isEmpty) {
|
||||
log("Akka home is not defined")
|
||||
System.exit(1)
|
||||
Thread.currentThread.getContextClassLoader
|
||||
} else {
|
||||
val home = system.settings.Home.get
|
||||
def createClassLoader(): ClassLoader = {
|
||||
if (ActorSystem.GlobalHome.isDefined) {
|
||||
val home = ActorSystem.GlobalHome.get
|
||||
val deploy = new File(home, "deploy")
|
||||
|
||||
if (!deploy.exists) {
|
||||
log("No deploy dir found at " + deploy)
|
||||
log("Please check that akka home is defined correctly")
|
||||
System.exit(1)
|
||||
if (deploy.exists) {
|
||||
loadDeployJars(deploy)
|
||||
} else {
|
||||
log("[warning] No deploy dir found at " + deploy)
|
||||
Thread.currentThread.getContextClassLoader
|
||||
}
|
||||
|
||||
val jars = deploy.listFiles.filter(_.getName.endsWith(".jar"))
|
||||
|
||||
val nestedJars = jars flatMap { jar ⇒
|
||||
val jarFile = new JarFile(jar)
|
||||
val jarEntries = jarFile.entries.asScala.toArray.filter(_.getName.endsWith(".jar"))
|
||||
jarEntries map { entry ⇒ new File("jar:file:%s!/%s" format (jarFile.getName, entry.getName)) }
|
||||
}
|
||||
|
||||
val urls = (jars ++ nestedJars) map { _.toURI.toURL }
|
||||
|
||||
urls foreach { url ⇒ log("Deploying " + url) }
|
||||
|
||||
new URLClassLoader(urls, Thread.currentThread.getContextClassLoader)
|
||||
} else {
|
||||
log("[warning] Akka home is not defined")
|
||||
Thread.currentThread.getContextClassLoader
|
||||
}
|
||||
}
|
||||
|
||||
def addShutdownHook(system: ActorSystem, bootables: Seq[Bootable]): Unit = {
|
||||
def loadDeployJars(deploy: File): ClassLoader = {
|
||||
val jars = deploy.listFiles.filter(_.getName.endsWith(".jar"))
|
||||
|
||||
val nestedJars = jars flatMap { jar ⇒
|
||||
val jarFile = new JarFile(jar)
|
||||
val jarEntries = jarFile.entries.asScala.toArray.filter(_.getName.endsWith(".jar"))
|
||||
jarEntries map { entry ⇒ new File("jar:file:%s!/%s" format (jarFile.getName, entry.getName)) }
|
||||
}
|
||||
|
||||
val urls = (jars ++ nestedJars) map { _.toURI.toURL }
|
||||
|
||||
urls foreach { url ⇒ log("Deploying " + url) }
|
||||
|
||||
new URLClassLoader(urls, Thread.currentThread.getContextClassLoader)
|
||||
}
|
||||
|
||||
def addShutdownHook(bootables: Seq[Bootable]): Unit = {
|
||||
Runtime.getRuntime.addShutdownHook(new Thread(new Runnable {
|
||||
def run = {
|
||||
log("")
|
||||
log("Received signal to stop")
|
||||
log("Shutting down Akka...")
|
||||
|
||||
for (bootable ← bootables) {
|
||||
log("Shutting down " + bootable.getClass.getName)
|
||||
bootable.shutdown(system)
|
||||
bootable.shutdown()
|
||||
}
|
||||
system.stop()
|
||||
|
||||
log("Successfully shut down Akka")
|
||||
}
|
||||
}))
|
||||
|
|
|
|||
24
akka-kernel/src/main/scripts/akka
Executable file
24
akka-kernel/src/main/scripts/akka
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
declare quiet="false"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-q | --quiet ) quiet="true"; shift ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ "$@" ]] || {
|
||||
echo "No boot classes specified"
|
||||
echo "Usage: bin/akka org.somewhere.BootClass"
|
||||
exit 1
|
||||
}
|
||||
|
||||
declare AKKA_HOME="$(cd "$(cd "$(dirname "$0")"; pwd -P)"/..; pwd)"
|
||||
|
||||
[ -n "$JAVA_OPTS" ] || JAVA_OPTS="-Xmx1024M -Xms1024M -Xss1M -XX:MaxPermSize=256M -XX:+UseParallelGC -XX:OnOutOfMemoryError=\"kill -9 %p\""
|
||||
|
||||
[ -n "$AKKA_CLASSPATH" ] || AKKA_CLASSPATH="$AKKA_HOME/lib/scala-library.jar:$AKKA_HOME/lib/akka/*:$AKKA_HOME/config"
|
||||
|
||||
java "$JAVA_OPTS" -cp "$AKKA_CLASSPATH" -Dakka.home="$AKKA_HOME" -Dakka.kernel.quiet=$quiet akka.kernel.Main "$@"
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
@echo off
|
||||
|
||||
set AKKA_HOME=%~dp0..
|
||||
set JAVA_OPTS=-Xms1024M -Xmx1024M -Xss1M -XX:MaxPermSize=256M -XX:+UseParallelGC
|
||||
set JAVA_OPTS=-Xmx1024M -Xms1024M -Xss1M -XX:MaxPermSize=256M -XX:+UseParallelGC
|
||||
set AKKA_CLASSPATH=%AKKA_HOME%\lib\scala-library.jar;%AKKA_HOME%\config;%AKKA_HOME%\lib\akka\*
|
||||
|
||||
java %JAVA_OPTS% -cp "%AKKA_CLASSPATH%" -Dakka.home="%AKKA_HOME%" akka.kernel.Main
|
||||
java %JAVA_OPTS% -cp "%AKKA_CLASSPATH%" -Dakka.home="%AKKA_HOME%" akka.kernel.Main %*
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.kernel
|
||||
|
||||
import akka.testkit.AkkaSpec
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference) {
|
||||
|
||||
"The default configuration file (i.e. reference.conf)" must {
|
||||
"contain correct defaults for akka-kernel" in {
|
||||
|
||||
val config = system.settings.config
|
||||
|
||||
config.getString("akka.kernel.system.name") must be === "default"
|
||||
config.getList("akka.kernel.boot").asScala.toList must be === Nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# Config for the Hello Kernel sample
|
||||
|
||||
akka {
|
||||
kernel {
|
||||
system.name = "hellokernel"
|
||||
boot = ["sample.kernel.hello.HelloKernel"]
|
||||
}
|
||||
}
|
||||
|
|
@ -25,9 +25,13 @@ class WorldActor extends Actor {
|
|||
}
|
||||
|
||||
class HelloKernel extends Bootable {
|
||||
def startup(system: ActorSystem) = {
|
||||
val system = ActorSystem("hellokernel")
|
||||
|
||||
def startup = {
|
||||
system.actorOf(Props[HelloActor]) ! Start
|
||||
}
|
||||
|
||||
def shutdown(system: ActorSystem) = {}
|
||||
def shutdown = {
|
||||
system.shutdown()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,10 +52,12 @@ object Dist {
|
|||
(baseDirectory, distSources, distUnzipped, version, distFile, streams) map {
|
||||
(projectBase, allSources, unzipped, version, zipFile, s) => {
|
||||
val base = unzipped / ("akka-" + version)
|
||||
val scripts = (projectBase / "scripts" / "microkernel" * "*").get
|
||||
val scripts = (projectBase / "akka-kernel" / "src" / "main" / "scripts" * "*").get
|
||||
val bin = base / "bin"
|
||||
val configSources = projectBase / "config"
|
||||
val config = base / "config"
|
||||
val deploy = base / "deploy"
|
||||
val deployReadme = deploy / "readme"
|
||||
val doc = base / "doc" / "akka"
|
||||
val api = doc / "api"
|
||||
val docs = doc / "docs"
|
||||
|
|
@ -68,6 +70,8 @@ object Dist {
|
|||
IO.delete(unzipped)
|
||||
copyFilesTo(scripts, bin, setExecutable = true)
|
||||
IO.copyDirectory(configSources, config)
|
||||
IO.createDirectory(deploy)
|
||||
IO.write(deployReadme, "Place application jars in this directory")
|
||||
IO.copyDirectory(allSources.api, api)
|
||||
IO.copyDirectory(allSources.docs, docs)
|
||||
copyFilesTo(allSources.docJars, docJars)
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
declare quiet="false"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-q | --quiet ) quiet="true"; shift ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
declare AKKA_HOME="$(cd "$(cd "$(dirname "$0")"; pwd -P)"/..; pwd)"
|
||||
|
||||
[ -n "$JAVA_OPTS" ] || JAVA_OPTS="-Xms1536M -Xmx1536M -Xss1M -XX:MaxPermSize=256M -XX:+UseParallelGC"
|
||||
|
||||
[ -n "$AKKA_CLASSPATH" ] || AKKA_CLASSPATH="$AKKA_HOME/lib/scala-library.jar:$AKKA_HOME/lib/akka/*:$AKKA_HOME/config"
|
||||
|
||||
java $JAVA_OPTS -cp "$AKKA_CLASSPATH" -Dakka.home="$AKKA_HOME" -Dakka.kernel.quiet=$quiet akka.kernel.Main
|
||||
Loading…
Add table
Add a link
Reference in a new issue