Merge pull request #1310 from vivosys/akkaosgisample

Akka OSGi sample
This commit is contained in:
Roland Kuhn 2013-04-08 12:32:24 -07:00
commit 1835b641e6
33 changed files with 1824 additions and 4 deletions

View file

@ -54,7 +54,8 @@ abstract class ActorSystemActivator extends BundleActivator {
val logServiceListner = new ServiceListener {
def serviceChanged(event: ServiceEvent) {
event.getType match {
case ServiceEvent.REGISTERED system.eventStream.publish(serviceForReference[LogService](context, event.getServiceReference))
case ServiceEvent.REGISTERED
system.eventStream.publish(serviceForReference[LogService](context, event.getServiceReference))
case ServiceEvent.UNREGISTERING system.eventStream.publish(UnregisteringLogService)
}
}

View file

@ -0,0 +1,49 @@
akka-osgi-sample : Clustered DiningHakkers
================
This project may be used to test akka bundles in OSGi Frameworks. The build tool (sbt for the moment) provide scripts to run in an OSGi Framework (Karaf only for the moment) a version of the DiningHakkers that runs on several nodes using the akka-cluster module.
## Bundle overview
This project provides three Osgi Bundles
- api providing an API for the Service exposed by the core and used by the command
- core implementing the whole logic: clustered connections, Hakkers, ChopSticks. Finally it provides an ActorRef of one created Hakker
- command use a service to get a Hakker (ActorRef) with its position around the table
An integration testing module is provided to verify OSGi functionality:
- integration-test
Two modules that provision the project into the Karaf OSGi container for experimentation and integration testing:
- assembly-features defines the karaf "feature" that allows Karaf to provision the bundles
- assembly-dist creates a distribution tar.gz and zip file containing the configured Karaf runtime
## How to use it
### Setup with sbt
just run:
```bash
sbt clean
sbt package
sbt osgi-bundle
```
sbt will creates the bundles in each subproject akka-sample/akka-sample-osgi-dining-hakkers/(api, command, core)/target directories. To have integration tests and OSGi environment loaded, please use the Maven build (at least for the moment)
### Setup with Maven
```bash
mvn clean install
```
The assembly-dist/target/ directory will now contain a tar.gz file that contains a pre-configured Karaf runtime.
This can be extracted to any location, and bin/karaf executed. The provided karaf.sh script automates this.
### Run
Extract the OSGi Framework from the tar.gz described above into any location, or run:
``./karaf.sh``
Execute the framework by running ``bin/karaf`` from inside the extracted directory.
Then try to restart some bundles, to test the stability of the bundles:
``list`` to get the list of the bundles
``restart #bundle_number`` to restart the bundle using its ID
``exit`` or CTRL-D to exit the Karaf console
Depending on the akka version you're using, you may need to modify the core bundle when deploying on a second machine, to set its akka.remote.netty.hostname in the application.conf.

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.typesafe.akka.akka-sample-osgi-dining-hakkers</groupId>
<artifactId>project</artifactId>
<version>2.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api</artifactId>
<name>Dining Hakker :: Service API</name>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-osgi_${scala.dep.version}</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,27 @@
/**
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>.
*/
package akka.sample.osgi.api
import akka.actor.ActorRef
/*
* Define our messages, they basically speak for themselves
*/
sealed trait DiningHakkerMessage
case class Busy(chopstick: ActorRef) extends DiningHakkerMessage
case class Put(hakker: ActorRef) extends DiningHakkerMessage
case class Take(hakker: ActorRef) extends DiningHakkerMessage
case class Taken(chopstick: ActorRef) extends DiningHakkerMessage
object Eat extends DiningHakkerMessage
object Think extends DiningHakkerMessage
object Identify extends DiningHakkerMessage
case class Identification(name: String, busyWith: String) extends DiningHakkerMessage

View file

@ -0,0 +1,22 @@
/*
Copyright 2013 Crossing-Tech
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akka.sample.osgi.api
import akka.actor.ActorRef
trait DiningHakkersService {
def getHakker(name: String, chairNumber: Int): ActorRef
}

View file

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.typesafe.akka.akka-sample-osgi-dining-hakkers</groupId>
<artifactId>project</artifactId>
<version>2.2.0-SNAPSHOT</version>
</parent>
<artifactId>akka-sample-osgi-dining-hakkers-dist</artifactId>
<name>Dining Hakkers :: Distribution</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>akka-sample-osgi-dining-hakkers</artifactId>
<type>pom</type>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>api</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>core</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>command</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.karaf.tooling</groupId>
<artifactId>features-maven-plugin</artifactId>
<version>${karaf.version}</version>
<executions>
<execution>
<id>add-features-to-repo</id>
<phase>prepare-package</phase>
<goals>
<goal>add-features-to-repo</goal>
</goals>
<configuration>
<descriptors>
<descriptor>mvn:org.apache.karaf.assemblies.features/standard/${karaf.version}/xml/features</descriptor>
<descriptor>mvn:org.apache.karaf.assemblies.features/enterprise/${karaf.version}/xml/features</descriptor>
<descriptor>mvn:com.typesafe.akka.akka-sample-osgi-dining-hakkers/akka-sample-osgi-dining-hakkers/${project.version}/xml/features</descriptor>
</descriptors>
<repository>target/generated-features-repo</repository>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-cli-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<useRepositoryLayout>true</useRepositoryLayout>
<outputDirectory>target/generated-features-repo</outputDirectory>
</configuration>
</execution>
<execution>
<!-- Uncompress the standard Karaf distribution -->
<id>unpack</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.apache.karaf</groupId>
<artifactId>apache-karaf</artifactId>
<type>tar.gz</type>
<outputDirectory>target/dependencies</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>bin</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/descriptors/bin.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
<tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,85 @@
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>bin</id>
<formats>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<baseDirectory>akka-sample-osgi-dining-hakkers-${project.version}</baseDirectory>
<fileSets>
<!-- Copy over jar files -->
<fileSet>
<directory>${project.build.directory}/dependencies</directory>
<includes>
<include>*.jar</include>
</includes>
<outputDirectory>/lib/</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/main/distribution</directory>
<outputDirectory>/</outputDirectory>
<directoryMode>0755</directoryMode>
<fileMode>0644</fileMode>
<filtered>true</filtered>
<excludes>
<exclude>lib/readme.txt</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/main/bin</directory>
<outputDirectory>/bin</outputDirectory>
<directoryMode>0755</directoryMode>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>${project.build.directory}/classes/etc</directory>
<outputDirectory>/etc/</outputDirectory>
<lineEnding>unix</lineEnding>
<directoryMode>0755</directoryMode>
<fileMode>0644</fileMode>
</fileSet>
<fileSet>
<directory>${project.build.directory}/generated-features-repo</directory>
<outputDirectory>/system</outputDirectory>
<directoryMode>0755</directoryMode>
<fileMode>0644</fileMode>
</fileSet>
<!-- Expanded Karaf Standard Distribution, anything included above will not be overwritten here -->
<fileSet>
<directory>${project.build.directory}/dependencies/apache-karaf-${karaf.version}</directory>
<outputDirectory>/</outputDirectory>
<excludes>
<exclude>**/demos/**</exclude>
<exclude>bin/**</exclude>
<exclude>etc/custom.properties</exclude>
<exclude>etc/org.apache.karaf.features.cfg</exclude>
<exclude>README</exclude>
<exclude>RELEASE-NOTES</exclude>
<exclude>karaf-manual*.html</exclude>
<exclude>karaf-manual*.pdf</exclude>
</excludes>
</fileSet>
<!-- Copy over karaf/bin/* separately to get the correct file mode -->
<fileSet>
<directory>${project.build.directory}/dependencies/apache-karaf-${karaf.version}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>bin/**</include>
</includes>
<lineEnding>unix</lineEnding>
<fileMode>0755</fileMode>
</fileSet>
</fileSets>
</assembly>

View file

@ -0,0 +1,47 @@
==============================================================================
ZZ:
ZZZZ
ZZZZZZ
ZZZ' ZZZ
~7 7ZZ' ZZZ
:ZZZ: IZZ' ZZZ
,OZZZZ.~ZZ? ZZZ
ZZZZ' 'ZZZ$ ZZZ
. $ZZZ ~ZZ$ ZZZ
.=Z?. .ZZZO ~ZZ7 OZZ
.ZZZZ7..:ZZZ~ 7ZZZ ZZZ~
.$ZZZ$Z+.ZZZZ ZZZ: ZZZ$
.,ZZZZ?' =ZZO= .OZZ 'ZZZ
.$ZZZZ+ .ZZZZ IZZZ ZZZ$
.ZZZZZ' .ZZZZ' .ZZZ$ ?ZZZ
.ZZZZZZ' .OZZZ? ?ZZZ 'ZZZ$
.?ZZZZZZ' .ZZZZ? .ZZZ? 'ZZZO
.+ZZZZZZ?' .7ZZZZ' .ZZZZ :ZZZZ
.ZZZZZZ$' .?ZZZZZ' .~ZZZZ 'ZZZZ.
NNNNN $NNNN+
NNNNN $NNNN+
NNNNN $NNNN+
NNNNN $NNNN+
NNNNN $NNNN+
=NNNNNNNNND$ NNNNN DDDDDD: $NNNN+ DDDDDN NDDNNNNNNNN,
NNNNNNNNNNNNND NNNNN DNNNNN $NNNN+ 8NNNNN= :NNNNNNNNNNNNNN
NNNNN$ DNNNNN NNNNN $NNNNN~ $NNNN+ NNNNNN NNNNN, :NNNNN+
?DN~ NNNNN NNNNN MNNNNN $NNNN+:NNNNN7 $ND =NNNNN
DNNNNN NNNNNDNNNN$ $NNNNDNNNNN :DNNNNN
ZNDNNNNNNNNND NNNNNNNNNND, $NNNNNNNNNNN DNDNNNNNNNNNN
NNNNNNNDDINNNNN NNNNNNNNNNND $NNNNNNNNNNND ONNNNNNND8+NNNNN
:NNNND NNNNN NNNNNN DNNNN, $NNNNNO 7NNNND NNNNNO :NNNNN
DNNNN NNNNN NNNNN DNNNN $NNNN+ 8NNNNN NNNNN $NNNNN
DNNNNO NNNNNN NNNNN NNNNN $NNNN+ NNNNN$ NNNND, ,NNNNND
NNNNNNDDNNNNNNNN NNNNN =NNNNN $NNNN+ DNNNN? DNNNNNNDNNNNNNNND
NNNNNNNNN NNNN$ NNNNN 8NNNND $NNNN+ NNNNN= ,DNNNNNNND NNNNN$
==============================================================================
Welcome to the Akka ${project.version} OSGi sample.
The sample runs inside a Karaf environment, but the concepts are easily
applicable to any OSGi environment.

View file

@ -0,0 +1 @@
Akka ${project.version}

View file

@ -0,0 +1,31 @@
################################################################################
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
#
# All the values specified here will override the default values given
# in config.properties.
#
karaf.systemBundlesStartLevel=50
# Use Equinox
karaf.framework=equinox
# Poll etc every 5s (default = 1s)
felix.fileinstall.poll=5000

View file

@ -0,0 +1,8 @@
#
# Comma separated list of features repositories to register by default
# Default list + Dining Hakkers feature
#
featuresRepositories=mvn:org.apache.karaf.assemblies.features/standard/${karaf.version}/xml/features,mvn:org.apache.karaf.assemblies.features/enterprise/${karaf.version}/xml/features,mvn:com.typesafe.akka.akka-sample-osgi-dining-hakkers/akka-sample-osgi-dining-hakkers/${project.version}/xml/features
# Comma separated list of features to install at startup. Features definitions are looked up from repositories above.
featuresBoot=config,ssh,management,dining-hakker

View file

@ -0,0 +1,92 @@
################################################################################
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
#
# If set to true, the following property will not allow any certificate to be used
# when accessing Maven repositories through SSL
#
#org.ops4j.pax.url.mvn.certificateCheck=
#
# Path to the local Maven settings file.
# The repositories defined in this file will be automatically added to the list
# of default repositories if the 'org.ops4j.pax.url.mvn.repositories' property
# below is not set.
# The following locations are checked for the existence of the settings.xml file
# * 1. looks for the specified url
# * 2. if not found looks for ${user.home}/.m2/settings.xml
# * 3. if not found looks for ${maven.home}/conf/settings.xml
# * 4. if not found looks for ${M2_HOME}/conf/settings.xml
#
#org.ops4j.pax.url.mvn.settings=
#
# Path to the local Maven repository which is used to avoid downloading
# artifacts when they already exist locally.
# The value of this property will be extracted from the settings.xml file
# above, or defaulted to:
# System.getProperty( "user.home" ) + "/.m2/repository"
#
org.ops4j.pax.url.mvn.localRepository=file:${karaf.home}/${karaf.default.repository}
#
# Default this to false. It's just weird to use undocumented repos
#
org.ops4j.pax.url.mvn.useFallbackRepositories=false
#
# Uncomment if you don't wanna use the proxy settings
# from the Maven conf/settings.xml file
#
# org.ops4j.pax.url.mvn.proxySupport=false
#
# Disable aether support by default. This ensure that the defaultRepositories
# below will be used
#
org.ops4j.pax.url.mvn.disableAether=true
#
# Comma separated list of repositories scanned when resolving an artifact.
# Those repositories will be checked before iterating through the
# below list of repositories and even before the local repository
# A repository url can be appended with zero or more of the following flags:
# @snapshots : the repository contains snaphots
# @noreleases : the repository does not contain any released artifacts
#
# The following property value will add the system folder as a repo.
#
org.ops4j.pax.url.mvn.defaultRepositories=file:${karaf.home}/${karaf.default.repository}@snapshots,\
file:${karaf.home}/local-repo@snapshots
#
# Comma separated list of repositories scanned when resolving an artifact.
# The default list includes the following repositories:
# http://repo1.maven.org/maven2
# http://repository.apache.org/content/groups/snapshots-group
# http://svn.apache.org/repos/asf/servicemix/m2-repo
# http://repository.springsource.com/maven/bundles/release
# http://repository.springsource.com/maven/bundles/external
# To add repositories to the default ones, prepend '+' to the list of repositories
# to add.
# A repository url can be appended with zero or more of the following flags:
# @snapshots : the repository contains snaphots
# @noreleases : the repository does not contain any released artifacts
#
org.ops4j.pax.url.mvn.repositories=file:${karaf.home}/${karaf.default.repository}

View file

@ -0,0 +1,7 @@
Place files like custom.properties and jre.properties here. Files
placed here will be copied to the Karaf etc/ directory.
Exclude the Karaf default version of the file in bin.xml.
Additional configuration files for etc should be placed into
src/main/resources/etc.

View file

@ -0,0 +1,2 @@
Jars targeted to the ${app.home}/lib directory should be placed here.
See lib/README in the distribution package for more details.

View file

@ -0,0 +1,3 @@
Configuration files for the Karaf etc/ directory.
Place Karaf ConfigAdmin files here.

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.typesafe.akka.akka-sample-osgi-dining-hakkers</groupId>
<artifactId>project</artifactId>
<version>2.2.0-SNAPSHOT</version>
</parent>
<artifactId>akka-sample-osgi-dining-hakkers</artifactId>
<name>Dining Hakkers :: Features</name>
<packaging>pom</packaging>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<useDefaultDelimiters>false</useDefaultDelimiters>
<delimiters>
<!--suppress MavenModelInspection -->
<delimiter>${*}</delimiter>
</delimiters>
</configuration>
<executions>
<execution>
<id>filter</id>
<phase>generate-resources</phase>
<goals>
<goal>resources</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>attach-artifact</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>target/classes/features.xml</file>
<type>xml</type>
<classifier>features</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="akka.features-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.0.0">
<feature name='osgi-compendium' description='OSGi compendium feature' version='${osgi.version}' resolver='(obr)'>
<bundle start-level="10">mvn:org.osgi/org.osgi.compendium/${osgi.version}</bundle>
</feature>
<feature name='scala' description='Scala' version='${scala.version}' resolver='(obr)'>
<bundle start-level="15">mvn:org.scala-lang/scala-library/${scala.version}</bundle>
</feature>
<feature name='uncommons-maths' description='Uncommons Maths' version='1.2.2' resolver='(obr)'>
<!-- TODO replace this with upstream uncommons maths 1.2.3 which is OSGi enabled -->
<bundle start-level="20">mvn:com.typesafe.akka.akka-sample-osgi-dining-hakkers/uncommons/1.2.2</bundle>
</feature>
<feature name='protobuf' description='Protobuf feature' version='${protobuf.version}' resolver='(obr)'>
<bundle start-level="20">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.protobuf-java/${protobuf.version}_1</bundle>
</feature>
<feature name='netty' description='Netty feature' version='${netty.version}' resolver='(obr)'>
<bundle start-level="20">mvn:io.netty/netty/${netty.version}</bundle>
</feature>
<feature name='typesafe-config' description='Typesafe config' version='${typesafe.config.version}' resolver='(obr)'>
<feature version="[${scala.version},2.11.0)">scala</feature>
<bundle start-level="25">mvn:com.typesafe/config/${typesafe.config.version}</bundle>
</feature>
<feature name='akka' description='Akka' version='${akka.version}' resolver='(obr)'>
<feature version="[${scala.version},2.11.0)">scala</feature>
<feature version="[${netty.version},4.0.0)">netty</feature>
<feature version="[1.2.2,3.0.0)">uncommons-maths</feature>
<feature version="[${protobuf.version},3.0.0)">protobuf</feature>
<feature version="[${typesafe.config.version},2.0.0)">typesafe-config</feature>
<bundle start-level="30">mvn:com.typesafe.akka/akka-cluster-experimental_${scala.dep.version}/${akka.version}</bundle>
<bundle start-level="30">mvn:com.typesafe.akka/akka-remote_${scala.dep.version}/${akka.version}</bundle>
<bundle start-level="30">mvn:com.typesafe.akka/akka-osgi_${scala.dep.version}/${akka.version}</bundle>
</feature>
<feature name='dining-hakker' description='Akka Dining Hakker Sample' version='${project.version}' resolver='(obr)'>
<feature version="[${akka.version},3.0.0)">akka</feature>
<bundle start-level="50">mvn:com.typesafe.akka.akka-sample-osgi-dining-hakkers/api/${project.version}</bundle>
<bundle start-level="50">mvn:com.typesafe.akka.akka-sample-osgi-dining-hakkers/core/${project.version}</bundle>
<bundle start-level="50">mvn:com.typesafe.akka.akka-sample-osgi-dining-hakkers/command/${project.version}</bundle>
</feature>
</features>

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.typesafe.akka.akka-sample-osgi-dining-hakkers</groupId>
<artifactId>project</artifactId>
<version>2.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>command</artifactId>
<name>Dining Hakker :: Service consumer</name>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>api</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-osgi_${scala.dep.version}</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-Activator>akka.sample.osgi.command.Activator</Bundle-Activator>
<Import-Package>*</Import-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,41 @@
/*
Copyright 2013 Crossing-Tech
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akka.sample.osgi.command
import org.osgi.framework.{ ServiceEvent, BundleContext, BundleActivator }
import akka.sample.osgi.api.DiningHakkersService
import akka.actor.{ ActorRef, PoisonPill }
import org.osgi.util.tracker.ServiceTracker
class Activator extends BundleActivator {
println("Command Activator created")
var hakker: Option[ActorRef] = None
def start(context: BundleContext) {
val logServiceTracker = new ServiceTracker(context, classOf[DiningHakkersService].getName, null)
logServiceTracker.open()
val service = Option(logServiceTracker.getService.asInstanceOf[DiningHakkersService])
service.foreach(startHakker(_, context.getBundle.getSymbolicName + ":" + context.getBundle.getBundleId))
}
def startHakker(service: DiningHakkersService, name: String) {
hakker = Some(service.getHakker(name, (math.floor(math.random * 5)).toInt))
}
def stop(context: BundleContext) {
hakker.foreach(_ ! PoisonPill)
}
}

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.typesafe.akka.akka-sample-osgi-dining-hakkers</groupId>
<artifactId>project</artifactId>
<version>2.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>core</artifactId>
<name>Dining Hakker :: Core</name>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-osgi_${scala.dep.version}</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-remote_${scala.dep.version}</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>api</artifactId>
</dependency>
<!--Test dependencies-->
<!-- <dependency>
<groupId>org.specs2</groupId>
<artifactId>specs2_${scala.version}</artifactId>
<version>1.12.3</version>
<scope>test</scope>
</dependency>-->
<!--remoting dependencies-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-cluster-experimental_${scala.dep.version}</artifactId>
</dependency>
<dependency>
<groupId>org.fusesource</groupId>
<artifactId>sigar</artifactId>
<version>1.6.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Private-Package>akka.sample.osgi.internal, akka.sample.osgi.service</Private-Package>
<Bundle-Activator>akka.sample.osgi.activation.Activator</Bundle-Activator>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,19 @@
akka {
actor {
provider = "akka.cluster.ClusterActorRefProvider"
}
remote {
netty.tcp {
hostname = "localhost"
port = 4242
}
}
cluster {
seed-nodes = ["akka.tcp://akka-osgi-sample@localhost:4242"]
auto-down = on
}
}

View file

@ -0,0 +1,74 @@
/*
Copyright 2013 Crossing-Tech
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akka.sample.osgi.activation
import akka.osgi.ActorSystemActivator
import akka.actor.{ Props, ActorSystem }
import akka.sample.osgi.internal.Table
import akka.sample.osgi.service.DiningHakkersServiceImpl
import akka.sample.osgi.api.DiningHakkersService
import akka.event.{ LogSource, Logging }
import org.osgi.framework.{ ServiceRegistration, BundleContext }
import scala.collection.mutable.ListBuffer
class Activator extends ActorSystemActivator {
import Activator._
var diningHakkerService: Option[ServiceRegistration] = None
def configure(context: BundleContext, system: ActorSystem) {
val log = Logging(system, this)
log.info("Core bundle configured")
system.actorOf(Props[Table], "table")
registerService(context, system)
registerHakkersService(context, system)
log.info("Hakker service registered")
}
/**
* registers the DinningHakkerService as a Service to be tracked and find by other OSGi bundles.
* in other words, this instance may be used in other bundles which listen or track the OSGi Service
* @param context OSGi BundleContext
* @param system ActorSystem
*/
def registerHakkersService(context: BundleContext, system: ActorSystem) {
val hakkersService = new DiningHakkersServiceImpl(system)
diningHakkerService = Some(context.registerService(classOf[DiningHakkersService].getName(), hakkersService, null))
}
override def stop(context: BundleContext) {
unregisterServices(context)
println("Hakker service unregistred")
super.stop(context)
}
def unregisterServices(context: BundleContext) {
diningHakkerService foreach (_.unregister())
}
override def getActorSystemName(context: BundleContext): String = "akka-osgi-sample"
}
object Activator {
implicit val logSource: LogSource[AnyRef] = new LogSource[AnyRef] {
def genString(o: AnyRef): String = o.getClass.getName
override def getClazz(o: AnyRef): Class[_] = o.getClass
}
}

View file

@ -0,0 +1,170 @@
/**
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>.
*/
package akka.sample.osgi.internal
import language.postfixOps
import akka.cluster.Cluster
import akka.cluster.ClusterEvent.{ CurrentClusterState, LeaderChanged }
import akka.event.Logging
import akka.sample.osgi.api._
//Akka adaptation of
//http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
import akka.actor._
import scala.concurrent.duration._
import akka.sample.osgi.api._
/*
* A Chopstick is an actor, it can be taken, and put back
*/
class Chopstick extends Actor {
val log = Logging(context.system, this)
import context._
//When a Chopstick is taken by a hakker
//It will refuse to be taken by other hakkers
//But the owning hakker can put it back
def takenBy(hakker: ActorRef): Receive = {
case Take(otherHakker)
otherHakker ! Busy(self)
case Put(`hakker`)
become(available)
}
//When a Chopstick is available, it can be taken by a hakker
def available: Receive = {
case Take(hakker)
log.info(self.path + " is taken by " + hakker)
become(takenBy(hakker))
hakker ! Taken(self)
}
//A Chopstick begins its existence as available
def receive = available
}
/*
* A hakker is an awesome dude or dudett who either thinks about hacking or has to eat ;-)
*/
class Hakker(name: String, chair: Int) extends Actor {
val log = Logging(context.system, this)
log.info("Created Hakker at" + self.path)
import context._
val cluster = Cluster(context.system)
override def preStart() {
log.info(s"Hakker ($name) takes position($chair)")
cluster.subscribe(self, classOf[LeaderChanged])
}
override def postStop() {
log.info(s"Hakker ($name) leaves position($chair)")
cluster.unsubscribe(self)
}
//When a hakker is thinking it can become hungry
//and try to pick up its chopsticks and eat
def thinking(left: ActorRef, right: ActorRef): Receive = {
case Eat
become(hungry(left, right) orElse (clusterEvents))
left ! Take(self)
right ! Take(self)
case Identify identify("Thinking")
}
//When a hakker is hungry it tries to pick up its chopsticks and eat
//When it picks one up, it goes into wait for the other
//If the hakkers first attempt at grabbing a chopstick fails,
//it starts to wait for the response of the other grab
def hungry(left: ActorRef, right: ActorRef): Receive = {
case Taken(`left`)
become(waiting_for(left, right, false) orElse (clusterEvents))
case Taken(`right`)
become(waiting_for(left, right, true) orElse (clusterEvents))
case Busy(chopstick)
become(denied_a_chopstick(left, right) orElse (clusterEvents))
case Identify identify("Hungry")
}
//When a hakker is waiting for the last chopstick it can either obtain it
//and start eating, or the other chopstick was busy, and the hakker goes
//back to think about how he should obtain his chopsticks :-)
def waiting_for(left: ActorRef, right: ActorRef, waitingForLeft: Boolean): Receive = {
case Taken(`left`) if waitingForLeft
log.info("%s has picked up %s and %s and starts to eat".format(name, left.path.name, right.path.name))
become(eating(left, right) orElse (clusterEvents))
system.scheduler.scheduleOnce(5 seconds, self, Think)
case Taken(`right`) if !waitingForLeft
log.info("%s has picked up %s and %s and starts to eat".format(name, left.path.name, right.path.name))
become(eating(left, right) orElse (clusterEvents))
system.scheduler.scheduleOnce(5 seconds, self, Think)
case Busy(chopstick)
become(thinking(left, right) orElse (clusterEvents))
if (waitingForLeft) {
right ! Put(self)
} else {
left ! Put(self)
}
self ! Eat
case Identify identify("Waiting for Chopstick")
}
//When the results of the other grab comes back,
//he needs to put it back if he got the other one.
//Then go back and think and try to grab the chopsticks again
def denied_a_chopstick(left: ActorRef, right: ActorRef): Receive = {
case Taken(chopstick)
become(thinking(left, right) orElse (clusterEvents))
chopstick ! Put(self)
self ! Eat
case Busy(chopstick)
become(thinking(left, right) orElse (clusterEvents))
self ! Eat
case Identify identify("Denied a Chopstick")
}
//When a hakker is eating, he can decide to start to think,
//then he puts down his chopsticks and starts to think
def eating(left: ActorRef, right: ActorRef): Receive = {
case Think
become(thinking(left, right) orElse (clusterEvents))
left ! Put(self)
right ! Put(self)
log.info("%s puts down his chopsticks and starts to think".format(name))
system.scheduler.scheduleOnce(5 seconds, self, Eat)
case Identify identify("Eating")
}
def waitForChopsticks: Receive = {
case (left: ActorRef, right: ActorRef)
become(thinking(left, right) orElse (clusterEvents))
system.scheduler.scheduleOnce(5 seconds, self, Eat)
}
def clusterEvents: Receive = {
case state: CurrentClusterState state.leader foreach updateTable
case LeaderChanged(Some(leaderAddress)) updateTable(leaderAddress)
}
def identify(busyWith: String) {
sender ! Identification(name, busyWith)
}
def updateTable(leaderAdress: Address) {
become(waitForChopsticks)
context.actorFor(RootActorPath(leaderAdress) / "user" / "table") ! chair
}
//All hakkers start in a non-eating state
def receive = clusterEvents
}

View file

@ -0,0 +1,26 @@
/*
Copyright 2013 Crossing-Tech
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akka.sample.osgi.internal
import akka.actor.{ Props, Actor }
class Table extends Actor {
val chopsticks = for (i 1 to 5) yield context.actorOf(Props[Chopstick], "Chopstick" + i)
def receive = {
case x: Int sender ! (chopsticks(x), chopsticks((x + 1) % 5))
}
}

View file

@ -0,0 +1,26 @@
/*
Copyright 2013 Crossing-Tech
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akka.sample.osgi.service
import akka.sample.osgi.api.DiningHakkersService
import akka.actor.{ Props, ActorSystem }
import akka.sample.osgi.internal.Hakker
class DiningHakkersServiceImpl(system: ActorSystem) extends DiningHakkersService {
def getHakker(name: String, chairNumber: Int) = {
system.actorOf(Props(new Hakker(name, chairNumber)))
}
}

View file

@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.typesafe.akka.akka-sample-osgi-dining-hakkers</groupId>
<artifactId>project</artifactId>
<version>2.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>integration-test</artifactId>
<name>Dining Hakker :: Integration Test</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.karaf.tooling.exam</groupId>
<artifactId>org.apache.karaf.tooling.exam.container</artifactId>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit4</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-testkit_${scala.dep.version}</artifactId>
</dependency>
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest_${scala.dep.version}</artifactId>
</dependency>
<!--
This is needed only for the scalatest osgi bundle, it has a non-optional import on scala-actors stuff.
This can be removed once we are using ScalaTest 2.0.x: https://groups.google.com/d/topic/scalatest-users/XAVWBJ-v6vA/discussion
See also TestOptions.testBundles
-->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-actors</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--
Explicitly import Pax Exam 2.6.0, should be able to remove this once Karaf 2.3.1 is released
(http://www.mail-archive.com/user@karaf.apache.org/msg04061.html)
-->
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam</artifactId>
<version>${paxexam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-remote</artifactId>
<version>${paxexam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-invoker-junit</artifactId>
<version>${paxexam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-rbc</artifactId>
<version>${paxexam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-rbc-client</artifactId>
<version>${paxexam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-extender-service</artifactId>
<version>${paxexam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-inject</artifactId>
<version>${paxexam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.swissbox</groupId>
<artifactId>pax-swissbox-core</artifactId>
<version>${paxswissbox.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.swissbox</groupId>
<artifactId>pax-swissbox-extender</artifactId>
<version>${paxswissbox.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.swissbox</groupId>
<artifactId>pax-swissbox-lifecycle</artifactId>
<version>${paxswissbox.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.swissbox</groupId>
<artifactId>pax-swissbox-framework</artifactId>
<version>${paxswissbox.version}</version>
<scope>test</scope>
</dependency>
<!-- End of temporary Pax Exam dependency imports -->
</dependencies>
<build>
<plugins>
<!-- This is to allow use of versionAsInProject -->
<plugin>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>maven-paxexam-plugin</artifactId>
<version>1.2.4</version>
<executions>
<execution>
<id>generate-config</id>
<goals>
<goal>generate-depends-file</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<systemPropertyVariables>
<pax.exam.service.timeout>30000</pax.exam.service.timeout>
<karaf.version>${karaf.version}</karaf.version>
<project.version>${project.version}</project.version>
<!--<scalatest.artifact>scalatest_${scala.dep.version}</scalatest.artifact>-->
<scala.dep.version>${scala.dep.version}</scala.dep.version>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,105 @@
package akka.sample.osgi.test
import akka.actor._
import akka.sample.osgi.api.{DiningHakkersService, Identify, Identification}
import akka.sample.osgi.test.TestOptions._
import org.junit.runner.RunWith
import org.junit.{Before, Test}
import org.ops4j.pax.exam.{Option => PaxOption}
import org.ops4j.pax.exam.junit.{Configuration, JUnit4TestRunner}
import org.ops4j.pax.exam.util.Filter
import org.scalatest.junit.JUnitSuite
import org.scalatest.junit.ShouldMatchersForJUnit
import javax.inject.Inject
import java.util.concurrent.{TimeUnit, SynchronousQueue}
import akka.testkit.TestProbe
import scala.Some
/**
* This is a ScalaTest based integration test. Pax-Exam, which is responsible for loading the test class into
* the OSGi environment and executing it, currently does not support ScalaTest directly. However, ScalaTest
* provides a JUnit-compatible runner, so the test is defined to use that runner. Pax-Exam can then invoke
* it as a normal JUnit test. Because Pax Exam is using the JUnitRunner and not one of the ScalaTest traits such
* as FunSuite, the test must be defined using the JUnit @Test annotation.
*
* This is a simple test demonstrating in-container integration testing.
*
* One thing to note is that we only depend on the API bundle, not the implementation in core. The implementation
* is injected into the test at runtime via an OSGi service lookup performed by Pax Exam.
*
* TODO attempt to use the Akka test probe
*/
@RunWith(classOf[JUnit4TestRunner])
class HakkerStatusTest extends JUnitSuite with ShouldMatchersForJUnit {
@Inject @Filter(timeout = 30000)
var actorSystem: ActorSystem = _
@Inject @Filter(timeout = 30000)
var service: DiningHakkersService = _
var testProbe: TestProbe = _
@Configuration
def config: Array[PaxOption] = Array[PaxOption](
karafOptionsWithTestBundles(),
featureDiningHakkers()
//,debugOptions()
)
// Junit @Before and @After can be used as well
/* @Before
def setupAkkaTestkit() {
testProbe = new TestProbe(actorSystem)
}*/
@Test
def verifyObtainingAHakkerViaTheTheDiningHakkersService() {
val name = "TestHakker"
val hakker = Some(service.getHakker(name, (math.floor(math.random * 5)).toInt))
.getOrElse(throw new IllegalStateException("No Hakker was created via DiningHakkerService"))
hakker should not be (null)
/* TODO Getting some weird config error with TestProbe, is it a TestProbe inside OSGi issue?
Exception in thread "RMI TCP Connection(idle)" java.lang.NullPointerException
at com.typesafe.config.impl.SerializedConfigValue.writeOrigin(SerializedConfigValue.java:202)
at com.typesafe.config.impl.ConfigImplUtil.writeOrigin(ConfigImplUtil.java:224)
at com.typesafe.config.ConfigException.writeObject(ConfigException.java:58)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.io.EOFException
at java.io.ObjectInputStream$BlockDataInputStream.readUnsignedByte(ObjectInputStream.java:2747)
at java.io.ObjectInputStream.readUnsignedByte(ObjectInputStream.java:924)
at com.typesafe.config.impl.SerializedConfigValue.readCode(SerializedConfigValue.java:412)
at com.typesafe.config.impl.SerializedConfigValue.readOrigin(SerializedConfigValue.java:218)
...
testProbe.send(hakker, Identify)
val identification = testProbe.expectMsgClass(classOf[Identification])
val (fromHakker, busyWith) = (identification.name, identification.busyWith)
*/
// create an "Interrogator" actor that receives a Hakker's identification
// this is to work around the TestProbe Exception above
val response = new SynchronousQueue[(String, String)]()
class Interrogator extends Actor {
def receive = {
case msg: Identification {
response.put((msg.name, msg.busyWith))
}
}
}
hakker.tell(Identify, actorSystem.actorOf(Props(new Interrogator()), "Interrogator"))
val (fromHakker, busyWith) = response.poll(5, TimeUnit.SECONDS)
println("---------------> %s is busy with %s.".format(fromHakker, busyWith))
fromHakker should be ("TestHakker")
busyWith should not be (null)
}
}

View file

@ -0,0 +1,61 @@
package akka.sample.osgi.test
import org.ops4j.pax.exam.CoreOptions._
import org.ops4j.pax.exam.options.DefaultCompositeOption
import org.ops4j.pax.exam.{Option => PaxOption}
import org.apache.karaf.tooling.exam.options.LogLevelOption
import org.apache.karaf.tooling.exam.options.KarafDistributionOption._
/**
* Re-usable PAX Exam option groups.
*/
object TestOptions {
val scalaDepVersion = System.getProperty("scala.dep.version")
def karafOptions(useDeployFolder: Boolean = false): PaxOption = {
new DefaultCompositeOption(
karafDistributionConfiguration.frameworkUrl(
maven.groupId("org.apache.karaf").artifactId("apache-karaf").`type`("zip").version(System.getProperty("karaf.version")))
.karafVersion(System.getProperty("karaf.version")).name("Apache Karaf").useDeployFolder(useDeployFolder),
editConfigurationFilePut("etc/config.properties", "karaf.framework", "equinox")
)
}
def testBundles(): PaxOption = {
new DefaultCompositeOption(
mavenBundle("com.typesafe.akka", "akka-testkit_%s".format(scalaDepVersion)).versionAsInProject,
mavenBundle("org.scalatest", "scalatest_%s".format(scalaDepVersion)).versionAsInProject,
// This is needed for the scalatest osgi bundle, it has a non-optional import on a scala-actors package
mavenBundle("org.scala-lang", "scala-actors").versionAsInProject,
junitBundles
)
}
def debugOptions(level: LogLevelOption.LogLevel = LogLevelOption.LogLevel.INFO, debugPort: Int= 5005): PaxOption = {
new DefaultCompositeOption(
logLevel(level),
debugConfiguration(String.valueOf(debugPort), true),
configureConsole().startLocalConsole(),
configureConsole().startRemoteShell()
)
}
def karafOptionsWithTestBundles(useDeployFolder: Boolean = false): PaxOption = {
new DefaultCompositeOption(
karafOptions(useDeployFolder),
testBundles()
)
}
def featureDiningHakkers(): PaxOption = {
akkaFeature("dining-hakker")
}
def akkaFeature(feature: String): PaxOption = {
scanFeatures(maven.groupId("com.typesafe.akka.akka-sample-osgi-dining-hakkers")
.artifactId("akka-sample-osgi-dining-hakkers") .`type`("xml").classifier("features")
.version(System.getProperty("project.version")), feature)
}
}

View file

@ -0,0 +1,16 @@
#!/bin/bash
projdir=$(cd $(dirname $0); pwd)
version=2.2.0-SNAPSHOT
# This directory is specified in the build as the root of the tar
# Use tar --strip-components=1 to ignore the root
outputdir="$projdir/akka-osgi-sample-$version"
if [[ -d "$outputdir" ]]; then
echo Deleting existing $outputdir...
rm -fr "$outputdir"
fi
echo Extracting configured container into $outputdir...
tar -C $projdir -zxf assembly-dist/target/assembly-dist-$version.tar.gz
echo Extract complete, please run $outputdir/bin/karaf

View file

@ -0,0 +1,2 @@
#!/bin/bash
./apache-karaf-2.3.0/bin/karaf

View file

@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.typesafe.akka.akka-sample-osgi-dining-hakkers</groupId>
<artifactId>project</artifactId>
<version>2.2.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<akka.version>2.2-SNAPSHOT</akka.version>
<karaf.version>2.3.0</karaf.version>
<karaf.tooling.exam.version>${karaf.version}</karaf.tooling.exam.version>
<netty.version>3.6.2.Final</netty.version>
<osgi.version>4.2.0</osgi.version>
<paxexam.version>2.6.0</paxexam.version>
<paxswissbox.version>1.6.0</paxswissbox.version>
<protobuf.version>2.4.1</protobuf.version>
<scala.version>2.10.1</scala.version>
<scala.dep.version>2.10</scala.dep.version>
<scalatest.version>1.9.1</scalatest.version>
<typesafe.config.version>1.0.0</typesafe.config.version>
</properties>
<modules>
<module>api</module>
<module>command</module>
<module>uncommons</module>
<module>core</module>
<module>assembly-features</module>
<module>assembly-dist</module>
<module>integration-test</module>
</modules>
<packaging>pom</packaging>
<name>akka-sample-osgi-dining-hakkers</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>${osgi.version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
<version>${osgi.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>command</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>akka-sample-osgi-dining-hakkers</artifactId>
<version>${project.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-osgi_${scala.dep.version}</artifactId>
<version>${akka.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-cluster-experimental_${scala.dep.version}</artifactId>
<version>${akka.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-remote_${scala.dep.version}</artifactId>
<version>${akka.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
<version>${typesafe.config.version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>org.apache.karaf.tooling.exam</groupId>
<artifactId>org.apache.karaf.tooling.exam.container</artifactId>
<version>${karaf.tooling.exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit4</artifactId>
<version>${paxexam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-testkit_${scala.dep.version}</artifactId>
<version>${akka.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest_${scala.dep.version}</artifactId>
<version>${scalatest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-compiler</artifactId>
<version>${scala.version}</version>
</dependency>
<!-- Depend on both the tar.gz and the zip, Pax Exam then does not have to download them separately -->
<dependency>
<groupId>org.apache.karaf</groupId>
<artifactId>apache-karaf</artifactId>
<version>${karaf.version}</version>
<type>tar.gz</type>
</dependency>
<dependency>
<groupId>org.apache.karaf</groupId>
<artifactId>apache-karaf</artifactId>
<version>${karaf.version}</version>
<type>zip</type>
</dependency>
</dependencies>
<repositories>
<repository>
<id>oss-sonatype-releases</id>
<url>https://oss.sonatype.org/content/repositories/releases</url>
</repository>
<repository>
<id>typesafe-snapshots</id>
<url>http://repo.typesafe.com/typesafe/snapshots/</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/scala</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.1.2</version>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<args>
<arg>-deprecation</arg>
<arg>-feature</arg>
<arg>-encoding</arg>
<arg>UTF-8</arg>
<!--<arg>-uniqid</arg>-->
</args>
<!--<useZincServer>true</useZincServer>-->
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.7</version>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.typesafe.akka.akka-sample-osgi-dining-hakkers</groupId>
<artifactId>project</artifactId>
<version>2.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<!--
Note that 1.2.3 of uncommons-maths has OSGi meta-data, see https://www.assembla.com/spaces/akka/tickets/2990
Once that is available, this module can be removed.
@see ticket #2990
-->
<artifactId>uncommons</artifactId>
<name>org.uncommons.maths.random</name>
<version>1.2.2</version>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.uncommons.maths</groupId>
<artifactId>uncommons-maths</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>jfree</groupId>
<artifactId>jcommon</artifactId>
<version>1.0.16</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Export-Package>org.uncommons.maths.random</Export-Package>
<Import-Package>!sun.misc, *</Import-Package>
<Private-Package>org.uncommons.maths.binary, org.uncommons.maths, org.uncommons.maths.number</Private-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -344,7 +344,7 @@ object AkkaBuild extends Build {
id = "akka-samples",
base = file("akka-samples"),
settings = parentSettings,
aggregate = Seq(camelSample, fsmSample, helloSample, helloKernelSample, remoteSample, clusterSample, multiNodeSample)
aggregate = Seq(camelSample, fsmSample, helloSample, helloKernelSample, remoteSample, clusterSample, multiNodeSample, osgiDiningHakkersSample)
)
lazy val camelSample = Project(
@ -414,6 +414,44 @@ object AkkaBuild extends Build {
)
) configs (MultiJvm)
lazy val osgiDiningHakkersSample = Project(id = "akka-sample-osgi-dining-hakkers",
base = file("akka-samples/akka-sample-osgi-dining-hakkers"),
settings = sampleSettings ++ Seq(
test in Test ~= { x => {
if({List("sh", "-c", "cd akka-samples/akka-sample-osgi-dining-hakkers; mvn clean install") !} != 0 ) {throw new Exception("Osgi sample Dining hakkers failed")}
} }
)
) aggregate(osgiDiningHakkersSampleApi, osgiDiningHakkersSampleCommand, osgiDiningHakkersSampleCore, uncommons)
lazy val osgiDiningHakkersSampleApi = Project(id = "akka-sample-osgi-dining-hakkers-api",
base = file("akka-samples/akka-sample-osgi-dining-hakkers/api"),
settings = sampleSettings ++ OSGi.osgiDiningHakkersSampleApi
)dependsOn(actor)
lazy val osgiDiningHakkersSampleCommand = Project(id = "akka-sample-osgi-dining-hakkers-command",
base = file("akka-samples/akka-sample-osgi-dining-hakkers/command"),
settings = sampleSettings ++ OSGi.osgiDiningHakkersSampleCommand ++ Seq(
libraryDependencies ++= Dependencies.osgiDiningHakkerSampleCommand
)
) dependsOn (osgiDiningHakkersSampleApi, actor)
lazy val osgiDiningHakkersSampleCore = Project(id = "akka-sample-osgi-dining-hakkers-core",
base = file("akka-samples/akka-sample-osgi-dining-hakkers/core"),
settings = sampleSettings ++ OSGi.osgiDiningHakkersSampleCore ++ Seq(
libraryDependencies ++= Dependencies.osgiDiningHakkerSampleCore
)
) dependsOn (osgiDiningHakkersSampleApi, actor, remote, cluster, osgi)
//TODO to remove it as soon as the uncommons gets OSGified, see ticket #2990
lazy val uncommons = Project(id = "akka-sample-osgi-dining-hakkers-uncommons",
base = file("akka-samples/akka-sample-osgi-dining-hakkers//uncommons"),
settings = sampleSettings ++ OSGi.osgiDiningHakkersSampleUncommons ++ Seq(
libraryDependencies ++= Dependencies.uncommons,
version := "1.2.2"
)
)
lazy val docs = Project(
id = "akka-docs",
base = file("akka-docs"),
@ -804,6 +842,14 @@ object AkkaBuild extends Build {
OsgiKeys.importPackage := (osgiOptionalImports map optionalResolution) ++ Seq("!sun.misc", scalaImport(),configImport(), "*")
)
val osgiDiningHakkersSampleApi = exports(Seq("akka.sample.osgi.api"))
val osgiDiningHakkersSampleCommand = osgiSettings ++ Seq(OsgiKeys.bundleActivator := Option("akka.sample.osgi.command.Activator"), OsgiKeys.privatePackage := Seq("akka.sample.osgi.command"))
val osgiDiningHakkersSampleCore = exports(Seq("")) ++ Seq(OsgiKeys.bundleActivator := Option("akka.sample.osgi.activation.Activator"), OsgiKeys.privatePackage := Seq("akka.sample.osgi.internal", "akka.sample.osgi.activation", "akka.sample.osgi.service"))
val osgiDiningHakkersSampleUncommons = exports(Seq("org.uncommons.maths.random")) ++ Seq(OsgiKeys.privatePackage := Seq("org.uncommons.maths.binary", "org.uncommons.maths", "org.uncommons.maths.number"))
val osgiAries = exports() ++ Seq(OsgiKeys.privatePackage := Seq("akka.osgi.aries.*"))
val remote = exports(Seq("akka.remote.*"), imports = Seq(protobufImport()))
@ -875,7 +921,7 @@ object Dependencies {
val camelJetty = "org.apache.camel" % "camel-jetty" % camelCore.revision // ApacheV2
// Cluster Sample
val sigar = "org.fusesource" % "sigar" % "1.6.4" // ApacheV2
val sigar = "org.fusesource" % "sigar" % "1.6.4" // ApacheV2
// Compiler plugins
val genjavadoc = compilerPlugin("com.typesafe.genjavadoc" %% "genjavadoc-plugin" % "0.4" cross CrossVersion.full) // ApacheV2
@ -932,7 +978,13 @@ object Dependencies {
val osgi = Seq(osgiCore, osgiCompendium, Test.logback, Test.commonsIo, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit)
val osgiAries = Seq(osgiCore, ariesBlueprint, Test.ariesProxy)
val osgiDiningHakkerSampleCore = Seq(config, osgiCore, osgiCompendium)
val osgiDiningHakkerSampleCommand = Seq(osgiCore, osgiCompendium)
val uncommons = Seq(uncommonsMath)
val osgiAries = Seq(osgiCore, osgiCompendium, ariesBlueprint, Test.ariesProxy)
val docs = Seq(Test.scalatest, Test.junit, Test.junitIntf)