Merge pull request #500 from gertv/osgi
Proposal for an akka-osgi module
This commit is contained in:
commit
34b05e554b
22 changed files with 1030 additions and 3 deletions
19
akka-docs/additional/code/osgi/Activator.scala
Normal file
19
akka-docs/additional/code/osgi/Activator.scala
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
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(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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//#Activator
|
||||||
14
akka-docs/additional/code/osgi/blueprint.xml
Normal file
14
akka-docs/additional/code/osgi/blueprint.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||||
|
xmlns:akka="http://akka.io/xmlns/blueprint/v1.0.0">
|
||||||
|
|
||||||
|
<akka:actor-system name="BlueprintSystem" />
|
||||||
|
|
||||||
|
<akka:actor-system name="BlueprintSystemWithConfig">
|
||||||
|
<akka:config>
|
||||||
|
some.config {
|
||||||
|
key=value
|
||||||
|
}
|
||||||
|
</akka:config>
|
||||||
|
</akka:actor-system>
|
||||||
|
</blueprint>
|
||||||
|
|
@ -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
|
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.
|
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
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Register the namespace handler in the OSGi Service Registry
|
||||||
|
-->
|
||||||
|
<service interface="org.apache.aries.blueprint.NamespaceHandler">
|
||||||
|
<service-properties>
|
||||||
|
<entry key="osgi.service.blueprint.namespace">
|
||||||
|
<array>
|
||||||
|
<value>http://akka.io/xmlns/blueprint/v1.0.0</value>
|
||||||
|
</array>
|
||||||
|
</entry>
|
||||||
|
</service-properties>
|
||||||
|
<bean class="akka.osgi.aries.blueprint.NamespaceHandler"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
</blueprint>
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xsd:schema xmlns="http://akka.io/xmlns/blueprint/v1.0.0"
|
||||||
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||||
|
targetNamespace="http://akka.io/xmlns/blueprint/v1.0.0"
|
||||||
|
elementFormDefault="qualified"
|
||||||
|
attributeFormDefault="unqualified">
|
||||||
|
|
||||||
|
<xsd:import namespace="http://www.osgi.org/xmlns/blueprint/v1.0.0"/>
|
||||||
|
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation>
|
||||||
|
Defines the configuration elements for setting up Akka with Blueprint
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
|
||||||
|
<xsd:element name="actor-system">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation>
|
||||||
|
Defines an Akka ActorSystem
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xsd:element ref="config" />
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="id" type="xsd:ID"/>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="optional"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
|
<xsd:element name="config" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation>
|
||||||
|
Defines an Akka ActorSystem configuration
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
|
</xsd:schema>
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package akka.osgi.aries.blueprint
|
||||||
|
|
||||||
|
import org.osgi.framework.BundleContext
|
||||||
|
import akka.osgi.OsgiActorSystemFactory
|
||||||
|
import com.typesafe.config.ConfigFactory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
|
||||||
|
var config: Option[String] = None
|
||||||
|
|
||||||
|
lazy val system = super.createActorSystem(stringToOption(name))
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
package akka.osgi.aries.blueprint
|
||||||
|
|
||||||
|
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.{ Element, Node }
|
||||||
|
import org.osgi.service.blueprint.reflect.{ BeanMetadata, ComponentMetadata }
|
||||||
|
import akka.actor.ActorSystem
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
import ParserHelper.childElements
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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:
|
||||||
|
*
|
||||||
|
* {{{
|
||||||
|
* <?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
* <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||||
|
* xmlns:akka="http://akka.io/xmlns/blueprint/v1.0.0">
|
||||||
|
*
|
||||||
|
* <akka:actor-system name="config">
|
||||||
|
* <akka:config>
|
||||||
|
* some.config {
|
||||||
|
* key=value
|
||||||
|
* }
|
||||||
|
* </akka:config>
|
||||||
|
* </akka:actor-system>
|
||||||
|
*
|
||||||
|
* </blueprint>
|
||||||
|
* }}}
|
||||||
|
*
|
||||||
|
* Users of other IoC frameworks in an OSGi environment should use [[akka.osgi.OsgiActorSystemFactory]] instead.
|
||||||
|
*/
|
||||||
|
class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler {
|
||||||
|
|
||||||
|
import NamespaceHandler._
|
||||||
|
|
||||||
|
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) = 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 <akka:actor-system/>
|
||||||
|
*/
|
||||||
|
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 <akka:config/>
|
||||||
|
*/
|
||||||
|
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.setRuntimeClass(classOf[BlueprintActorSystemFactory])
|
||||||
|
|
||||||
|
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 {
|
||||||
|
findAvailableId(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package akka.osgi.aries.blueprint
|
||||||
|
|
||||||
|
import org.w3c.dom.{ Node, Element }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to deal with the W3C DOM types
|
||||||
|
*/
|
||||||
|
object ParserHelper {
|
||||||
|
|
||||||
|
def childElements(element: Element): Seq[Element] =
|
||||||
|
children(element).filter(_.getNodeType == Node.ELEMENT_NODE).asInstanceOf[Seq[Element]]
|
||||||
|
|
||||||
|
private[this] def children(element: Element): Seq[Node] = {
|
||||||
|
val nodelist = element.getChildNodes
|
||||||
|
for (index ← 0 until nodelist.getLength) yield nodelist.item(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||||
|
xmlns:akka="http://akka.io/xmlns/blueprint/v1.0.0">
|
||||||
|
|
||||||
|
<service ref="system" interface="akka.actor.ActorSystem" />
|
||||||
|
|
||||||
|
<akka:actor-system id="system" name="config">
|
||||||
|
<akka:config>
|
||||||
|
some.config {
|
||||||
|
key=value
|
||||||
|
}
|
||||||
|
</akka:config>
|
||||||
|
</akka:actor-system>
|
||||||
|
|
||||||
|
</blueprint>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||||
|
xmlns:akka="http://akka.io/xmlns/blueprint/v1.0.0">
|
||||||
|
|
||||||
|
<service interface="akka.osgi.aries.blueprint.ActorSystemAwareBean">
|
||||||
|
<bean class="akka.osgi.aries.blueprint.ActorSystemAwareBean">
|
||||||
|
<argument>
|
||||||
|
<akka:actor-system name="simple" />
|
||||||
|
</argument>
|
||||||
|
</bean>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
</blueprint>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||||
|
xmlns:akka="http://akka.io/xmlns/blueprint/v1.0.0">
|
||||||
|
|
||||||
|
<service ref="system" interface="akka.actor.ActorSystem" />
|
||||||
|
|
||||||
|
<akka:actor-system id="system" name="simple" />
|
||||||
|
|
||||||
|
</blueprint>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package akka.osgi.aries.blueprint
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
package akka.osgi.aries.blueprint
|
||||||
|
|
||||||
|
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}
|
||||||
|
*/
|
||||||
|
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 WordSpec with MustMatchers with PojoSRTestSupport {
|
||||||
|
|
||||||
|
import NamespaceHandlerTest._
|
||||||
|
|
||||||
|
val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq(
|
||||||
|
AKKA_OSGI_BLUEPRINT,
|
||||||
|
bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("simple.xml"))))
|
||||||
|
|
||||||
|
"simple.xml" must {
|
||||||
|
"set up ActorSystem when bundle starts" in {
|
||||||
|
val system = serviceForType[ActorSystem]
|
||||||
|
assert(system != 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 ConfigNamespaceHandlerTest extends WordSpec with MustMatchers with PojoSRTestSupport {
|
||||||
|
|
||||||
|
import NamespaceHandlerTest._
|
||||||
|
|
||||||
|
val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq(
|
||||||
|
AKKA_OSGI_BLUEPRINT,
|
||||||
|
bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("config.xml"))))
|
||||||
|
|
||||||
|
"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")
|
||||||
|
}
|
||||||
|
|
||||||
|
"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 WordSpec with MustMatchers with PojoSRTestSupport {
|
||||||
|
|
||||||
|
import NamespaceHandlerTest._
|
||||||
|
|
||||||
|
val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq(
|
||||||
|
AKKA_OSGI_BLUEPRINT,
|
||||||
|
bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("injection.xml"))))
|
||||||
|
|
||||||
|
"injection.xml" must {
|
||||||
|
|
||||||
|
"set up bean containing ActorSystem" in {
|
||||||
|
val bean = serviceForType[ActorSystemAwareBean]
|
||||||
|
assert(bean != null)
|
||||||
|
assert(bean.system != null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
package akka.osgi
|
||||||
|
|
||||||
|
import akka.actor.ActorSystem
|
||||||
|
import java.util.{ Dictionary, Properties }
|
||||||
|
import org.osgi.framework.{ ServiceRegistration, BundleContext, BundleActivator }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
abstract class ActorSystemActivator extends BundleActivator {
|
||||||
|
|
||||||
|
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): Unit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up a new ActorSystem
|
||||||
|
*
|
||||||
|
* @param context the BundleContext
|
||||||
|
*/
|
||||||
|
def start(context: BundleContext): Unit = {
|
||||||
|
system = Some(OsgiActorSystemFactory(context).createActorSystem(Option(getActorSystemName(context))))
|
||||||
|
system foreach (configure(context, _))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuts down the ActorSystem when the bundle is stopped and, if necessary, unregisters a service registration.
|
||||||
|
*
|
||||||
|
* @param context the BundleContext
|
||||||
|
*/
|
||||||
|
def stop(context: BundleContext): Unit = {
|
||||||
|
registration foreach (_.unregister())
|
||||||
|
system foreach (_.shutdown())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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): Unit = {
|
||||||
|
val properties = new Properties()
|
||||||
|
properties.put("name", system.name)
|
||||||
|
registration = Some(context.registerService(classOf[ActorSystem].getName, system,
|
||||||
|
properties.asInstanceOf[Dictionary[String, Any]]))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, the [[akka.actor.ActorSystem]] name will be set to `bundle-<bundle id>-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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package akka.osgi
|
||||||
|
|
||||||
|
import impl.BundleDelegatingClassLoader
|
||||||
|
import akka.actor.ActorSystem
|
||||||
|
import com.typesafe.config.{ ConfigFactory, Config }
|
||||||
|
import org.osgi.framework.BundleContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
private val classloader = BundleDelegatingClassLoader.createFor(context)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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-<bundle id>-ActorSystem`) is assigned when you pass along [[scala.None]] instead.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
* loaded from the akka-actor bundle.
|
||||||
|
*/
|
||||||
|
def actorSystemConfig(context: BundleContext): Config = {
|
||||||
|
val reference = ConfigFactory.defaultReference(classOf[ActorSystem].getClassLoader)
|
||||||
|
ConfigFactory.load(classloader).withFallback(reference)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the name for the [[akka.actor.ActorSystem]]
|
||||||
|
* Returns a default value of `bundle-<bundle id>-ActorSystem` is no name is being specified
|
||||||
|
*/
|
||||||
|
def actorSystemName(name: Option[String]): String =
|
||||||
|
name.getOrElse("bundle-%s-ActorSystem".format(context.getBundle().getBundleId))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object OsgiActorSystemFactory {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create an [[OsgiActorSystemFactory]] instance to set up Akka in an OSGi environment
|
||||||
|
*/
|
||||||
|
def apply(context: BundleContext): OsgiActorSystemFactory = new OsgiActorSystemFactory(context)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
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 = 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 = 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def rethrowClassNotFoundException(name: String, cnfe: ClassNotFoundException): Nothing =
|
||||||
|
throw new ClassNotFoundException(name + " from bundle " + bundle.getBundleId + " (" + bundle.getSymbolicName + ")", cnfe)
|
||||||
|
|
||||||
|
override def toString: String = String.format("BundleDelegatingClassLoader(%s)", bundle)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
23
akka-osgi/src/test/resources/logback-test.xml
Normal file
23
akka-osgi/src/test/resources/logback-test.xml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<configuration>
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
||||||
|
<file>target/akka-osgi.log</file>
|
||||||
|
<append>true</append>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="debug">
|
||||||
|
<appender-ref ref="FILE" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
package akka.osgi
|
||||||
|
|
||||||
|
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.{ RuntimeNameActorSystemActivator, TestActivators, PingPongActorSystemActivator }
|
||||||
|
import test.PingPong._
|
||||||
|
import PojoSRTestSupport.bundle
|
||||||
|
import org.scalatest.matchers.MustMatchers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test cases for [[akka.osgi.ActorSystemActivator]] in 2 different scenarios:
|
||||||
|
* - no name configured for [[akka.actor.ActorSystem]]
|
||||||
|
* - runtime name configuration
|
||||||
|
*/
|
||||||
|
object ActorSystemActivatorTest {
|
||||||
|
|
||||||
|
val TEST_BUNDLE_NAME = "akka.osgi.test.activator"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class PingPongActorSystemActivatorTest extends WordSpec with MustMatchers with PojoSRTestSupport {
|
||||||
|
|
||||||
|
import ActorSystemActivatorTest._
|
||||||
|
|
||||||
|
val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq(
|
||||||
|
bundle(TEST_BUNDLE_NAME).withActivator(classOf[PingPongActorSystemActivator])))
|
||||||
|
|
||||||
|
"PingPongActorSystemActivator" must {
|
||||||
|
|
||||||
|
"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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
160
akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala
Normal file
160
akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
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 = 12800
|
||||||
|
val START_WAIT_TIME = 50
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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))
|
||||||
|
Thread.sleep(wait)
|
||||||
|
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 ⇒ awaitReference(serviceType, wait * 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def buildTestBundles(builders: Seq[BundleDescriptorBuilder]): Seq[BundleDescriptor] = builders map (_.build)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a Bundle activator to our test bundle
|
||||||
|
*/
|
||||||
|
def withActivator(activator: Class[_ <: BundleActivator]): BundleDescriptorBuilder =
|
||||||
|
returnBuilder(tinybundle.set(Constants.BUNDLE_ACTIVATOR, activator.getName))
|
||||||
|
|
||||||
|
private def returnBuilder(block: ⇒ Unit) = {
|
||||||
|
block
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
22
akka-osgi/src/test/scala/akka/osgi/test/PingPong.scala
Normal file
22
akka-osgi/src/test/scala/akka/osgi/test/PingPong.scala
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
39
akka-osgi/src/test/scala/akka/osgi/test/TestActivators.scala
Normal file
39
akka-osgi/src/test/scala/akka/osgi/test/TestActivators.scala
Normal file
|
|
@ -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)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -217,6 +217,24 @@ 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 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(
|
lazy val akkaSbtPlugin = Project(
|
||||||
id = "akka-sbt-plugin",
|
id = "akka-sbt-plugin",
|
||||||
base = file("akka-sbt-plugin"),
|
base = file("akka-sbt-plugin"),
|
||||||
|
|
@ -469,6 +487,10 @@ object Dependencies {
|
||||||
|
|
||||||
val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito)
|
val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito)
|
||||||
|
|
||||||
|
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)
|
val tutorials = Seq(Test.scalatest, Test.junit)
|
||||||
|
|
||||||
val docs = Seq(Test.scalatest, Test.junit, Test.specs2)
|
val docs = Seq(Test.scalatest, Test.junit, Test.specs2)
|
||||||
|
|
@ -484,6 +506,7 @@ object Dependency {
|
||||||
val Camel = "2.8.0"
|
val Camel = "2.8.0"
|
||||||
val Logback = "1.0.4"
|
val Logback = "1.0.4"
|
||||||
val Netty = "3.5.1.Final"
|
val Netty = "3.5.1.Final"
|
||||||
|
val OSGi = "4.2.0"
|
||||||
val Protobuf = "2.4.1"
|
val Protobuf = "2.4.1"
|
||||||
val ScalaStm = "0.5"
|
val ScalaStm = "0.5"
|
||||||
val Scalatest = "1.6.1"
|
val Scalatest = "1.6.1"
|
||||||
|
|
@ -492,9 +515,11 @@ object Dependency {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile
|
// Compile
|
||||||
|
val ariesBlueprint = "org.apache.aries.blueprint" % "org.apache.aries.blueprint" % "0.3.2" // ApacheV2
|
||||||
val config = "com.typesafe" % "config" % "0.4.1" // ApacheV2
|
val config = "com.typesafe" % "config" % "0.4.1" // ApacheV2
|
||||||
val camelCore = "org.apache.camel" % "camel-core" % V.Camel // ApacheV2
|
val camelCore = "org.apache.camel" % "camel-core" % V.Camel // ApacheV2
|
||||||
val netty = "io.netty" % "netty" % V.Netty // 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 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 scalaStm = "org.scala-tools" % "scala-stm_2.9.1" % V.ScalaStm // Modified BSD (Scala)
|
||||||
val slf4jApi = "org.slf4j" % "slf4j-api" % V.Slf4j // MIT
|
val slf4jApi = "org.slf4j" % "slf4j-api" % V.Slf4j // MIT
|
||||||
|
|
@ -504,14 +529,18 @@ object Dependency {
|
||||||
// Test
|
// Test
|
||||||
|
|
||||||
object 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 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 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 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 mockito = "org.mockito" % "mockito-all" % "1.8.1" % "test" // MIT
|
||||||
|
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 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 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 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 log4j = "log4j" % "log4j" % "1.2.14" % "test" // ApacheV2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -531,6 +560,14 @@ object OSGi {
|
||||||
|
|
||||||
val mailboxesCommon = exports(Seq("akka.actor.mailbox.*"))
|
val mailboxesCommon = exports(Seq("akka.actor.mailbox.*"))
|
||||||
|
|
||||||
|
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.*"))
|
val remote = exports(Seq("akka.remote.*", "akka.routing.*", "akka.serialization.*"))
|
||||||
|
|
||||||
val slf4j = exports(Seq("akka.event.slf4j.*"))
|
val slf4j = exports(Seq("akka.event.slf4j.*"))
|
||||||
|
|
@ -539,11 +576,12 @@ object OSGi {
|
||||||
|
|
||||||
val zeroMQ = exports(Seq("akka.zeromq.*"))
|
val zeroMQ = exports(Seq("akka.zeromq.*"))
|
||||||
|
|
||||||
def exports(packages: Seq[String]) = osgiSettings ++ Seq(
|
def exports(packages: Seq[String] = Seq()) = osgiSettings ++ Seq(
|
||||||
OsgiKeys.importPackage := Seq("!sun.misc", akkaImport(), configImport(), scalaImport(), "*"),
|
OsgiKeys.importPackage := defaultImports,
|
||||||
OsgiKeys.exportPackage := packages
|
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 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 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)
|
def scalaImport(packageName: String = "scala.*") = "%s;version=\"[2.9.2,2.10)\"".format(packageName)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue