Adding a convenience BundleActivator implementation to bootstrap Akka from an OSGi bundle

This commit is contained in:
Gert Vanthienen 2012-05-29 12:41:09 +02:00
parent 12bd97b66d
commit 07dd654849
3 changed files with 169 additions and 0 deletions

View file

@ -0,0 +1,78 @@
package akka.osgi
import com.typesafe.config.{ Config, ConfigFactory }
import akka.actor.ActorSystem
import org.osgi.framework.{ BundleContext, BundleActivator }
import java.util.Properties
/**
* Abstract {@link BundleActivator} implementation to bootstrap and configure an {@link ActorSystem} in an
* OSGi environment.
*/
abstract class ActorSystemActivator extends BundleActivator {
var system: ActorSystem = null
/**
* Implement this method to add your own actors to the ActorSystem
*
* @param system the ActorSystem that was created by the activator
*/
def configure(system: ActorSystem)
/**
* Sets up a new ActorSystem and registers it in the OSGi Service Registry
*
* @param context the BundleContext
*/
def start(context: BundleContext) {
system = createActorSystem(context)
configure(system)
val properties = new Properties();
properties.put("name", getActorSystemName(context))
context.registerService(classOf[ActorSystem].getName, system, properties)
}
/**
* Shuts down the ActorSystem when the bundle is stopped.
*
* @param context the BundleContext
*/
def stop(context: BundleContext) {
if (system != null) {
system.shutdown()
system.shutdown()
system = null
}
}
/**
* Strategy method to create the ActorSystem.
*/
def createActorSystem(context: BundleContext) =
ActorSystem(getActorSystemName(context), getActorSystemConfig(context), getClass.getClassLoader)
/**
* Strategy method to create the Config for the ActorSystem, ensuring that the default/reference configuration is
* loaded from the akka-actor bundle.
*/
def getActorSystemConfig(context: BundleContext): Config = {
val reference = ConfigFactory.defaultReference(classOf[ActorSystem].getClassLoader)
ConfigFactory.load(getClass.getClassLoader).withFallback(reference)
}
/**
* Strategy method to determine the ActorSystem name - override this method to define the ActorSytem name yourself.
*
* The default implementation will use 'bundle-<id>-ActorSystem' where <id> matches the bundle id for the containing bundle.
*
* @param context the BundleContext
* @return the ActorSystem name
*/
def getActorSystemName(context: BundleContext): String = {
"bundle-%s-ActorSystem".format(context.getBundle().getBundleId)
}
}

View file

@ -0,0 +1,75 @@
package akka.osgi
import java.util.{ ServiceLoader, HashMap }
import de.kalpatec.pojosr.framework.launch.{ ClasspathScanner, PojoServiceRegistryFactory }
import org.scalatest.FlatSpec
import org.osgi.framework.BundleContext
import akka.actor.{ Actor, Props, ActorSystem }
import akka.pattern.ask
import akka.dispatch.Await
import akka.util.duration._
import akka.util.Timeout
/**
* Test cases for {@link ActorSystemActivator}
*/
class ActorSystemActivatorTest extends FlatSpec {
abstract class TestMessage
case object Ping extends TestMessage
case object Pong extends TestMessage
class PongActor extends Actor {
def receive = {
case Ping
sender ! Pong
}
}
lazy val context: BundleContext = {
val config = new HashMap[String, AnyRef]();
config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, new ClasspathScanner().scanForBundles());
val loader = ServiceLoader.load(classOf[PojoServiceRegistryFactory]);
val registry = loader.iterator().next().newPojoServiceRegistry(config);
registry.getBundleContext
}
val activator = new ActorSystemActivator {
def configure(system: ActorSystem) {
system.actorOf(Props(new PongActor), name = "pong")
}
}
"ActorSystemActivator" should "start and register the ActorSystem on start" in {
activator.start(context)
val reference = context.getServiceReference(classOf[ActorSystem].getName)
assert(reference != null)
val system = context.getService(reference).asInstanceOf[ActorSystem]
val actor = system.actorFor("/user/pong")
implicit val timeout = Timeout(5 seconds)
val future = actor ? Ping
val result = Await.result(future, timeout.duration)
assert(result != null)
}
it should "stop the ActorSystem on bundle stop" in {
val reference = context.getServiceReference(classOf[ActorSystem].getName)
assert(reference != null)
val system = context.getService(reference).asInstanceOf[ActorSystem]
assert(!system.isTerminated)
activator.stop(context)
system.awaitTermination()
assert(system.isTerminated)
}
}

View file

@ -209,6 +209,15 @@ object AkkaBuild extends Build {
)
)
lazy val osgi = Project(
id = "akka-osgi",
base = file("akka-osgi"),
dependencies = Seq(actor),
settings = defaultSettings ++ OSGi.osgi ++ Seq(
libraryDependencies ++= Dependencies.osgi
)
)
lazy val akkaSbtPlugin = Project(
id = "akka-sbt-plugin",
base = file("akka-sbt-plugin"),
@ -419,6 +428,8 @@ object Dependencies {
val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito)
val osgi = Seq(osgiCore, Test.pojosr, Test.scalatest, Test.junit)
val tutorials = Seq(Test.scalatest, Test.junit)
val docs = Seq(Test.scalatest, Test.junit, Test.specs2)
@ -434,6 +445,7 @@ object Dependency {
val Camel = "2.8.0"
val Logback = "0.9.28"
val Netty = "3.3.0.Final"
val OSGi = "4.2.0"
val Protobuf = "2.4.1"
val ScalaStm = "0.5"
val Scalatest = "1.6.1"
@ -444,6 +456,7 @@ object Dependency {
val camelCore = "org.apache.camel" % "camel-core" % V.Camel // ApacheV2
val netty = "io.netty" % "netty" % V.Netty // ApacheV2
val osgiCore = "org.osgi" % "org.osgi.core" % V.OSGi // ApacheV2
val protobuf = "com.google.protobuf" % "protobuf-java" % V.Protobuf // New BSD
val scalaStm = "org.scala-tools" % "scala-stm_2.9.1" % V.ScalaStm // Modified BSD (Scala)
val slf4jApi = "org.slf4j" % "slf4j-api" % V.Slf4j // MIT
@ -463,6 +476,7 @@ object Dependency {
val junit = "junit" % "junit" % "4.5" % "test" // Common Public License 1.0
val logback = "ch.qos.logback" % "logback-classic" % V.Logback % "test" // EPL 1.0 / LGPL 2.1
val mockito = "org.mockito" % "mockito-all" % "1.8.1" % "test" // MIT
val pojosr = "com.googlecode.pojosr" % "de.kalpatec.pojosr.framework" % "0.1.8" % "test" // ApacheV2
val scalatest = "org.scalatest" % "scalatest_2.9.1" % V.Scalatest % "test" // ApacheV2
val scalacheck = "org.scala-tools.testing" % "scalacheck_2.9.1" % "1.9" % "test" // New BSD
val specs2 = "org.specs2" % "specs2_2.9.1" % "1.9" % "test" // Modified BSD / ApacheV2
@ -487,6 +501,8 @@ object OSGi {
val mailboxesCommon = exports(Seq("akka.actor.mailbox.*"))
val osgi = exports(Seq("akka.osgi.*"))
val remote = exports(Seq("akka.remote.*", "akka.routing.*", "akka.serialization.*"))
val slf4j = exports(Seq("akka.event.slf4j.*"))