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)