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)