From 07dd65484922ca7328edf5b6027cfd0c04830157 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Tue, 29 May 2012 12:41:09 +0200 Subject: [PATCH 01/11] Adding a convenience BundleActivator implementation to bootstrap Akka from an OSGi bundle --- .../akka/osgi/ActorSystemActivator.scala | 78 +++++++++++++++++++ .../akka/osgi/ActorSystemActivatorTest.scala | 75 ++++++++++++++++++ project/AkkaBuild.scala | 16 ++++ 3 files changed, 169 insertions(+) create mode 100644 akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala create mode 100644 akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala new file mode 100644 index 0000000000..d63404334a --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -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--ActorSystem' where 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) + } + +} diff --git a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala new file mode 100644 index 0000000000..ffcc3cc0e7 --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala @@ -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) + } + +} diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index dbe9fbae9e..f0e6446879 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -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.*")) From 205b8ee7c13ad570429492081d6277e7c0882f69 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Thu, 31 May 2012 22:53:15 +0200 Subject: [PATCH 02/11] Initial stab at a Blueprint namespace handler --- .../additional/code/osgi/Activator.scala | 16 +++ akka-docs/additional/code/osgi/blueprint.xml | 10 ++ akka-docs/additional/osgi.rst | 17 +++ .../blueprint/akka-namespacehandler.xml | 15 +++ .../akka/osgi/blueprint/aries/akka.xsd | 67 ++++++++++++ .../akka/osgi/ActorSystemActivator.scala | 39 +------ .../akka/osgi/OsgiActorSystemFactory.scala | 56 ++++++++++ .../BlueprintActorSystemFactory.scala | 34 ++++++ .../blueprint/aries/NamespaceHandler.scala | 100 ++++++++++++++++++ .../impl/BundleDelegatingClassLoader.scala | 74 +++++++++++++ .../akka/osgi/blueprint/aries/simple.xml | 9 ++ .../akka/osgi/ActorSystemActivatorTest.scala | 3 - project/AkkaBuild.scala | 4 +- 13 files changed, 406 insertions(+), 38 deletions(-) create mode 100644 akka-docs/additional/code/osgi/Activator.scala create mode 100644 akka-docs/additional/code/osgi/blueprint.xml create mode 100644 akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml create mode 100644 akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd create mode 100644 akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala create mode 100644 akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala create mode 100644 akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala create mode 100644 akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala create mode 100644 akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml diff --git a/akka-docs/additional/code/osgi/Activator.scala b/akka-docs/additional/code/osgi/Activator.scala new file mode 100644 index 0000000000..0e3a5c82ee --- /dev/null +++ b/akka-docs/additional/code/osgi/Activator.scala @@ -0,0 +1,16 @@ +import akka.actor.{Props, ActorSystem} +import akka.osgi.ActorSystemActivator +import org.apache.servicemix.examples.akka.Listener +import org.apache.servicemix.examples.akka.Master + +//#Activator +class Activator extends ActorSystemActivator("PiSystem") { + + def configure(system: ActorSystem) { + val listener = system.actorOf(Props[Listener], name = "listener") + val master = system.actorOf(Props(new Master(4, 10000, 10000, listener)), name = "master") + master ! Calculate + } + +} +//#Activator \ No newline at end of file diff --git a/akka-docs/additional/code/osgi/blueprint.xml b/akka-docs/additional/code/osgi/blueprint.xml new file mode 100644 index 0000000000..f817da85b0 --- /dev/null +++ b/akka-docs/additional/code/osgi/blueprint.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/akka-docs/additional/osgi.rst b/akka-docs/additional/osgi.rst index aea554ef9c..3bedc8c7dd 100644 --- a/akka-docs/additional/osgi.rst +++ b/akka-docs/additional/osgi.rst @@ -8,3 +8,20 @@ To use Akka in an OSGi environment, the ``org.osgi.framework.bootdelegation`` property must be set to always delegate the ``sun.misc`` package to the boot classloader instead of resolving it through the normal OSGi class space. + +Activator +--------- + +To bootstrap Akka inside an OSGi environment, you can use the akka.osgi.AkkaSystemActivator class +to conveniently set up the ActorSystem. + +.. includecode:: code/osgi/Activator.scala#Activator + + +Blueprint +--------- + +For the Apache Aries Blueprint implementation, there's also a namespace handler available. The namespace URI +is http://akka.io/xmlns/blueprint/v1.0.0 and it can be used to set up an ActorSystem. + +.. includecode:: code/osgi/blueprint.xml diff --git a/akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml b/akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml new file mode 100644 index 0000000000..650738b10a --- /dev/null +++ b/akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml @@ -0,0 +1,15 @@ + + + + + + + + http://akka.io/xmlns/blueprint/v1.0.0 + + + + + + + diff --git a/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd b/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd new file mode 100644 index 0000000000..256dff22e9 --- /dev/null +++ b/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd @@ -0,0 +1,67 @@ + + + + + + + + + + + + Defines the configuration elements for setting up Akka with Blueprint + + + + + + + + Defines an Akka ActorSystem + + + + + + + + + + + + + + + + Defines an Akka Actor + + + + + + + + + diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index d63404334a..c6d24e8262 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -9,7 +9,10 @@ import java.util.Properties * Abstract {@link BundleActivator} implementation to bootstrap and configure an {@link ActorSystem} in an * OSGi environment. */ -abstract class ActorSystemActivator extends BundleActivator { +abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ String) extends BundleActivator { + + def this() = this({ context: BundleContext ⇒ null }) + def this(name: String) = this({ context: BundleContext ⇒ name }) var system: ActorSystem = null @@ -26,12 +29,8 @@ abstract class ActorSystemActivator extends BundleActivator { * @param context the BundleContext */ def start(context: BundleContext) { - system = createActorSystem(context) + system = OsgiActorSystemFactory(context).createActorSystem(nameFor(context)) configure(system) - - val properties = new Properties(); - properties.put("name", getActorSystemName(context)) - context.registerService(classOf[ActorSystem].getName, system, properties) } /** @@ -47,32 +46,4 @@ abstract class ActorSystemActivator extends BundleActivator { } } - /** - * 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--ActorSystem' where 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) - } - } diff --git a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala new file mode 100644 index 0000000000..8c41521964 --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala @@ -0,0 +1,56 @@ +package akka.osgi + +import impl.BundleDelegatingClassLoader +import org.osgi.framework.BundleContext +import java.util.Properties +import akka.actor.ActorSystem +import com.typesafe.config.{ ConfigFactory, Config } + +/** + * Factory class to create ActorSystem implementations in an OSGi environment. This mainly involves dealing with + * bundle classloaders appropriately to ensure that configuration files and classes get loaded properly + */ +class OsgiActorSystemFactory(val context: BundleContext) { + + /* + * Classloader that delegates to the bundle for which the factory is creating an ActorSystem + */ + val classloader = BundleDelegatingClassLoader.createFor(context) + + /** + * Creates the ActorSystem and registers it in the OSGi Service Registry + */ + def createActorSystem(name: String) = { + val system = ActorSystem(actorSystemName(name), actorSystemConfig(context), classloader) + registerService(system) + system + } + + def registerService(system: ActorSystem) { + val properties = new Properties(); + properties.put("name", system.name) + context.registerService(classOf[ActorSystem].getName, system, properties) + } + + /** + * Strategy method to create the Config for the ActorSystem, ensuring that the default/reference configuration is + * loaded from the akka-actor bundle. + */ + def actorSystemConfig(context: BundleContext): Config = { + val reference = ConfigFactory.defaultReference(classOf[ActorSystem].getClassLoader) + ConfigFactory.load(classloader).withFallback(reference) + } + + /** + * Determine a the ActorSystem name + */ + def actorSystemName(name: String): String = + Option(name).getOrElse("bundle-%s-ActorSystem".format(context.getBundle().getBundleId)) + +} + +object OsgiActorSystemFactory { + + def apply(context: BundleContext) = new OsgiActorSystemFactory(context) + +} diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala new file mode 100644 index 0000000000..92e7e8a099 --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala @@ -0,0 +1,34 @@ +package akka.osgi.blueprint + +import org.osgi.framework.BundleContext +import akka.osgi.OsgiActorSystemFactory +import collection.mutable.Buffer +import akka.actor.{ Actor, Props, ActorSystem } + +/** + * A set of helper/factory classes to build a Akka system using Blueprint + */ +class BlueprintActorSystemFactory(context: BundleContext, name: String) extends OsgiActorSystemFactory(context) { + + val systems: Buffer[ActorSystem] = Buffer() + + def this(context: BundleContext) = this(context, null) + + def create: ActorSystem = create(null) + def create(name: String): ActorSystem = { + val system = super.createActorSystem(name) + systems += system + system + } + + def destroy = for (system ← systems) { + system.shutdown() + } +} + +class BlueprintActorSystem(context: BundleContext, system: ActorSystem) { + + def createActor(name: String) = system.actorOf(Props(context.getBundle.loadClass(name).asInstanceOf[Class[Actor]])) + +} + diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala new file mode 100644 index 0000000000..b1412eae91 --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala @@ -0,0 +1,100 @@ +package akka.osgi.blueprint.aries + +import org.apache.aries.blueprint.ParserContext +import org.osgi.service.blueprint.container.ComponentDefinitionException +import org.apache.aries.blueprint.mutable.MutableBeanMetadata + +import collection.JavaConversions.setAsJavaSet +import org.osgi.framework.BundleContext +import org.apache.aries.blueprint.reflect.{ ValueMetadataImpl, RefMetadataImpl, BeanArgumentImpl } +import org.w3c.dom.{ NodeList, Element, Node } +import org.osgi.service.blueprint.reflect.{ BeanMetadata, ComponentMetadata } +import akka.actor.{ ActorRef, ActorSystem } +import akka.osgi.blueprint.{ BlueprintActorSystem, BlueprintActorSystemFactory } + +/** + * Aries Blueprint namespace handler implementation + */ +class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { + + val CLASS_ATTRIBUTE = "class"; + val ID_ATTRIBUTE = "id"; + val NAME_ATTRIBUTE = "name"; + + var idCounter = 1 + + def getSchemaLocation(namespace: String) = getClass().getResource("akka.xsd") + + def getManagedClasses = setAsJavaSet(Set(classOf[BlueprintActorSystemFactory])) + + def parse(element: Element, context: ParserContext) = { + val factory = context.createMetadata(classOf[MutableBeanMetadata]) + factory.setId(getId(context, element)) + factory.setScope(BeanMetadata.SCOPE_SINGLETON) + factory.setProcessor(true) + factory.setClassName(classOf[BlueprintActorSystemFactory].getName) + factory.setDestroyMethod("destroy") + factory.addArgument(new BeanArgumentImpl(new RefMetadataImpl("blueprintBundleContext"), classOf[BundleContext].getName, -1)) + + val system = context.createMetadata(classOf[MutableBeanMetadata]) + system.setId(getId(context, element)) + system.setFactoryComponent(factory) + system.setFactoryMethod("create") + system.setRuntimeClass(classOf[ActorSystem]) + if (element.hasAttribute(NAME_ATTRIBUTE)) { + system.addArgument(new BeanArgumentImpl(new ValueMetadataImpl(element.getAttribute(NAME_ATTRIBUTE)), classOf[String].getName, -1)) + } + + val actorsystem = context.createMetadata(classOf[MutableBeanMetadata]) + actorsystem.setId(getId(context, element)) + actorsystem.setClassName(classOf[BlueprintActorSystem].getName) + actorsystem.addArgument(new BeanArgumentImpl(new RefMetadataImpl("blueprintBundleContext"), classOf[BundleContext].getName, -1)) + actorsystem.addArgument(new BeanArgumentImpl(system, classOf[ActorSystem].getName, -1)) + context.getComponentDefinitionRegistry.registerComponentDefinition(actorsystem) + + val nodelist = element.getChildNodes + var i = 0 + while (i < nodelist.getLength) { + val node = nodelist.item(i) + node.getLocalName match { + case "actor" if node.isInstanceOf[Element] ⇒ parseActor(node.asInstanceOf[Element], context, actorsystem) + case _ ⇒ + } + i += 1 + } + factory + } + + def parseActor(node: Element, context: ParserContext, actorsystem: MutableBeanMetadata) = { + val actor = context.createMetadata(classOf[MutableBeanMetadata]) + actor.setFactoryComponent(actorsystem) + if (node.hasAttribute(CLASS_ATTRIBUTE)) { + actor.addArgument(new BeanArgumentImpl(new ValueMetadataImpl(node.getAttribute(CLASS_ATTRIBUTE)), classOf[String].getName, -1)) + } + actor.setId(getId(context, node)) + actor.setFactoryMethod("createActor") + // actor.setRuntimeClass(classOf[ActorRef]) + context.getComponentDefinitionRegistry.registerComponentDefinition(actor) + } + + def decorate(node: Node, component: ComponentMetadata, context: ParserContext) = + throw new ComponentDefinitionException("Bad xml syntax: node decoration is not supported"); + + def getId(context: ParserContext, element: Element) = { + if (element.hasAttribute(ID_ATTRIBUTE)) { + element.getAttribute(ID_ATTRIBUTE); + } else { + generateId(context); + } + } + + def generateId(context: ParserContext): String = { + var id = ""; + do { + idCounter += 1 + id = ".akka-" + idCounter; + } while (context.getComponentDefinitionRegistry().containsComponentDefinition(id)); + id; + } + +} diff --git a/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala new file mode 100644 index 0000000000..74592392d9 --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala @@ -0,0 +1,74 @@ +package akka.osgi.impl + +import java.net.URL +import java.util.Enumeration + +import org.osgi.framework.{ BundleContext, Bundle } + +/* + * Companion object to create bundle delegating classloader instances + */ +object BundleDelegatingClassLoader { + + /* + * Create a bundle delegating classloader for the bundle context's bundle + */ + def createFor(context: BundleContext) = new BundleDelegatingClassLoader(context.getBundle) + +} + +/* + * A bundle delegating classloader implemenation - this will try to load classes and resources from the bundle + * specified first and if there's a classloader specified, that will be used as a fallback + */ +class BundleDelegatingClassLoader(bundle: Bundle, classLoader: Option[ClassLoader]) extends ClassLoader { + + def this(bundle: Bundle) = this(bundle, None) + + protected override def findClass(name: String): Class[_] = bundle.loadClass(name) + + protected override def findResource(name: String): URL = { + val resource: URL = bundle.getResource(name) + classLoader match { + case Some(loader) if resource == null ⇒ loader.getResource(name) + case _ ⇒ resource + } + } + + @SuppressWarnings(Array("unchecked", "rawtypes")) + protected override def findResources(name: String): Enumeration[URL] = + bundle.getResources(name).asInstanceOf[Enumeration[URL]] + + protected override def loadClass(name: String, resolve: Boolean): Class[_] = { + val clazz: Class[_] = try { + findClass(name) + } catch { + case cnfe: ClassNotFoundException ⇒ { + classLoader match { + case Some(loader) ⇒ loadClass(name, loader) + case None ⇒ rethrowClassNotFoundException(name, cnfe) + } + } + } + if (resolve) { + resolveClass(clazz) + } + clazz + } + + private def loadClass(name: String, classLoader: ClassLoader) = + try { + classLoader.loadClass(name) + } catch { + case cnfe: ClassNotFoundException ⇒ rethrowClassNotFoundException(name, cnfe) + } + + def rethrowClassNotFoundException(name: String, cnfe: ClassNotFoundException): Nothing = + throw new ClassNotFoundException(name + " from bundle " + bundle.getBundleId + " (" + bundle.getSymbolicName + ")", cnfe) + + def getBundle: Bundle = bundle + + override def toString = String.format("BundleDelegatingClassLoader(%s)", bundle) + +} + diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml new file mode 100644 index 0000000000..d276ee86a0 --- /dev/null +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala index ffcc3cc0e7..34472e3537 100644 --- a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala +++ b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala @@ -29,10 +29,7 @@ class ActorSystemActivatorTest extends FlatSpec { 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 } diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index f0e6446879..629d0475f3 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -428,7 +428,7 @@ object Dependencies { val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito) - val osgi = Seq(osgiCore, Test.pojosr, Test.scalatest, Test.junit) + val osgi = Seq(osgiCore, ariesBlueprint, Runtime.logback, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) val tutorials = Seq(Test.scalatest, Test.junit) @@ -454,6 +454,7 @@ object Dependency { // Compile + val ariesBlueprint = "org.apache.aries.blueprint" % "org.apache.aries.blueprint" % "0.3.1" // ApacheV2 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 @@ -480,6 +481,7 @@ object Dependency { 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 + val tinybundles = "org.ops4j.pax.tinybundles" % "tinybundles" % "1.0.0" % "test" // ApacheV2 val zookeeper = "org.apache.hadoop.zookeeper" % "zookeeper" % "3.4.0" % "test" // ApacheV2 val log4j = "log4j" % "log4j" % "1.2.14" % "test" // ApacheV2 } From cc79aae1a41a36b3eaa3f2f96a1154e6b6cf9092 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Tue, 12 Jun 2012 16:08:19 +0200 Subject: [PATCH 03/11] Adding PojoSR tests and a lot of code cleanup --- .../additional/code/osgi/Activator.scala | 2 +- .../akka/osgi/blueprint/aries/akka.xsd | 41 +---- .../akka/osgi/ActorSystemActivator.scala | 1 - .../akka/osgi/OsgiActorSystemFactory.scala | 14 +- .../BlueprintActorSystemFactory.scala | 38 +++-- .../blueprint/aries/NamespaceHandler.scala | 154 +++++++++++------- .../osgi/blueprint/aries/ParserHelper.scala | 16 ++ .../akka/osgi/blueprint/aries/config.xml | 13 ++ .../akka/osgi/blueprint/aries/injection.xml | 13 ++ .../akka/osgi/blueprint/aries/simple.xml | 6 +- akka-osgi/src/test/resources/logback-test.xml | 23 +++ .../akka/osgi/ActorSystemActivatorTest.scala | 56 ++----- .../scala/akka/osgi/PojoSRTestSupport.scala | 150 +++++++++++++++++ .../aries/NamespaceHandlerTest.scala | 94 +++++++++++ .../akka/osgi/test/ActorSystemAwareBean.scala | 11 ++ .../test/scala/akka/osgi/test/PingPong.scala | 22 +++ .../osgi/test/TestActorSystemActivator.scala | 16 ++ project/AkkaBuild.scala | 17 +- 18 files changed, 517 insertions(+), 170 deletions(-) create mode 100644 akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala create mode 100644 akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml create mode 100644 akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml create mode 100644 akka-osgi/src/test/resources/logback-test.xml create mode 100644 akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala create mode 100644 akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala create mode 100644 akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala create mode 100644 akka-osgi/src/test/scala/akka/osgi/test/PingPong.scala create mode 100644 akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala diff --git a/akka-docs/additional/code/osgi/Activator.scala b/akka-docs/additional/code/osgi/Activator.scala index 0e3a5c82ee..06a538d242 100644 --- a/akka-docs/additional/code/osgi/Activator.scala +++ b/akka-docs/additional/code/osgi/Activator.scala @@ -1,4 +1,4 @@ -import akka.actor.{Props, ActorSystem} +import akka.actor.{ Props, ActorSystem } import akka.osgi.ActorSystemActivator import org.apache.servicemix.examples.akka.Listener import org.apache.servicemix.examples.akka.Master diff --git a/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd b/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd index 256dff22e9..d7d0f77a2c 100644 --- a/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd +++ b/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd @@ -1,25 +1,4 @@ - - - - - + + - - - - - Defines an Akka Actor - - - - - - + + + + Defines an Akka ActorSystem configuration + + diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index c6d24e8262..ef04607976 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -40,7 +40,6 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ String) extends */ def stop(context: BundleContext) { if (system != null) { - system.shutdown() system.shutdown() system = null } diff --git a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala index 8c41521964..cddf797d07 100644 --- a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala +++ b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala @@ -2,9 +2,9 @@ package akka.osgi import impl.BundleDelegatingClassLoader import org.osgi.framework.BundleContext -import java.util.Properties import akka.actor.ActorSystem import com.typesafe.config.{ ConfigFactory, Config } +import java.util.{ Dictionary, Properties } /** * Factory class to create ActorSystem implementations in an OSGi environment. This mainly involves dealing with @@ -20,16 +20,18 @@ class OsgiActorSystemFactory(val context: BundleContext) { /** * Creates the ActorSystem and registers it in the OSGi Service Registry */ - def createActorSystem(name: String) = { + def createActorSystem(name: String): ActorSystem = createActorSystem(Option(name)) + + def createActorSystem(name: Option[String]): ActorSystem = { val system = ActorSystem(actorSystemName(name), actorSystemConfig(context), classloader) registerService(system) system } def registerService(system: ActorSystem) { - val properties = new Properties(); + val properties = new Properties() properties.put("name", system.name) - context.registerService(classOf[ActorSystem].getName, system, properties) + context.registerService(classOf[ActorSystem].getName, system, properties.asInstanceOf[Dictionary[String, Any]]) } /** @@ -44,8 +46,8 @@ class OsgiActorSystemFactory(val context: BundleContext) { /** * Determine a the ActorSystem name */ - def actorSystemName(name: String): String = - Option(name).getOrElse("bundle-%s-ActorSystem".format(context.getBundle().getBundleId)) + def actorSystemName(name: Option[String]): String = + name.getOrElse("bundle-%s-ActorSystem".format(context.getBundle().getBundleId)) } diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala index 92e7e8a099..51c3e7291f 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala @@ -4,31 +4,39 @@ import org.osgi.framework.BundleContext import akka.osgi.OsgiActorSystemFactory import collection.mutable.Buffer import akka.actor.{ Actor, Props, ActorSystem } +import com.typesafe.config.ConfigFactory /** * A set of helper/factory classes to build a Akka system using Blueprint */ class BlueprintActorSystemFactory(context: BundleContext, name: String) extends OsgiActorSystemFactory(context) { - val systems: Buffer[ActorSystem] = Buffer() + var config: Option[String] = None - def this(context: BundleContext) = this(context, null) + lazy val system = super.createActorSystem(stringToOption(name)) - def create: ActorSystem = create(null) - def create(name: String): ActorSystem = { - val system = super.createActorSystem(name) - systems += system - system + def setConfig(config: String) = { this.config = Some(config) } + + def create = system + + def destroy = system.shutdown() + + def stringToOption(original: String) = if (original == null || original.isEmpty) { + None + } else { + Some(original) } - def destroy = for (system ← systems) { - system.shutdown() + /** + * Strategy method to create the Config for the ActorSystem, ensuring that the default/reference configuration is + * loaded from the akka-actor bundle. + */ + override def actorSystemConfig(context: BundleContext) = { + config match { + case Some(value) ⇒ ConfigFactory.parseString(value).withFallback(super.actorSystemConfig(context)) + case None ⇒ super.actorSystemConfig(context) + } + } } -class BlueprintActorSystem(context: BundleContext, system: ActorSystem) { - - def createActor(name: String) = system.actorOf(Props(context.getBundle.loadClass(name).asInstanceOf[Class[Actor]])) - -} - diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala index b1412eae91..245ea538b6 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala @@ -9,92 +9,122 @@ import org.osgi.framework.BundleContext import org.apache.aries.blueprint.reflect.{ ValueMetadataImpl, RefMetadataImpl, BeanArgumentImpl } import org.w3c.dom.{ NodeList, Element, Node } import org.osgi.service.blueprint.reflect.{ BeanMetadata, ComponentMetadata } -import akka.actor.{ ActorRef, ActorSystem } -import akka.osgi.blueprint.{ BlueprintActorSystem, BlueprintActorSystemFactory } +import akka.actor.{ ActorSystem } +import akka.osgi.blueprint.{ BlueprintActorSystemFactory } +import java.util.concurrent.atomic.AtomicInteger + +import ParserHelper.childElements /** * Aries Blueprint namespace handler implementation */ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { - val CLASS_ATTRIBUTE = "class"; - val ID_ATTRIBUTE = "id"; - val NAME_ATTRIBUTE = "name"; + import NamespaceHandler._ - var idCounter = 1 + val idCounter = new AtomicInteger(0) def getSchemaLocation(namespace: String) = getClass().getResource("akka.xsd") def getManagedClasses = setAsJavaSet(Set(classOf[BlueprintActorSystemFactory])) - def parse(element: Element, context: ParserContext) = { - val factory = context.createMetadata(classOf[MutableBeanMetadata]) - factory.setId(getId(context, element)) - factory.setScope(BeanMetadata.SCOPE_SINGLETON) - factory.setProcessor(true) - factory.setClassName(classOf[BlueprintActorSystemFactory].getName) - factory.setDestroyMethod("destroy") - factory.addArgument(new BeanArgumentImpl(new RefMetadataImpl("blueprintBundleContext"), classOf[BundleContext].getName, -1)) - - val system = context.createMetadata(classOf[MutableBeanMetadata]) - system.setId(getId(context, element)) - system.setFactoryComponent(factory) - system.setFactoryMethod("create") - system.setRuntimeClass(classOf[ActorSystem]) - if (element.hasAttribute(NAME_ATTRIBUTE)) { - system.addArgument(new BeanArgumentImpl(new ValueMetadataImpl(element.getAttribute(NAME_ATTRIBUTE)), classOf[String].getName, -1)) - } - - val actorsystem = context.createMetadata(classOf[MutableBeanMetadata]) - actorsystem.setId(getId(context, element)) - actorsystem.setClassName(classOf[BlueprintActorSystem].getName) - actorsystem.addArgument(new BeanArgumentImpl(new RefMetadataImpl("blueprintBundleContext"), classOf[BundleContext].getName, -1)) - actorsystem.addArgument(new BeanArgumentImpl(system, classOf[ActorSystem].getName, -1)) - context.getComponentDefinitionRegistry.registerComponentDefinition(actorsystem) - - val nodelist = element.getChildNodes - var i = 0 - while (i < nodelist.getLength) { - val node = nodelist.item(i) - node.getLocalName match { - case "actor" if node.isInstanceOf[Element] ⇒ parseActor(node.asInstanceOf[Element], context, actorsystem) - case _ ⇒ - } - i += 1 - } - factory - } - - def parseActor(node: Element, context: ParserContext, actorsystem: MutableBeanMetadata) = { - val actor = context.createMetadata(classOf[MutableBeanMetadata]) - actor.setFactoryComponent(actorsystem) - if (node.hasAttribute(CLASS_ATTRIBUTE)) { - actor.addArgument(new BeanArgumentImpl(new ValueMetadataImpl(node.getAttribute(CLASS_ATTRIBUTE)), classOf[String].getName, -1)) - } - actor.setId(getId(context, node)) - actor.setFactoryMethod("createActor") - // actor.setRuntimeClass(classOf[ActorRef]) - context.getComponentDefinitionRegistry.registerComponentDefinition(actor) + def parse(element: Element, context: ParserContext) = element.getLocalName match { + case ACTORSYSTEM_ELEMENT_NAME ⇒ parseActorSystem(element, context) + case _ ⇒ throw new ComponentDefinitionException("Unexpected element for Akka namespace: %s".format(element)) } def decorate(node: Node, component: ComponentMetadata, context: ParserContext) = throw new ComponentDefinitionException("Bad xml syntax: node decoration is not supported"); + /* + * Parse + */ + def parseActorSystem(element: Element, context: ParserContext) = { + val factory = createFactoryBean(context, element.getAttribute(NAME_ATTRIBUTE)) + + for (child ← childElements(element)) { + child.getLocalName match { + case CONFIG_ELEMENT_NAME ⇒ parseConfig(child, context, factory) + case _ ⇒ throw new ComponentDefinitionException("Unexpected child element %s found in %s".format(child, element)) + } + } + + createActorSystemBean(context, element, factory) + } + + /* + * Parse + */ + def parseConfig(node: Element, context: ParserContext, factory: MutableBeanMetadata) = { + factory.addProperty("config", new ValueMetadataImpl(node.getTextContent)) + } + + /* + * Create the bean definition for the ActorSystem + */ + def createActorSystemBean(context: ParserContext, element: Element, factory: MutableBeanMetadata): MutableBeanMetadata = { + val system = context.createMetadata(classOf[MutableBeanMetadata]) + system.setId(getId(context, element)) + system.setFactoryComponent(factory) + + system.setFactoryMethod(FACTORY_METHOD_NAME) + system.setRuntimeClass(classOf[ActorSystem]) + system + } + + /* + * Create the bean definition for the BlueprintActorSystemFactory + */ + def createFactoryBean(context: ParserContext, name: String): MutableBeanMetadata = { + val factory = context.createMetadata(classOf[MutableBeanMetadata]) + factory.setId(findAvailableId(context)) + factory.setScope(BeanMetadata.SCOPE_SINGLETON) + factory.setProcessor(true) + factory.setClassName(classOf[BlueprintActorSystemFactory].getName) + + factory.setDestroyMethod(DESTROY_METHOD_NAME) + + factory.addArgument(new BeanArgumentImpl(new RefMetadataImpl(BUNDLE_CONTEXT_REFID), classOf[BundleContext].getName, -1)) + factory.addArgument(new BeanArgumentImpl(new ValueMetadataImpl(name), classOf[String].getName, -1)) + factory.setProcessor(true) + context.getComponentDefinitionRegistry.registerComponentDefinition(factory) + factory + } + + /* + * Get the assigned id or generate a suitable id + */ def getId(context: ParserContext, element: Element) = { if (element.hasAttribute(ID_ATTRIBUTE)) { element.getAttribute(ID_ATTRIBUTE); } else { - generateId(context); + findAvailableId(context); } } - def generateId(context: ParserContext): String = { - var id = ""; - do { - idCounter += 1 - id = ".akka-" + idCounter; - } while (context.getComponentDefinitionRegistry().containsComponentDefinition(id)); - id; + /* + * Find the next available component id + */ + def findAvailableId(context: ParserContext): String = { + val id = ".akka-" + idCounter.incrementAndGet() + if (context.getComponentDefinitionRegistry.containsComponentDefinition(id)) { + // id already exists, let's try the next one + findAvailableId(context) + } else id } +} + +object NamespaceHandler { + + private val ID_ATTRIBUTE = "id"; + private val NAME_ATTRIBUTE = "name"; + + private val BUNDLE_CONTEXT_REFID = "blueprintBundleContext" + + private val ACTORSYSTEM_ELEMENT_NAME = "actor-system" + private val CONFIG_ELEMENT_NAME = "config" + + private val DESTROY_METHOD_NAME = "destroy" + private val FACTORY_METHOD_NAME = "create" } diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala new file mode 100644 index 0000000000..82fb7bc113 --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala @@ -0,0 +1,16 @@ +package akka.osgi.blueprint.aries + +import org.w3c.dom.{ Node, Element } + +/** + * Helper class to deal with the W3C DOM types + */ +object ParserHelper { + + def childElements(element: Element) = children(element).filter(_.getNodeType == Node.ELEMENT_NODE).asInstanceOf[Seq[Element]] + + private[this] def children(element: Element) = { + val nodelist = element.getChildNodes + for (index ← 0 until nodelist.getLength) yield nodelist.item(index) + } +} diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml new file mode 100644 index 0000000000..6bd3d49c9d --- /dev/null +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml @@ -0,0 +1,13 @@ + + + + + + some.config { + key=value + } + + + + diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml new file mode 100644 index 0000000000..9712ee6d1f --- /dev/null +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml index d276ee86a0..a46834f74b 100644 --- a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml @@ -1,9 +1,7 @@ + xmlns:akka="http://akka.io/xmlns/blueprint/v1.0.0"> - - - + diff --git a/akka-osgi/src/test/resources/logback-test.xml b/akka-osgi/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..9c441a6fb6 --- /dev/null +++ b/akka-osgi/src/test/resources/logback-test.xml @@ -0,0 +1,23 @@ + + + + + + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n + + + + + target/akka-osgi.log + true + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n + + + + + + + + diff --git a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala index 34472e3537..0b2fdd19ac 100644 --- a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala +++ b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala @@ -1,53 +1,28 @@ 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.actor.ActorSystem import akka.pattern.ask import akka.dispatch.Await import akka.util.duration._ import akka.util.Timeout +import de.kalpatec.pojosr.framework.launch.BundleDescriptor +import test.TestActorSystemActivator +import test.PingPong._ +import PojoSRTestSupport.bundle /** * Test cases for {@link ActorSystemActivator} */ -class ActorSystemActivatorTest extends FlatSpec { +class ActorSystemActivatorTest extends FlatSpec with PojoSRTestSupport { - abstract class TestMessage + val TEST_BUNDLE_NAME = "akka.osgi.test.activator" - case object Ping extends TestMessage - case object Pong extends TestMessage + val testBundles: Seq[BundleDescriptor] = Seq( + bundle(TEST_BUNDLE_NAME).withActivator(classOf[TestActorSystemActivator])) - class PongActor extends Actor { - def receive = { - case Ping ⇒ - sender ! Pong - } - } - - lazy val context: BundleContext = { - val config = new HashMap[String, AnyRef](); - 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] + "ActorSystemActivator" should "start and register the ActorSystem when bundle starts" in { + val system = serviceForType[ActorSystem] val actor = system.actorFor("/user/pong") implicit val timeout = Timeout(5 seconds) @@ -56,14 +31,11 @@ class ActorSystemActivatorTest extends FlatSpec { 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] + it should "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] assert(!system.isTerminated) - activator.stop(context) + bundleForName(TEST_BUNDLE_NAME).stop() system.awaitTermination() assert(system.isTerminated) diff --git a/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala b/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala new file mode 100644 index 0000000000..cbed282c18 --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala @@ -0,0 +1,150 @@ +package akka.osgi + +import de.kalpatec.pojosr.framework.launch.{ BundleDescriptor, PojoServiceRegistryFactory, ClasspathScanner } + +import scala.collection.JavaConversions.seqAsJavaList +import scala.collection.JavaConversions.collectionAsScalaIterable +import org.apache.commons.io.IOUtils.copy + +import org.osgi.framework._ +import java.net.URL + +import java.util.jar.JarInputStream +import java.io.{ FileInputStream, FileOutputStream, File } +import java.util.{ Date, ServiceLoader, HashMap } +import org.scalatest.{ BeforeAndAfterAll, Suite } + +/** + * Trait that provides support for building akka-osgi tests using PojoSR + */ +trait PojoSRTestSupport extends Suite with BeforeAndAfterAll { + + val MAX_WAIT_TIME = 8000; + val START_WAIT_TIME = 100; + + implicit def buildBundleDescriptor(builder: BundleDescriptorBuilder) = builder.build + + /** + * All bundles being found on the test classpath are automatically installed and started in the PojoSR runtime. + * Implement this to define the extra bundles that should be available for testing. + */ + val testBundles: Seq[BundleDescriptor] + + lazy val context: BundleContext = { + val config = new HashMap[String, AnyRef](); + System.setProperty("org.osgi.framework.storage", "target/akka-osgi/" + System.currentTimeMillis) + + val bundles = new ClasspathScanner().scanForBundles() + bundles.addAll(testBundles) + config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, bundles); + + val loader: ServiceLoader[PojoServiceRegistryFactory] = ServiceLoader.load(classOf[PojoServiceRegistryFactory]) + + val registry = loader.iterator.next.newPojoServiceRegistry(config) + registry.getBundleContext + } + + // Ensure bundles get stopped at the end of the test to release resources and stop threads + override protected def afterAll() = context.getBundles.foreach(_.stop) + + /** + * Convenience method to find a bundle by symbolic name + */ + def bundleForName(name: String) = context.getBundles.find(_.getSymbolicName == name) match { + case Some(bundle) ⇒ bundle + case None ⇒ fail("Unable to find bundle with symbolic name %s".format(name)) + } + + /** + * Convenience method to find a service by interface. If the service is not already available in the OSGi Service + * Registry, this method will wait for a few seconds for the service to appear. + */ + def serviceForType[T](implicit manifest: Manifest[T]): T = { + val reference = awaitReference(manifest.erasure) + context.getService(reference).asInstanceOf[T] + } + + def awaitReference(serviceType: Class[_]): ServiceReference = awaitReference(serviceType, START_WAIT_TIME) + + def awaitReference(serviceType: Class[_], wait: Long): ServiceReference = { + val option = Option(context.getServiceReference(serviceType.getName)) + option match { + case Some(reference) ⇒ reference; + case None if (wait > MAX_WAIT_TIME) ⇒ fail("Gave up waiting for service of type %s".format(serviceType)) + case None ⇒ { + Thread.sleep(wait); + awaitReference(serviceType, wait * 2); + } + } + } +} + +object PojoSRTestSupport { + + /** + * Convenience method to define additional test bundles + */ + def bundle(name: String) = new BundleDescriptorBuilder(name) + +} + +/** + * Helper class to make it easier to define test bundles + */ +class BundleDescriptorBuilder(name: String) { + + import org.ops4j.pax.tinybundles.core.TinyBundles + + val tinybundle = TinyBundles.bundle.set(Constants.BUNDLE_SYMBOLICNAME, name) + + def withBlueprintFile(name: String, contents: URL) = + returnBuilder(tinybundle.add("OSGI-INF/blueprint/%s".format(name), contents)) + + def withBlueprintFile(contents: URL): BundleDescriptorBuilder = withBlueprintFile(filename(contents), contents) + + def withActivator(activator: Class[_ <: BundleActivator]) = + returnBuilder(tinybundle.set(Constants.BUNDLE_ACTIVATOR, activator.getName)) + + def returnBuilder(block: ⇒ Unit) = { + block + this + } + + def build = { + val file: File = tinybundleToJarFile(name) + + new BundleDescriptor( + getClass().getClassLoader(), + new URL("jar:" + file.toURI().toString() + "!/"), + extractHeaders(file)); + } + + def extractHeaders(file: File): HashMap[String, String] = { + val headers = new HashMap[String, String](); + + val jis = new JarInputStream(new FileInputStream(file)); + try { + for (entry ← jis.getManifest().getMainAttributes().entrySet()) { + headers.put(entry.getKey().toString(), entry.getValue().toString()); + } + } finally { + jis.close() + } + + headers + } + + def tinybundleToJarFile(name: String): File = { + val file = new File("target/%s-%tQ.jar".format(name, new Date())); + val fos = new FileOutputStream(file); + try { + copy(tinybundle.build(), fos); + } finally { + fos.close(); + } + file + } + + private[this] def filename(url: URL) = url.getFile.split("/").last +} + diff --git a/akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala b/akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala new file mode 100644 index 0000000000..bbe26b5e92 --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala @@ -0,0 +1,94 @@ +package akka.osgi.blueprint.aries + +import org.scalatest.FlatSpec +import akka.actor.ActorSystem +import de.kalpatec.pojosr.framework.launch.BundleDescriptor +import akka.osgi.PojoSRTestSupport +import akka.osgi.PojoSRTestSupport.bundle +import akka.osgi.test.ActorSystemAwareBean + +/** + * Test cases for {@link ActorSystemActivator} + */ +object NamespaceHandlerTest { + + /* + * Bundle-SymbolicName to easily find our test bundle + */ + val TEST_BUNDLE_NAME = "akka.osgi.test.aries.namespace" + + /* + * Bundle descriptor representing the akka-osgi bundle itself + */ + val AKKA_OSGI_BLUEPRINT = + bundle("akka-osgi").withBlueprintFile(getClass.getResource("/OSGI-INF/blueprint/akka-namespacehandler.xml")) + +} + +class SimpleNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { + + import NamespaceHandlerTest._ + + val testBundles: Seq[BundleDescriptor] = Seq( + AKKA_OSGI_BLUEPRINT, + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("simple.xml"))) + + "simple.xml" should "set up ActorSystem when bundle starts" in { + val system = serviceForType[ActorSystem] + assert(system != null) + } + + it should "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] + assert(!system.isTerminated) + + bundleForName(TEST_BUNDLE_NAME).stop() + + system.awaitTermination() + assert(system.isTerminated) + } + +} + +class ConfigNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { + + import NamespaceHandlerTest._ + + val testBundles: Seq[BundleDescriptor] = Seq( + AKKA_OSGI_BLUEPRINT, + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("config.xml"))) + + "config.xml" should "set up ActorSystem when bundle starts" in { + val system = serviceForType[ActorSystem] + assert(system != null) + + assert(system.settings.config.getString("some.config.key") == "value") + } + + it should "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] + assert(!system.isTerminated) + + bundleForName(TEST_BUNDLE_NAME).stop() + + system.awaitTermination() + assert(system.isTerminated) + } + +} + +class DependencyInjectionNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { + + import NamespaceHandlerTest._ + + val testBundles: Seq[BundleDescriptor] = Seq( + AKKA_OSGI_BLUEPRINT, + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("injection.xml"))) + + "injection.xml" should "set up bean containing ActorSystem" in { + val bean = serviceForType[ActorSystemAwareBean] + assert(bean != null) + assert(bean.system != null) + } + +} diff --git a/akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala b/akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala new file mode 100644 index 0000000000..ca0df7cc04 --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala @@ -0,0 +1,11 @@ +package akka.osgi.test + +import akka.actor.ActorSystem + +/** + * Just a simple POJO that can contain an actor system. + * Used for testing dependency injection with Blueprint + */ +class ActorSystemAwareBean(val system: ActorSystem) { + +} diff --git a/akka-osgi/src/test/scala/akka/osgi/test/PingPong.scala b/akka-osgi/src/test/scala/akka/osgi/test/PingPong.scala new file mode 100644 index 0000000000..6a7409c667 --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/test/PingPong.scala @@ -0,0 +1,22 @@ +package akka.osgi.test + +import akka.actor.Actor + +/** + * Simple ping-pong actor, used for testing + */ +object PingPong { + + abstract class TestMessage + + case object Ping extends TestMessage + case object Pong extends TestMessage + + class PongActor extends Actor { + def receive = { + case Ping ⇒ + sender ! Pong + } + } + +} diff --git a/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala b/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala new file mode 100644 index 0000000000..2a44e91e4a --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala @@ -0,0 +1,16 @@ +package akka.osgi.test + +import akka.osgi.ActorSystemActivator +import akka.actor.{ Props, ActorSystem } +import PingPong._ + +/** + * Sample ActorSystemActivator implementation used for testing purposes + */ +class TestActorSystemActivator extends ActorSystemActivator { + + def configure(system: ActorSystem) { + system.actorOf(Props(new PongActor), name = "pong") + } + +} diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 79a2aeb694..7b3f5bf65c 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -480,7 +480,7 @@ object Dependencies { val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito) - val osgi = Seq(osgiCore, ariesBlueprint, Runtime.logback, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) + val osgi = Seq(osgiCore, ariesBlueprint, Runtime.logback, Test.ariesProxy, Test.commonsIo, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) val tutorials = Seq(Test.scalatest, Test.junit) @@ -505,7 +505,7 @@ object Dependency { } // Compile - val ariesBlueprint = "org.apache.aries.blueprint" % "org.apache.aries.blueprint" % "0.3.1" // ApacheV2 + val ariesBlueprint = "org.apache.aries.blueprint" % "org.apache.aries.blueprint" % "0.3.2" // ApacheV2 val config = "com.typesafe" % "config" % "0.4.1" // ApacheV2 val camelCore = "org.apache.camel" % "camel-core" % V.Camel // ApacheV2 val netty = "io.netty" % "netty" % V.Netty // ApacheV2 @@ -518,12 +518,13 @@ object Dependency { // Test object Test { + val ariesProxy = "org.apache.aries.proxy" % "org.apache.aries.proxy.impl" % "0.3" % "test" // ApacheV2 val commonsMath = "org.apache.commons" % "commons-math" % "2.1" % "test" // ApacheV2 - val commonsIo = "commons-io" % "commons-io" % "2.0.1" % "test"// ApacheV2 + val commonsIo = "commons-io" % "commons-io" % "2.0.1" % "test"// ApacheV2 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 pojosr = "com.googlecode.pojosr" % "de.kalpatec.pojosr.framework" % "0.1.4" % "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 @@ -548,7 +549,10 @@ object OSGi { val mailboxesCommon = exports(Seq("akka.actor.mailbox.*")) - val osgi = exports(Seq("akka.osgi.*")) + val osgi = exports(Seq("akka.osgi.*")) ++ Seq( + OsgiKeys.importPackage := Seq("org.apache.aries.blueprint.*;resolution:=optional", + "org.osgi.service.blueprint.*;resolution:=optional") ++ defaultImports + ) val remote = exports(Seq("akka.remote.*", "akka.routing.*", "akka.serialization.*")) @@ -559,10 +563,11 @@ object OSGi { val zeroMQ = exports(Seq("akka.zeromq.*")) def exports(packages: Seq[String]) = osgiSettings ++ Seq( - OsgiKeys.importPackage := Seq("!sun.misc", akkaImport(), configImport(), scalaImport(), "*"), + OsgiKeys.importPackage := defaultImports, OsgiKeys.exportPackage := packages ) + def defaultImports = Seq("!sun.misc", akkaImport(), configImport(), scalaImport(), "*") def akkaImport(packageName: String = "akka.*") = "%s;version=\"[2.1,2.2)\"".format(packageName) def configImport(packageName: String = "com.typesafe.config.*") = "%s;version=\"[0.4.1,0.5)\"".format(packageName) def scalaImport(packageName: String = "scala.*") = "%s;version=\"[2.9.2,2.10)\"".format(packageName) From cfbf13ec2a042760cb35c21dc8972550c90eff2b Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Tue, 12 Jun 2012 16:22:21 +0200 Subject: [PATCH 04/11] Using Test.logback instead of Runtime.logback --- project/AkkaBuild.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 7b3f5bf65c..7b2b7846e9 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -480,7 +480,7 @@ object Dependencies { val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito) - val osgi = Seq(osgiCore, ariesBlueprint, Runtime.logback, Test.ariesProxy, Test.commonsIo, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) + val osgi = Seq(osgiCore, ariesBlueprint, Test.logback, Test.ariesProxy, Test.commonsIo, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) val tutorials = Seq(Test.scalatest, Test.junit) From f33c45090dfd03717148f15f03edaf72a7a3e2bd Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Tue, 12 Jun 2012 16:57:25 +0200 Subject: [PATCH 05/11] Update docs and use Option[String] in the activator instead of null --- akka-docs/additional/code/osgi/blueprint.xml | 12 ++++++++---- .../main/scala/akka/osgi/ActorSystemActivator.scala | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/akka-docs/additional/code/osgi/blueprint.xml b/akka-docs/additional/code/osgi/blueprint.xml index f817da85b0..8fcedb990c 100644 --- a/akka-docs/additional/code/osgi/blueprint.xml +++ b/akka-docs/additional/code/osgi/blueprint.xml @@ -2,9 +2,13 @@ - - - - + + + + some.config { + key=value + } + + diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index ef04607976..7f60aebccc 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -9,10 +9,10 @@ import java.util.Properties * Abstract {@link BundleActivator} implementation to bootstrap and configure an {@link ActorSystem} in an * OSGi environment. */ -abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ String) extends BundleActivator { +abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) extends BundleActivator { - def this() = this({ context: BundleContext ⇒ null }) - def this(name: String) = this({ context: BundleContext ⇒ name }) + def this() = this({ context: BundleContext ⇒ None }) + def this(name: String) = this({ context: BundleContext ⇒ Some(name) }) var system: ActorSystem = null From 2bf2cec282d1606d10692aaca770a53b5bc52917 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Wed, 13 Jun 2012 22:37:41 +0200 Subject: [PATCH 06/11] Service registration is now optional, service references get cleaned up properly --- .../additional/code/osgi/Activator.scala | 5 ++- .../akka/osgi/ActorSystemActivator.scala | 35 +++++++++++++------ .../akka/osgi/OsgiActorSystemFactory.scala | 16 ++------- .../akka/osgi/blueprint/aries/config.xml | 4 ++- .../akka/osgi/blueprint/aries/simple.xml | 4 ++- .../osgi/test/TestActorSystemActivator.scala | 4 ++- 6 files changed, 40 insertions(+), 28 deletions(-) diff --git a/akka-docs/additional/code/osgi/Activator.scala b/akka-docs/additional/code/osgi/Activator.scala index 06a538d242..34e83fcf77 100644 --- a/akka-docs/additional/code/osgi/Activator.scala +++ b/akka-docs/additional/code/osgi/Activator.scala @@ -6,7 +6,10 @@ import org.apache.servicemix.examples.akka.Master //#Activator class Activator extends ActorSystemActivator("PiSystem") { - def configure(system: ActorSystem) { + def configure(context: BundleContext, system: ActorSystem) { + // optionally register the ActorSystem in the OSGi Service Registry + registerService(context, system) + val listener = system.actorOf(Props[Listener], name = "listener") val master = system.actorOf(Props(new Master(4, 10000, 10000, listener)), name = "master") master ! Calculate diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index 7f60aebccc..546ff8c2c4 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -2,8 +2,8 @@ package akka.osgi import com.typesafe.config.{ Config, ConfigFactory } import akka.actor.ActorSystem -import org.osgi.framework.{ BundleContext, BundleActivator } -import java.util.Properties +import java.util.{ Dictionary, Properties } +import org.osgi.framework.{ ServiceRegistration, BundleContext, BundleActivator } /** * Abstract {@link BundleActivator} implementation to bootstrap and configure an {@link ActorSystem} in an @@ -14,14 +14,16 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) def this() = this({ context: BundleContext ⇒ None }) def this(name: String) = this({ context: BundleContext ⇒ Some(name) }) - var system: ActorSystem = null + var system: Option[ActorSystem] = None + var registration: Option[ServiceRegistration] = None /** * Implement this method to add your own actors to the ActorSystem * + * @param context the bundle context * @param system the ActorSystem that was created by the activator */ - def configure(system: ActorSystem) + def configure(context: BundleContext, system: ActorSystem) /** * Sets up a new ActorSystem and registers it in the OSGi Service Registry @@ -29,20 +31,31 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) * @param context the BundleContext */ def start(context: BundleContext) { - system = OsgiActorSystemFactory(context).createActorSystem(nameFor(context)) - configure(system) + system = Some(OsgiActorSystemFactory(context).createActorSystem(nameFor(context))) + system.foreach(configure(context, _)) } /** - * Shuts down the ActorSystem when the bundle is stopped. + * Shuts down the ActorSystem when the bundle is stopped and, if necessary, unregisters a service registration * * @param context the BundleContext */ def stop(context: BundleContext) { - if (system != null) { - system.shutdown() - system = null - } + registration.foreach(_.unregister()) + system.foreach(_.shutdown()) + } + + /** + * Register the actor system in the OSGi service registry + * + * @param context the bundle context + * @param system the actor system + */ + def registerService(context: BundleContext, system: ActorSystem) { + val properties = new Properties() + properties.put("name", system.name) + registration = Some(context.registerService(classOf[ActorSystem].getName, system, + properties.asInstanceOf[Dictionary[String, Any]])) } } diff --git a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala index cddf797d07..2c5a6eca14 100644 --- a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala +++ b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala @@ -1,10 +1,9 @@ package akka.osgi import impl.BundleDelegatingClassLoader -import org.osgi.framework.BundleContext import akka.actor.ActorSystem import com.typesafe.config.{ ConfigFactory, Config } -import java.util.{ Dictionary, Properties } +import org.osgi.framework.BundleContext /** * Factory class to create ActorSystem implementations in an OSGi environment. This mainly involves dealing with @@ -22,17 +21,8 @@ class OsgiActorSystemFactory(val context: BundleContext) { */ def createActorSystem(name: String): ActorSystem = createActorSystem(Option(name)) - def createActorSystem(name: Option[String]): ActorSystem = { - val system = ActorSystem(actorSystemName(name), actorSystemConfig(context), classloader) - registerService(system) - system - } - - def registerService(system: ActorSystem) { - val properties = new Properties() - properties.put("name", system.name) - context.registerService(classOf[ActorSystem].getName, system, properties.asInstanceOf[Dictionary[String, Any]]) - } + def createActorSystem(name: Option[String]): ActorSystem = + ActorSystem(actorSystemName(name), actorSystemConfig(context), classloader) /** * Strategy method to create the Config for the ActorSystem, ensuring that the default/reference configuration is diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml index 6bd3d49c9d..ce9f48c551 100644 --- a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml @@ -2,7 +2,9 @@ - + + + some.config { key=value diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml index a46834f74b..2ac6552f80 100644 --- a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml @@ -2,6 +2,8 @@ - + + + diff --git a/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala b/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala index 2a44e91e4a..90305bc663 100644 --- a/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala +++ b/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala @@ -3,14 +3,16 @@ package akka.osgi.test import akka.osgi.ActorSystemActivator import akka.actor.{ Props, ActorSystem } import PingPong._ +import org.osgi.framework.BundleContext /** * Sample ActorSystemActivator implementation used for testing purposes */ class TestActorSystemActivator extends ActorSystemActivator { - def configure(system: ActorSystem) { + def configure(context: BundleContext, system: ActorSystem) { system.actorOf(Props(new PongActor), name = "pong") + registerService(context, system) } } From 4f0fe8723bd2d8c0b72a88cbc7dffa2f0f954dc7 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Mon, 18 Jun 2012 19:00:36 +0200 Subject: [PATCH 07/11] Using setRuntimeClass() avoids the need for the user bundle to import akka.osgi.blueprint --- .../main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala index 245ea538b6..b20e959f23 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala @@ -80,7 +80,7 @@ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { factory.setId(findAvailableId(context)) factory.setScope(BeanMetadata.SCOPE_SINGLETON) factory.setProcessor(true) - factory.setClassName(classOf[BlueprintActorSystemFactory].getName) + factory.setRuntimeClass(classOf[BlueprintActorSystemFactory]) factory.setDestroyMethod(DESTROY_METHOD_NAME) From 379c0e16b462bf6354a1b7c83512b9519ce758f1 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Tue, 26 Jun 2012 22:07:57 +0200 Subject: [PATCH 08/11] Move Aries Blueprint namespace handler into a separate akka-osgi-aries module --- .../blueprint/akka-namespacehandler.xml | 2 +- .../akka/osgi/aries/blueprint}/akka.xsd | 0 .../BlueprintActorSystemFactory.scala | 4 +--- .../aries/blueprint}/NamespaceHandler.scala | 7 +++--- .../osgi/aries/blueprint}/ParserHelper.scala | 2 +- .../akka/osgi/aries/blueprint}/config.xml | 0 .../akka/osgi/aries/blueprint}/injection.xml | 4 ++-- .../akka/osgi/aries/blueprint}/simple.xml | 0 .../blueprint}/ActorSystemAwareBean.scala | 2 +- .../blueprint}/NamespaceHandlerTest.scala | 3 +-- .../akka/osgi/ActorSystemActivator.scala | 1 - project/AkkaBuild.scala | 24 +++++++++++++++---- 12 files changed, 29 insertions(+), 20 deletions(-) rename {akka-osgi => akka-osgi-aries}/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml (87%) rename {akka-osgi/src/main/resources/akka/osgi/blueprint/aries => akka-osgi-aries/src/main/resources/akka/osgi/aries/blueprint}/akka.xsd (100%) rename {akka-osgi/src/main/scala/akka/osgi => akka-osgi-aries/src/main/scala/akka/osgi/aries}/blueprint/BlueprintActorSystemFactory.scala (91%) rename {akka-osgi/src/main/scala/akka/osgi/blueprint/aries => akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint}/NamespaceHandler.scala (96%) rename {akka-osgi/src/main/scala/akka/osgi/blueprint/aries => akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint}/ParserHelper.scala (92%) rename {akka-osgi/src/test/resources/akka/osgi/blueprint/aries => akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint}/config.xml (100%) rename {akka-osgi/src/test/resources/akka/osgi/blueprint/aries => akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint}/injection.xml (68%) rename {akka-osgi/src/test/resources/akka/osgi/blueprint/aries => akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint}/simple.xml (100%) rename {akka-osgi/src/test/scala/akka/osgi/test => akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint}/ActorSystemAwareBean.scala (86%) rename {akka-osgi/src/test/scala/akka/osgi/blueprint/aries => akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint}/NamespaceHandlerTest.scala (97%) diff --git a/akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml b/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml similarity index 87% rename from akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml rename to akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml index 650738b10a..8cc52e85f2 100644 --- a/akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml +++ b/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml @@ -9,7 +9,7 @@ - + diff --git a/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd b/akka-osgi-aries/src/main/resources/akka/osgi/aries/blueprint/akka.xsd similarity index 100% rename from akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd rename to akka-osgi-aries/src/main/resources/akka/osgi/aries/blueprint/akka.xsd diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala similarity index 91% rename from akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala rename to akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala index 51c3e7291f..876d0f0045 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala @@ -1,9 +1,7 @@ -package akka.osgi.blueprint +package akka.osgi.aries.blueprint import org.osgi.framework.BundleContext import akka.osgi.OsgiActorSystemFactory -import collection.mutable.Buffer -import akka.actor.{ Actor, Props, ActorSystem } import com.typesafe.config.ConfigFactory /** diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala similarity index 96% rename from akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala rename to akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala index b20e959f23..13b644010e 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala @@ -1,4 +1,4 @@ -package akka.osgi.blueprint.aries +package akka.osgi.aries.blueprint import org.apache.aries.blueprint.ParserContext import org.osgi.service.blueprint.container.ComponentDefinitionException @@ -7,10 +7,9 @@ import org.apache.aries.blueprint.mutable.MutableBeanMetadata import collection.JavaConversions.setAsJavaSet import org.osgi.framework.BundleContext import org.apache.aries.blueprint.reflect.{ ValueMetadataImpl, RefMetadataImpl, BeanArgumentImpl } -import org.w3c.dom.{ NodeList, Element, Node } +import org.w3c.dom.{ Element, Node } import org.osgi.service.blueprint.reflect.{ BeanMetadata, ComponentMetadata } -import akka.actor.{ ActorSystem } -import akka.osgi.blueprint.{ BlueprintActorSystemFactory } +import akka.actor.ActorSystem import java.util.concurrent.atomic.AtomicInteger import ParserHelper.childElements diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala similarity index 92% rename from akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala rename to akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala index 82fb7bc113..35bf72931a 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala @@ -1,4 +1,4 @@ -package akka.osgi.blueprint.aries +package akka.osgi.aries.blueprint import org.w3c.dom.{ Node, Element } diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml b/akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/config.xml similarity index 100% rename from akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml rename to akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/config.xml diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml b/akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/injection.xml similarity index 68% rename from akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml rename to akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/injection.xml index 9712ee6d1f..6fd21db5ef 100644 --- a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml +++ b/akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/injection.xml @@ -2,8 +2,8 @@ - - + + diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml b/akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/simple.xml similarity index 100% rename from akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml rename to akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/simple.xml diff --git a/akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/ActorSystemAwareBean.scala similarity index 86% rename from akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala rename to akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/ActorSystemAwareBean.scala index ca0df7cc04..6e4bac39dd 100644 --- a/akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala +++ b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/ActorSystemAwareBean.scala @@ -1,4 +1,4 @@ -package akka.osgi.test +package akka.osgi.aries.blueprint import akka.actor.ActorSystem diff --git a/akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala similarity index 97% rename from akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala rename to akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala index bbe26b5e92..44178978ec 100644 --- a/akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala +++ b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala @@ -1,11 +1,10 @@ -package akka.osgi.blueprint.aries +package akka.osgi.aries.blueprint import org.scalatest.FlatSpec import akka.actor.ActorSystem import de.kalpatec.pojosr.framework.launch.BundleDescriptor import akka.osgi.PojoSRTestSupport import akka.osgi.PojoSRTestSupport.bundle -import akka.osgi.test.ActorSystemAwareBean /** * Test cases for {@link ActorSystemActivator} diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index 546ff8c2c4..28912cf6b0 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -1,6 +1,5 @@ package akka.osgi -import com.typesafe.config.{ Config, ConfigFactory } import akka.actor.ActorSystem import java.util.{ Dictionary, Properties } import org.osgi.framework.{ ServiceRegistration, BundleContext, BundleActivator } diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index d9d5517703..f1d71729c2 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -226,6 +226,15 @@ object AkkaBuild extends Build { ) ) + lazy val osgiAries = Project( + id = "akka-osgi-aries", + base = file("akka-osgi-aries"), + dependencies = Seq(osgi % "compile;test->test"), + settings = defaultSettings ++ OSGi.osgiAries ++ Seq( + libraryDependencies ++= Dependencies.osgiAries + ) + ) + lazy val akkaSbtPlugin = Project( id = "akka-sbt-plugin", base = file("akka-sbt-plugin"), @@ -480,7 +489,9 @@ object Dependencies { val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito) - val osgi = Seq(osgiCore, ariesBlueprint, Test.logback, Test.ariesProxy, Test.commonsIo, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) + val osgi = Seq(osgiCore,Test.logback, Test.commonsIo, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) + + val osgiAries = Seq(osgiCore, ariesBlueprint, Test.ariesProxy) val tutorials = Seq(Test.scalatest, Test.junit) @@ -551,9 +562,12 @@ object OSGi { val mailboxesCommon = exports(Seq("akka.actor.mailbox.*")) - val osgi = exports(Seq("akka.osgi.*")) ++ Seq( - OsgiKeys.importPackage := Seq("org.apache.aries.blueprint.*;resolution:=optional", - "org.osgi.service.blueprint.*;resolution:=optional") ++ defaultImports + val osgi = exports(Seq("akka.osgi")) ++ Seq( + OsgiKeys.privatePackage := Seq("akka.osgi.impl") + ) + + val osgiAries = exports() ++ Seq( + OsgiKeys.privatePackage := Seq("akka.osgi.aries.*") ) val remote = exports(Seq("akka.remote.*", "akka.routing.*", "akka.serialization.*")) @@ -564,7 +578,7 @@ object OSGi { val zeroMQ = exports(Seq("akka.zeromq.*")) - def exports(packages: Seq[String]) = osgiSettings ++ Seq( + def exports(packages: Seq[String] = Seq()) = osgiSettings ++ Seq( OsgiKeys.importPackage := defaultImports, OsgiKeys.exportPackage := packages ) From 85cf1c0580aec43f112df8d8308119d4585b0c74 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Wed, 27 Jun 2012 14:01:09 +0200 Subject: [PATCH 09/11] Add types to API methods and clarify Scaladocs --- .../blueprint/akka-namespacehandler.xml | 3 ++ .../BlueprintActorSystemFactory.scala | 6 ++- .../aries/blueprint/NamespaceHandler.scala | 21 ++++++++- .../osgi/aries/blueprint/ParserHelper.scala | 5 ++- .../akka/osgi/ActorSystemActivator.scala | 45 +++++++++++++------ .../akka/osgi/OsgiActorSystemFactory.scala | 17 +++++-- .../impl/BundleDelegatingClassLoader.scala | 4 +- 7 files changed, 76 insertions(+), 25 deletions(-) diff --git a/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml b/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml index 8cc52e85f2..99492bedf2 100644 --- a/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml +++ b/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml @@ -1,6 +1,9 @@ + diff --git a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala index 876d0f0045..40c9d7367b 100644 --- a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala @@ -5,7 +5,11 @@ import akka.osgi.OsgiActorSystemFactory import com.typesafe.config.ConfigFactory /** - * A set of helper/factory classes to build a Akka system using Blueprint + * A set of helper/factory classes to build a Akka system using Blueprint. This class is only meant to be used by + * the [[akka.osgi.aries.blueprint.NamespaceHandler]] class, you should not use this class directly. + * + * If you're looking for a way to set up Akka using Blueprint without the namespace handler, you should use + * [[akka.osgi.OsgiActorSystemFactory]] instead. */ class BlueprintActorSystemFactory(context: BundleContext, name: String) extends OsgiActorSystemFactory(context) { diff --git a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala index 13b644010e..2ab1306a0f 100644 --- a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala @@ -15,7 +15,26 @@ import java.util.concurrent.atomic.AtomicInteger import ParserHelper.childElements /** - * Aries Blueprint namespace handler implementation + * Aries Blueprint namespace handler implementation. This namespace handler will allow users of Apache Aries' Blueprint + * implementation to define their Akka [[akka.actor.ActorSystem]] using a syntax like this: + * + * {{{ + * + * + * + * + * + * some.config { + * key=value + * } + * + * + * + * + * }}} + * + * Users of other IoC frameworks in an OSGi environment should use [[akka.osgi.OsgiActorSystemFactory]] instead. */ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { diff --git a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala index 35bf72931a..585037db09 100644 --- a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala @@ -7,9 +7,10 @@ import org.w3c.dom.{ Node, Element } */ object ParserHelper { - def childElements(element: Element) = children(element).filter(_.getNodeType == Node.ELEMENT_NODE).asInstanceOf[Seq[Element]] + def childElements(element: Element): Seq[Element] = + children(element).filter(_.getNodeType == Node.ELEMENT_NODE).asInstanceOf[Seq[Element]] - private[this] def children(element: Element) = { + private[this] def children(element: Element): Seq[Node] = { val nodelist = element.getChildNodes for (index ← 0 until nodelist.getLength) yield nodelist.item(index) } diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index 28912cf6b0..794eec0317 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -5,52 +5,69 @@ import java.util.{ Dictionary, Properties } import org.osgi.framework.{ ServiceRegistration, BundleContext, BundleActivator } /** - * Abstract {@link BundleActivator} implementation to bootstrap and configure an {@link ActorSystem} in an - * OSGi environment. + * Abstract bundle activator implementation to bootstrap and configure an actor system in an + * OSGi environment. It also provides a convenience method to register the actor system in + * the OSGi Service Registry for sharing it with other OSGi bundles. + * + * This convenience activator is mainly useful for setting up a single [[akka.actor.ActorSystem]] instance and sharing that + * with other bundles in the OSGi Framework. If you want to set up multiple systems in the same bundle context, look at + * the [[akka.osgi.OsgiActorSystemFactory]] instead. + * + * @param nameFor a function that allows you to determine the name of the [[akka.actor.ActorSystem]] at bundle startup time */ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) extends BundleActivator { + /** + * No-args constructor - a default name (`bundle--ActorSystem`) will be assigned to the [[akka.actor.ActorSystem]] + */ def this() = this({ context: BundleContext ⇒ None }) - def this(name: String) = this({ context: BundleContext ⇒ Some(name) }) - - var system: Option[ActorSystem] = None - var registration: Option[ServiceRegistration] = None /** - * Implement this method to add your own actors to the ActorSystem + * Create the activator, specifying the name of the [[akka.actor.ActorSystem]] to be created + */ + def this(name: String) = this({ context: BundleContext ⇒ Some(name) }) + + private var system: Option[ActorSystem] = None + private var registration: Option[ServiceRegistration] = None + + /** + * Implement this method to add your own actors to the ActorSystem. If you want to share the actor + * system with other bundles, call the `registerService(BundleContext, ActorSystem)` method from within + * this method. * * @param context the bundle context * @param system the ActorSystem that was created by the activator */ - def configure(context: BundleContext, system: ActorSystem) + def configure(context: BundleContext, system: ActorSystem): Unit /** - * Sets up a new ActorSystem and registers it in the OSGi Service Registry + * Sets up a new ActorSystem * * @param context the BundleContext */ - def start(context: BundleContext) { + def start(context: BundleContext): Unit = { system = Some(OsgiActorSystemFactory(context).createActorSystem(nameFor(context))) system.foreach(configure(context, _)) } /** - * Shuts down the ActorSystem when the bundle is stopped and, if necessary, unregisters a service registration + * Shuts down the ActorSystem when the bundle is stopped and, if necessary, unregisters a service registration. * * @param context the BundleContext */ - def stop(context: BundleContext) { + def stop(context: BundleContext): Unit = { registration.foreach(_.unregister()) system.foreach(_.shutdown()) } /** - * Register the actor system in the OSGi service registry + * Register the actor system in the OSGi service registry. The activator itself will ensure that this service + * is unregistered again when the bundle is being stopped. * * @param context the bundle context * @param system the actor system */ - def registerService(context: BundleContext, system: ActorSystem) { + def registerService(context: BundleContext, system: ActorSystem): Unit = { val properties = new Properties() properties.put("name", system.name) registration = Some(context.registerService(classOf[ActorSystem].getName, system, diff --git a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala index 2c5a6eca14..ae36406a60 100644 --- a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala +++ b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala @@ -14,13 +14,18 @@ class OsgiActorSystemFactory(val context: BundleContext) { /* * Classloader that delegates to the bundle for which the factory is creating an ActorSystem */ - val classloader = BundleDelegatingClassLoader.createFor(context) + private val classloader = BundleDelegatingClassLoader.createFor(context) /** - * Creates the ActorSystem and registers it in the OSGi Service Registry + * Creates the [[akka.actor.ActorSystem]], using the name specified */ def createActorSystem(name: String): ActorSystem = createActorSystem(Option(name)) + /** + * Creates the [[akka.actor.ActorSystem]], using the name specified. + * + * A default name (`bundle--ActorSystem`) is assigned when you pass along [[scala.None]] instead. + */ def createActorSystem(name: Option[String]): ActorSystem = ActorSystem(actorSystemName(name), actorSystemConfig(context), classloader) @@ -34,7 +39,8 @@ class OsgiActorSystemFactory(val context: BundleContext) { } /** - * Determine a the ActorSystem name + * Determine the name for the [[akka.actor.ActorSystem]] + * Returns a default value of `bundle--ActorSystem` is no name is being specified */ def actorSystemName(name: Option[String]): String = name.getOrElse("bundle-%s-ActorSystem".format(context.getBundle().getBundleId)) @@ -43,6 +49,9 @@ class OsgiActorSystemFactory(val context: BundleContext) { object OsgiActorSystemFactory { - def apply(context: BundleContext) = new OsgiActorSystemFactory(context) + /* + * Create an [[OsgiActorSystemFactory]] instance to set up Akka in an OSGi environment + */ + def apply(context: BundleContext): OsgiActorSystemFactory = new OsgiActorSystemFactory(context) } diff --git a/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala index 74592392d9..0231a77714 100644 --- a/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala +++ b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala @@ -63,11 +63,9 @@ class BundleDelegatingClassLoader(bundle: Bundle, classLoader: Option[ClassLoade case cnfe: ClassNotFoundException ⇒ rethrowClassNotFoundException(name, cnfe) } - def rethrowClassNotFoundException(name: String, cnfe: ClassNotFoundException): Nothing = + private def rethrowClassNotFoundException(name: String, cnfe: ClassNotFoundException): Nothing = throw new ClassNotFoundException(name + " from bundle " + bundle.getBundleId + " (" + bundle.getSymbolicName + ")", cnfe) - def getBundle: Bundle = bundle - override def toString = String.format("BundleDelegatingClassLoader(%s)", bundle) } From 88c1f69050952188dc9db9b79500c43525f164b9 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Fri, 29 Jun 2012 14:12:36 +0200 Subject: [PATCH 10/11] A few more code-style fixes --- .../main/scala/akka/osgi/ActorSystemActivator.scala | 10 +++++----- .../akka/osgi/impl/BundleDelegatingClassLoader.scala | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index 794eec0317..e92415e1e1 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -20,12 +20,12 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) /** * No-args constructor - a default name (`bundle--ActorSystem`) will be assigned to the [[akka.actor.ActorSystem]] */ - def this() = this({ context: BundleContext ⇒ None }) + def this() = this(context ⇒ None) /** * Create the activator, specifying the name of the [[akka.actor.ActorSystem]] to be created */ - def this(name: String) = this({ context: BundleContext ⇒ Some(name) }) + def this(name: String) = this(context ⇒ Some(name)) private var system: Option[ActorSystem] = None private var registration: Option[ServiceRegistration] = None @@ -47,7 +47,7 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) */ def start(context: BundleContext): Unit = { system = Some(OsgiActorSystemFactory(context).createActorSystem(nameFor(context))) - system.foreach(configure(context, _)) + system foreach (configure(context, _)) } /** @@ -56,8 +56,8 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) * @param context the BundleContext */ def stop(context: BundleContext): Unit = { - registration.foreach(_.unregister()) - system.foreach(_.shutdown()) + registration foreach (_.unregister()) + system foreach (_.shutdown()) } /** diff --git a/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala index 0231a77714..08dee0344e 100644 --- a/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala +++ b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala @@ -28,7 +28,7 @@ class BundleDelegatingClassLoader(bundle: Bundle, classLoader: Option[ClassLoade protected override def findClass(name: String): Class[_] = bundle.loadClass(name) protected override def findResource(name: String): URL = { - val resource: URL = bundle.getResource(name) + val resource = bundle.getResource(name) classLoader match { case Some(loader) if resource == null ⇒ loader.getResource(name) case _ ⇒ resource @@ -40,7 +40,7 @@ class BundleDelegatingClassLoader(bundle: Bundle, classLoader: Option[ClassLoade bundle.getResources(name).asInstanceOf[Enumeration[URL]] protected override def loadClass(name: String, resolve: Boolean): Class[_] = { - val clazz: Class[_] = try { + val clazz = try { findClass(name) } catch { case cnfe: ClassNotFoundException ⇒ { @@ -66,7 +66,7 @@ class BundleDelegatingClassLoader(bundle: Bundle, classLoader: Option[ClassLoade private def rethrowClassNotFoundException(name: String, cnfe: ClassNotFoundException): Nothing = throw new ClassNotFoundException(name + " from bundle " + bundle.getBundleId + " (" + bundle.getSymbolicName + ")", cnfe) - override def toString = String.format("BundleDelegatingClassLoader(%s)", bundle) + override def toString: String = String.format("BundleDelegatingClassLoader(%s)", bundle) } From 3204e8a426e7216828d4388d71130675b2861b32 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Sat, 30 Jun 2012 20:30:36 +0200 Subject: [PATCH 11/11] A few more code-style improvements and an easier API for the activator --- .../aries/blueprint/NamespaceHandler.scala | 10 +-- .../blueprint/NamespaceHandlerTest.scala | 78 ++++++++++--------- .../akka/osgi/ActorSystemActivator.scala | 25 +++--- .../akka/osgi/ActorSystemActivatorTest.scala | 70 ++++++++++++----- .../scala/akka/osgi/PojoSRTestSupport.scala | 56 +++++++------ .../scala/akka/osgi/test/TestActivators.scala | 39 ++++++++++ .../osgi/test/TestActorSystemActivator.scala | 18 ----- 7 files changed, 181 insertions(+), 115 deletions(-) create mode 100644 akka-osgi/src/test/scala/akka/osgi/test/TestActivators.scala delete mode 100644 akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala diff --git a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala index 2ab1306a0f..0570a027b6 100644 --- a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala @@ -52,7 +52,7 @@ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { } def decorate(node: Node, component: ComponentMetadata, context: ParserContext) = - throw new ComponentDefinitionException("Bad xml syntax: node decoration is not supported"); + throw new ComponentDefinitionException("Bad xml syntax: node decoration is not supported") /* * Parse @@ -114,9 +114,9 @@ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { */ def getId(context: ParserContext, element: Element) = { if (element.hasAttribute(ID_ATTRIBUTE)) { - element.getAttribute(ID_ATTRIBUTE); + element.getAttribute(ID_ATTRIBUTE) } else { - findAvailableId(context); + findAvailableId(context) } } @@ -134,8 +134,8 @@ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { object NamespaceHandler { - private val ID_ATTRIBUTE = "id"; - private val NAME_ATTRIBUTE = "name"; + private val ID_ATTRIBUTE = "id" + private val NAME_ATTRIBUTE = "name" private val BUNDLE_CONTEXT_REFID = "blueprintBundleContext" diff --git a/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala index 44178978ec..4f4eb647e0 100644 --- a/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala +++ b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala @@ -1,10 +1,11 @@ package akka.osgi.aries.blueprint -import org.scalatest.FlatSpec +import org.scalatest.WordSpec import akka.actor.ActorSystem import de.kalpatec.pojosr.framework.launch.BundleDescriptor import akka.osgi.PojoSRTestSupport import akka.osgi.PojoSRTestSupport.bundle +import org.scalatest.matchers.MustMatchers /** * Test cases for {@link ActorSystemActivator} @@ -24,70 +25,77 @@ object NamespaceHandlerTest { } -class SimpleNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { +class SimpleNamespaceHandlerTest extends WordSpec with MustMatchers with PojoSRTestSupport { import NamespaceHandlerTest._ - val testBundles: Seq[BundleDescriptor] = Seq( + val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq( AKKA_OSGI_BLUEPRINT, - bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("simple.xml"))) + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("simple.xml")))) - "simple.xml" should "set up ActorSystem when bundle starts" in { - val system = serviceForType[ActorSystem] - assert(system != null) - } + "simple.xml" must { + "set up ActorSystem when bundle starts" in { + val system = serviceForType[ActorSystem] + assert(system != null) + } - it should "stop the ActorSystem when bundle stops" in { - val system = serviceForType[ActorSystem] - assert(!system.isTerminated) + "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] + assert(!system.isTerminated) - bundleForName(TEST_BUNDLE_NAME).stop() + bundleForName(TEST_BUNDLE_NAME).stop() - system.awaitTermination() - assert(system.isTerminated) + system.awaitTermination() + assert(system.isTerminated) + } } } -class ConfigNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { +class ConfigNamespaceHandlerTest extends WordSpec with MustMatchers with PojoSRTestSupport { import NamespaceHandlerTest._ - val testBundles: Seq[BundleDescriptor] = Seq( + val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq( AKKA_OSGI_BLUEPRINT, - bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("config.xml"))) + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("config.xml")))) - "config.xml" should "set up ActorSystem when bundle starts" in { - val system = serviceForType[ActorSystem] - assert(system != null) + "config.xml" must { + "set up ActorSystem when bundle starts" in { + val system = serviceForType[ActorSystem] + assert(system != null) - assert(system.settings.config.getString("some.config.key") == "value") - } + assert(system.settings.config.getString("some.config.key") == "value") + } - it should "stop the ActorSystem when bundle stops" in { - val system = serviceForType[ActorSystem] - assert(!system.isTerminated) + "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] + assert(!system.isTerminated) - bundleForName(TEST_BUNDLE_NAME).stop() + bundleForName(TEST_BUNDLE_NAME).stop() - system.awaitTermination() - assert(system.isTerminated) + system.awaitTermination() + assert(system.isTerminated) + } } } -class DependencyInjectionNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { +class DependencyInjectionNamespaceHandlerTest extends WordSpec with MustMatchers with PojoSRTestSupport { import NamespaceHandlerTest._ - val testBundles: Seq[BundleDescriptor] = Seq( + val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq( AKKA_OSGI_BLUEPRINT, - bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("injection.xml"))) + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("injection.xml")))) - "injection.xml" should "set up bean containing ActorSystem" in { - val bean = serviceForType[ActorSystemAwareBean] - assert(bean != null) - assert(bean.system != null) + "injection.xml" must { + + "set up bean containing ActorSystem" in { + val bean = serviceForType[ActorSystemAwareBean] + assert(bean != null) + assert(bean.system != null) + } } } diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index e92415e1e1..e279247dbc 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -12,20 +12,8 @@ import org.osgi.framework.{ ServiceRegistration, BundleContext, BundleActivator * This convenience activator is mainly useful for setting up a single [[akka.actor.ActorSystem]] instance and sharing that * with other bundles in the OSGi Framework. If you want to set up multiple systems in the same bundle context, look at * the [[akka.osgi.OsgiActorSystemFactory]] instead. - * - * @param nameFor a function that allows you to determine the name of the [[akka.actor.ActorSystem]] at bundle startup time */ -abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) extends BundleActivator { - - /** - * No-args constructor - a default name (`bundle--ActorSystem`) will be assigned to the [[akka.actor.ActorSystem]] - */ - def this() = this(context ⇒ None) - - /** - * Create the activator, specifying the name of the [[akka.actor.ActorSystem]] to be created - */ - def this(name: String) = this(context ⇒ Some(name)) +abstract class ActorSystemActivator extends BundleActivator { private var system: Option[ActorSystem] = None private var registration: Option[ServiceRegistration] = None @@ -46,7 +34,7 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) * @param context the BundleContext */ def start(context: BundleContext): Unit = { - system = Some(OsgiActorSystemFactory(context).createActorSystem(nameFor(context))) + system = Some(OsgiActorSystemFactory(context).createActorSystem(Option(getActorSystemName(context)))) system foreach (configure(context, _)) } @@ -74,4 +62,13 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) properties.asInstanceOf[Dictionary[String, Any]])) } + /** + * By default, the [[akka.actor.ActorSystem]] name will be set to `bundle--ActorSystem`. Override this + * method to define another name for your [[akka.actor.ActorSystem]] instance. + * + * @param context the bundle context + * @return the actor system name + */ + def getActorSystemName(context: BundleContext): String = null + } diff --git a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala index 0b2fdd19ac..37002975e4 100644 --- a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala +++ b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala @@ -1,44 +1,74 @@ package akka.osgi -import org.scalatest.FlatSpec +import org.scalatest.WordSpec import akka.actor.ActorSystem import akka.pattern.ask import akka.dispatch.Await import akka.util.duration._ import akka.util.Timeout import de.kalpatec.pojosr.framework.launch.BundleDescriptor -import test.TestActorSystemActivator +import test.{ RuntimeNameActorSystemActivator, TestActivators, PingPongActorSystemActivator } import test.PingPong._ import PojoSRTestSupport.bundle +import org.scalatest.matchers.MustMatchers /** - * Test cases for {@link ActorSystemActivator} + * Test cases for [[akka.osgi.ActorSystemActivator]] in 2 different scenarios: + * - no name configured for [[akka.actor.ActorSystem]] + * - runtime name configuration */ -class ActorSystemActivatorTest extends FlatSpec with PojoSRTestSupport { +object ActorSystemActivatorTest { val TEST_BUNDLE_NAME = "akka.osgi.test.activator" - val testBundles: Seq[BundleDescriptor] = Seq( - bundle(TEST_BUNDLE_NAME).withActivator(classOf[TestActorSystemActivator])) +} - "ActorSystemActivator" should "start and register the ActorSystem when bundle starts" in { - val system = serviceForType[ActorSystem] - val actor = system.actorFor("/user/pong") +class PingPongActorSystemActivatorTest extends WordSpec with MustMatchers with PojoSRTestSupport { - implicit val timeout = Timeout(5 seconds) - val future = actor ? Ping - val result = Await.result(future, timeout.duration) - assert(result != null) - } + import ActorSystemActivatorTest._ - it should "stop the ActorSystem when bundle stops" in { - val system = serviceForType[ActorSystem] - assert(!system.isTerminated) + val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq( + bundle(TEST_BUNDLE_NAME).withActivator(classOf[PingPongActorSystemActivator]))) - bundleForName(TEST_BUNDLE_NAME).stop() + "PingPongActorSystemActivator" must { - system.awaitTermination() - assert(system.isTerminated) + "start and register the ActorSystem when bundle starts" in { + val system = serviceForType[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) + } + + "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] + assert(!system.isTerminated) + + bundleForName(TEST_BUNDLE_NAME).stop() + + system.awaitTermination() + assert(system.isTerminated) + } } } + +class RuntimeNameActorSystemActivatorTest extends WordSpec with MustMatchers with PojoSRTestSupport { + + import ActorSystemActivatorTest._ + + val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq( + bundle(TEST_BUNDLE_NAME).withActivator(classOf[RuntimeNameActorSystemActivator]))) + + "RuntimeNameActorSystemActivator" must { + + "register an ActorSystem and add the bundle id to the system name" in { + val system = serviceForType[ActorSystem] + val bundle = bundleForName(TEST_BUNDLE_NAME) + system.name must equal(TestActivators.ACTOR_SYSTEM_NAME_PATTERN.format(bundle.getBundleId)) + } + } + +} \ No newline at end of file diff --git a/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala b/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala index cbed282c18..3ba9068907 100644 --- a/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala +++ b/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala @@ -19,10 +19,8 @@ import org.scalatest.{ BeforeAndAfterAll, Suite } */ trait PojoSRTestSupport extends Suite with BeforeAndAfterAll { - val MAX_WAIT_TIME = 8000; - val START_WAIT_TIME = 100; - - implicit def buildBundleDescriptor(builder: BundleDescriptorBuilder) = builder.build + val MAX_WAIT_TIME = 12800 + val START_WAIT_TIME = 50 /** * All bundles being found on the test classpath are automatically installed and started in the PojoSR runtime. @@ -31,12 +29,12 @@ trait PojoSRTestSupport extends Suite with BeforeAndAfterAll { val testBundles: Seq[BundleDescriptor] lazy val context: BundleContext = { - val config = new HashMap[String, AnyRef](); + val config = new HashMap[String, AnyRef]() System.setProperty("org.osgi.framework.storage", "target/akka-osgi/" + System.currentTimeMillis) val bundles = new ClasspathScanner().scanForBundles() bundles.addAll(testBundles) - config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, bundles); + config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, bundles) val loader: ServiceLoader[PojoServiceRegistryFactory] = ServiceLoader.load(classOf[PojoServiceRegistryFactory]) @@ -68,15 +66,15 @@ trait PojoSRTestSupport extends Suite with BeforeAndAfterAll { def awaitReference(serviceType: Class[_], wait: Long): ServiceReference = { val option = Option(context.getServiceReference(serviceType.getName)) + Thread.sleep(wait) option match { - case Some(reference) ⇒ reference; + case Some(reference) ⇒ reference case None if (wait > MAX_WAIT_TIME) ⇒ fail("Gave up waiting for service of type %s".format(serviceType)) - case None ⇒ { - Thread.sleep(wait); - awaitReference(serviceType, wait * 2); - } + case None ⇒ awaitReference(serviceType, wait * 2) } } + + protected def buildTestBundles(builders: Seq[BundleDescriptorBuilder]): Seq[BundleDescriptor] = builders map (_.build) } object PojoSRTestSupport { @@ -97,35 +95,47 @@ class BundleDescriptorBuilder(name: String) { val tinybundle = TinyBundles.bundle.set(Constants.BUNDLE_SYMBOLICNAME, name) - def withBlueprintFile(name: String, contents: URL) = + /** + * Add a Blueprint XML file to our test bundle + */ + def withBlueprintFile(name: String, contents: URL): BundleDescriptorBuilder = returnBuilder(tinybundle.add("OSGI-INF/blueprint/%s".format(name), contents)) + /** + * Add a Blueprint XML file to our test bundle + */ def withBlueprintFile(contents: URL): BundleDescriptorBuilder = withBlueprintFile(filename(contents), contents) - def withActivator(activator: Class[_ <: BundleActivator]) = + /** + * Add a Bundle activator to our test bundle + */ + def withActivator(activator: Class[_ <: BundleActivator]): BundleDescriptorBuilder = returnBuilder(tinybundle.set(Constants.BUNDLE_ACTIVATOR, activator.getName)) - def returnBuilder(block: ⇒ Unit) = { + private def returnBuilder(block: ⇒ Unit) = { block this } - def build = { + /** + * Build the actual PojoSR BundleDescriptor instance + */ + def build: BundleDescriptor = { val file: File = tinybundleToJarFile(name) new BundleDescriptor( getClass().getClassLoader(), new URL("jar:" + file.toURI().toString() + "!/"), - extractHeaders(file)); + extractHeaders(file)) } def extractHeaders(file: File): HashMap[String, String] = { - val headers = new HashMap[String, String](); + val headers = new HashMap[String, String]() - val jis = new JarInputStream(new FileInputStream(file)); + val jis = new JarInputStream(new FileInputStream(file)) try { for (entry ← jis.getManifest().getMainAttributes().entrySet()) { - headers.put(entry.getKey().toString(), entry.getValue().toString()); + headers.put(entry.getKey().toString(), entry.getValue().toString()) } } finally { jis.close() @@ -135,12 +145,12 @@ class BundleDescriptorBuilder(name: String) { } def tinybundleToJarFile(name: String): File = { - val file = new File("target/%s-%tQ.jar".format(name, new Date())); - val fos = new FileOutputStream(file); + val file = new File("target/%s-%tQ.jar".format(name, new Date())) + val fos = new FileOutputStream(file) try { - copy(tinybundle.build(), fos); + copy(tinybundle.build(), fos) } finally { - fos.close(); + fos.close() } file } diff --git a/akka-osgi/src/test/scala/akka/osgi/test/TestActivators.scala b/akka-osgi/src/test/scala/akka/osgi/test/TestActivators.scala new file mode 100644 index 0000000000..54369d88ca --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/test/TestActivators.scala @@ -0,0 +1,39 @@ +package akka.osgi.test + +import akka.osgi.ActorSystemActivator +import akka.actor.{ Props, ActorSystem } +import PingPong._ +import org.osgi.framework.BundleContext + +/** + * A set of [[akka.osgi.ActorSystemActivator]]s for testing purposes + */ +object TestActivators { + + val ACTOR_SYSTEM_NAME_PATTERN = "actor-system-for-bundle-%s" + +} + +/** + * Simple ActorSystemActivator that starts the sample ping-pong application + */ +class PingPongActorSystemActivator extends ActorSystemActivator { + + def configure(context: BundleContext, system: ActorSystem) { + system.actorOf(Props(new PongActor), name = "pong") + registerService(context, system) + } + +} + +/** + * [[akka.osgi.ActorSystemActivator]] implementation that determines [[akka.actor.ActorSystem]] name at runtime + */ +class RuntimeNameActorSystemActivator extends ActorSystemActivator { + + def configure(context: BundleContext, system: ActorSystem) = registerService(context, system); + + override def getActorSystemName(context: BundleContext) = + TestActivators.ACTOR_SYSTEM_NAME_PATTERN.format(context.getBundle.getBundleId) + +} \ No newline at end of file diff --git a/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala b/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala deleted file mode 100644 index 90305bc663..0000000000 --- a/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala +++ /dev/null @@ -1,18 +0,0 @@ -package akka.osgi.test - -import akka.osgi.ActorSystemActivator -import akka.actor.{ Props, ActorSystem } -import PingPong._ -import org.osgi.framework.BundleContext - -/** - * Sample ActorSystemActivator implementation used for testing purposes - */ -class TestActorSystemActivator extends ActorSystemActivator { - - def configure(context: BundleContext, system: ActorSystem) { - system.actorOf(Props(new PongActor), name = "pong") - registerService(context, system) - } - -}