0%

Working with OSGI container Karaf

This article describes the fundamental principles of the OSGi development model, using Apache Karaf or ServiceMix as the container.

Karaf Installation and Startup

Before Apache Karaf can provide you with an OSGi-based container runtime, we’ll have to set up our environment first. The process is quick, requiring a minimum of normal Java usage integration work. Package download and installation can follow the guide from Apache Karaf Project Site. I used 3.0.3 in this tutorial.

After extracting the Apache Karaf distribution kit and setting environment variables, the container can be started by invoking the Karaf script provided in the bin directory:

  • bin\karaf in Windows Command Shell
  • bin/karaf in Linux Shell

You can use ctrl-D to exit the shell. However, Karaf process dies when you exit the shell. To make Karaf running in the background, you can use script named start under bin directory or install Karaf as service. After that, we can use SSH client or script client under bin directory to connect already running Karaf.

The easiest way is to run start script:

  • bin\start in Windows Command Shell
  • bin/start in Linux Shell

On Linux, the command shows nothing and run in the background. You can use ps -ef | grep karaf to get the JVM process running with Karaf. While for windows, a shell window will appear. Now we can connect to Karaf using SSH client or client provided by Karaf. By default, Karaf opened SSH port 8101 and create user karaf with password karaf.

1
2
$ ssh localhost -p 8101 -l karaf
$ bin/client -h localhost -a 8101 -u karaf

When connected, using crtl-D will not quit the Karaf process, but only the connected client. If you want to quit the background process, use shutdown command provided by Karaf.

1
karaf@root()> shutdown

To install Karaf as service, we can use the wrapper feature provided by Karaf. Start Karaf shell and run the following commands:

1
2
karaf@root()> feature:install wrapper
karaf@root()> wrapper:install -s AUTO_START -n KARAF -d Karaf -D "KarafService"

On Windows platform:

1
bin\KARAF-service install

On linux platform:

1
2
3
huhhot # ln -s /opt/programming/karaf/bin/KARAF-service /etc/init.d/
huhhot # chkconfig KARAF-service --add
huhhot # chkconfig KARAF-service on

Karaf supports both Felix and Equinox framework for OSGI implementation. You can switch the framework inside kraf.

1
2
3
4
karaf@root()> system:framework equinox
Changed OSGi framework to equinox. Karaf needs to be restarted to make the change effective
karaf@root()> system:framework felix
Changed OSGi framework to felix. Karaf needs to be restarted to make the change effective

Build Karaf OSGI Bundle using Maven

In this tutorial, we are going to build two OSGI bundles. One to subscribe event messages with topic while the other one to generate event. And finally we will create KAR package for easy installation, in case of Karaf in production environment without Maven repository Internet access.

The Open Services Gateway Initiative (OSGi), also known as the Dynamic Module System for Java, defines an architecture for modular application development. OSGi container implementations such as Knopflerfish, Equinox, and Apache Felix allow you to break your application into multiple modules and thus more easily manage cross-dependencies between them.

In OSGi, software is distributed in the form of a bundle. The MANIFEST.MF file acts as deployment descriptor for your bundle. The format for this file is the same as that of a normal JAR file.

Build from scratch for the project

Maven provides archetype template toolkit to generate skelton code. With that we can do less pom.xml configuration work.

For the bundle to generate event, named eventpublisher here, will use both Scala and Java programming languages. That is the reason why we use archetype scala-archetype-simple and add bundle related configuration in pom.xml later. For me I think the configurtaion for Scala compiling is much more complex than the one for bundle. Although there are many others archetype for Scala programming, we prefer this one for our tutorial. com.github.igor-petruk.archetypes:maven-archetype-scala-executable is a good choice for Scala programming and it supports latest Scala version.

The default supported Scala version for scala-archetype-simple is 2.8.0 and that requires JDK version 1.6. Please set JAVA_HOME and PATH environment by yourself.

Now let’s start from the scratch to build the skelton,

1
2
3
4
5
6
$ mvn archetype:generate -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=pom-root -DgroupId=tk.wangkexiong.osgi -DartifactId=eventtutorial -Dversion=1.0 -DinteractiveMode=false
$ cd eventtutorial
$ mvn archetype:generate -DarchetypeGroupId=org.apache.karaf.archetypes -DarchetypeArtifactId=karaf-command-archetype -DarchetypeVersion=3.0.3 -DgroupId=tk.wangkexiong.osgi -DartifactId=eventconsumer -Dversion=1.0 -Dscope=eventhandler -Dcommand=add -Ddescription="Add an event listener." -DinteractiveMode=false
$ mvn archetype:generate -DarchetypeGroupId=org.scala-tools.archetypes -DarchetypeArtifactId=scala-archetype-simple -DgroupId=tk.wangkexiong.osgi -DartifactId=eventpublisher -Dversion=1.0 -DinteractiveMode=false
$ mvn archetype:generate -DarchetypeGroupId=org.apache.karaf.archetypes -DarchetypeArtifactId=karaf-kar-archetype -DarchetypeVersion=3.0.3 -DgroupId=tk.wangkexiong.osgi -DartifactId=eventkar -Dversion=1.0 -DinteractiveMode=false
$ mvn clean package

There is a basic concept in Maven Multiple Modules Project, which is called reactor. Making KAR package will depend on the eventconsumer and eventpublisher modules. So the module to make KAR package should be after above 2 modules in reactor order. You may ask if we can put KAR package work in root module (eventtutorial in our case). The answer is NO, because root is always the first one to be built in reactor order.

Here is the snippet from the Maven build logs and tells the reactor order.

[INFO] Reactor Summary:
[INFO]
[INFO] eventtutorial … SUCCESS [0.077s]
[INFO] Apache Karaf :: Shell eventhandler/add Commands … SUCCESS [3.982s]
[INFO] eventpublisher … SUCCESS [13.860s]
[INFO] eventkar-kar … SUCCESS [1.012s]

Programming with Event Subscription

BundleActivator is an interface that may be implemented when a bundle is started or stopped. And it should be specified through the Bundle-Activator Manifest header.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Activator implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
System.out.println(getBundleName(context) + " Started ...");
}

@Override
public void stop(BundleContext context) throws Exception {
System.out.println(getBundleName(context) + " Stoped ...");
}

private String getBundleName(BundleContext context) {
return context.getBundle().getHeaders().get("Bundle-Name");
}
}

Next, tell the bundle to use this interface in the META-INF\MANIFEST.MF. Such file is automatic generated by Maven plugin maven-bundle-plugin.
But Bundle-Activator header is not included by using archetype karaf-command-archetype, we need to add it back.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.5.3</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Activator>tk.wangkexiong.osgi.Activator</Bundle-Activator>
<Export-Package>tk.wangkexiong.osgi*;version=${project.version}</Export-Package>
<Import-Package>!tk.wangkexiong.osgi*,
org.apache.aries.blueprint,
org.osgi.service.blueprint.container,
org.osgi.service.blueprint.reflect,
org.apache.felix.service.command,
org.apache.karaf.shell.commands,
org.apache.karaf.shell.console,
*</Import-Package>
</instructions>
</configuration>
</plugin>

We are going add 3 commands: add/remove/list under eventhandler scope. For add operation, we need to register the event topic with its eventhandler. While for remove, we need to unregister them. Thus we need a hashmap to record our event topic and its eventhandler instance.

The event service registration API is provided by bundleContext. We could inject it by Blueprint service, DI like Sprint.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class EventRepository implements BundleContextAware {
private Map<String, Registry> eventHandlers = new HashMap<String, Registry>();
protected BundleContext bundleContext;

......

public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}

public BundleContext getBundleContext() {
Bundle framework = bundleContext.getBundle(0);

return (framework == null) ? bundleContext : framework.getBundleContext();
}
}

And the Blueprint configuration xml is under eventtutorial/eventconsumer/src/main/resources/OSGI-INF/blueprint. Our skelton already includes file named shell-log.xml. We can use it or rename its name as you like.

1
2
3
<bean id="repository" class="tk.wangkexiong.osgi.business.EventRepository" destroy-method="cleanup">
<property name="bundleContext" ref="blueprintBundleContext"/>
</bean>

Now MODIFY generated code for Karaf shell command eventhandler:add. Here I renamed the add.java to AddEventCommand.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Command(scope = "eventhandler", name = "add", description = "Add an event listener.")
public class AddEventCommand extends OsgiCommandSupport {
private static final String DESC =
"The event topic to listen to (*, org/apache/karaf, org/apache/karaf/*," +
"org/apache/karaf/log, org.apache/karaf/log2),\n" +
"only one handler per topic will be created." +
"The filter is space separated.";
@Argument(index = 0, name = "filter", description = DESC, required = true, multiValued = false)
String filter;
EventRepository repository;

@Override
protected Object doExecute() throws Exception {
repository.addEvent(filter);

return null;
}

public void setRepository(EventRepository respository) {
this.repository = respository;
}
}

Change Blueprint configuration to inject the repository for AddEventCommand, and map the command eventhandler:add with our implementation class.

1
2
3
4
5
<command>
<action class="tk.wangkexiong.osgi.karaf.AddEventCommand">
<property name="repository" ref="repository"/>
</action>
</command>

Finally, we could write our business logic for add command.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class EventRepository implements BundleContextAware {
......

public synchronized void addEvent(String filter) throws InvalidSyntaxException {
if (!eventHandlers.containsKey(filter)) {
EventHandler handler = new EventDisplayer();
Dictionary<String, String> properties = new Hashtable<String, String>();
properties.put(EventConstants.EVENT_TOPIC, filter);

ServiceRegistration<?> registration = getBundleContext()
.registerService(EventHandler.class.getName(),
handler, properties);

Registry registry = new Registry(handler, registration);
eventHandlers.put(filter, registry);
}
}

......
}

public class EventDisplayer implements EventHandler {
@Override
public void handleEvent(Event event) {
StringBuilder builder = new StringBuilder();
builder.append("\n");
builder.append("### Event received ###");
builder.append("\n");
builder.append(event.getTopic());
builder.append("\n");

for (String property : event.getPropertyNames()) {
builder.append("\t");
builder.append(property);
builder.append("->");
builder.append(event.getProperty(property));
builder.append("\n");
}

System.out.println(builder.toString());
}
}

Command list is used to display all the registered event topic. And Command remove is used to unregister the service and remove it from our recorded hashmap.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class EventRepository implements BundleContextAware {
......

public synchronized void removeEvent(String filter) {
if (eventHandlers.containsKey(filter)) {
Registry registry = eventHandlers.get(filter);
registry.getRegistration().unregister();

releaseResource(registry, registry.getHandler());
eventHandlers.remove(filter);
}
}

public Set<String> getFilters() {
return eventHandlers.keySet();
}

......
}

Programming with Event Generation

The pom.xml file in skelton does not know about OSGI Bundle working, we can copy back the part like the one in eventconsumer module. The source code directory in skelton only includes Scala codes, we can change it to src/main. Thus Java code will be put under src/main/java while Scala will be put under src/main/scala. They still follow the code directory structure rule.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<groupId>tk.wangkexiong.osgi</groupId>
<artifactId>eventpublisher</artifactId>
<version>1.0</version>
<packaging>bundle</packaging>

<build>
<sourceDirectory>src/main</sourceDirectory>
<testSourceDirectory>src/test</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${maven-bundle-plugin.version}</version>
<extensions>true</extensions>

<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>${project.version}</Bundle-Version>
<Bundle-Activator>tk.wangkexiong.osgi.scala.Activator</Bundle-Activator>
<Export-Package>tk.wangkexiong*;version=${project.version}</Export-Package>
<Import-Package>*</Import-Package>
</instructions>
</configuration>
</plugin>
</build>

Programming OSGI with Scala

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.osgi.framework._

class Activator extends BundleActivator {

def start( context: BundleContext ) {
var bundleNames = context.getBundle().getHeaders

println(bundleNames.get("Bundle-Name") + " Started ...")
}

def stop( context: BundleContext ) {
var bundleNames = context.getBundle().getHeaders

println(bundleNames.get("Bundle-Name") + " Stoped ...")
}
}

Add a new Karaf Shell Command named event, still the bundleContext is dependency injection by Blueprint. Sending event requires EventAdmin service. We use bundleContext to check if that OSGI service is ready. Which means if we don’t have EventAdmin service ready, we cannot send event. In Karaf, we could use feature:install eventadmin to make that bundle installed and running.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Command(scope = "event", name = "publish", description = "Publish Hello Event.")
public class PublishHelloCommand extends OsgiCommandSupport
implements BundleContextAware {

private static final String DESC = "Say hello to somebody";

@Argument(index = 0, name = "topic", description = DESC, required = true, multiValued = false)
protected String topic;
@Argument(index = 1, name = "name", description = DESC, required = true, multiValued = false)
protected String name;

protected BundleContext bundleContext;

public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}

@Override
protected Object doExecute() throws Exception {
ServiceReference<?> ref = bundleContext.getServiceReference(EventAdmin.class.getName());

if (ref != null) {
EventAdmin eventAdmin = (EventAdmin) bundleContext.getService(ref);

Dictionary<String, String> properties = new Hashtable<String, String>();
properties.put("name", this.name);

Event event = new Event(this.topic, properties);
eventAdmin.sendEvent(event);
} else {
System.out.println("eventadmin feature is not installed...");
}

return null;
}
}

Run in the Karaf

First, we do a fresh build and install our bundles in local Maven repository.

1
$ mvn clean install

In Karaf, we add our bundles using Maven repository.

You may notice that when install scala-library 2.8, we use wrap type. It is because that release does not include OSGI descriptions. We can use wrap type to deploy no-OSGI jar files(“classical” jar files). The exception information here is a bug that does not impact our bundle usage.

However, latest Scala-library are already OSGI bundles. The reason why I use version 2.8.0 is that, it is default generated by skelton and I want to show how to use wrap protocol here. If you don’t like it, you can upgrade the Scala version from 2.8.0 to 2.11.7. And remember to remove wrap protocol, because it is already OSGI bundle for 2.11.7.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
        __ __                  ____
/ //_/____ __________ _/ __/
/ ,< / __ `/ ___/ __ `/ /_
/ /| |/ /_/ / / / /_/ / __/
/_/ |_|\__,_/_/ \__,_/_/

Apache Karaf (3.0.3)

Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown Karaf.

karaf@root()> feature:install eventadmin
karaf@root()> bundle:install -s mvn:tk.wangkexiong.osgi/eventconsumer/1.0
Karaf :: Shell eventhandler Commands Started ...
Bundle ID: 65
karaf@root()> bundle:install wrap:mvn:org.scala-lang/scala-library/2.8.0
java.lang.ArrayIndexOutOfBoundsException: 176
at aQute.bnd.osgi.Clazz.classConstRef(Clazz.java:1862)
at aQute.bnd.osgi.Clazz.crawl(Clazz.java:1166)
at aQute.bnd.osgi.Clazz.doCode(Clazz.java:1134)
at aQute.bnd.osgi.Clazz.doAttribute(Clazz.java:945)
at aQute.bnd.osgi.Clazz.doAttributes(Clazz.java:910)
at aQute.bnd.osgi.Clazz.parseClassFile(Clazz.java:741)
at aQute.bnd.osgi.Clazz.parseClassFile(Clazz.java:494)
at aQute.bnd.osgi.Clazz.parseClassFileWithCollector(Clazz.java:483)
at aQute.bnd.osgi.Clazz.parseClassFile(Clazz.java:473)
at aQute.bnd.osgi.Analyzer.analyzeJar(Analyzer.java:2177)
at aQute.bnd.osgi.Analyzer.analyzeBundleClasspath(Analyzer.java:2083)
at aQute.bnd.osgi.Analyzer.analyze(Analyzer.java:138)
at aQute.bnd.osgi.Analyzer.calcManifest(Analyzer.java:616)
at org.ops4j.pax.swissbox.bnd.BndUtils.createBundle(BndUtils.java:161)
at org.ops4j.pax.url.wrap.internal.Connection.getInputStream(Connection.java:83)
at org.apache.felix.framework.util.SecureAction.getURLConnectionInputStream(SecureAction.java:524)
at org.apache.felix.framework.cache.JarRevision.initialize(JarRevision.java:165)
at org.apache.felix.framework.cache.JarRevision.<init>(JarRevision.java:77)
at org.apache.felix.framework.cache.BundleArchive.createRevisionFromLocation(BundleArchive.java:878)
at org.apache.felix.framework.cache.BundleArchive.reviseInternal(BundleArchive.java:550)
at org.apache.felix.framework.cache.BundleArchive.<init>(BundleArchive.java:153)
at org.apache.felix.framework.cache.BundleCache.create(BundleCache.java:277)
at org.apache.felix.framework.Felix.installBundle(Felix.java:2866)
at org.apache.felix.framework.BundleContextImpl.installBundle(BundleContextImpl.java:165)
at org.apache.karaf.bundle.command.Install.doExecute(Install.java:43)
at org.apache.karaf.shell.console.AbstractAction.execute(AbstractAction.java:33)
at org.apache.karaf.shell.console.OsgiCommandSupport.execute(OsgiCommandSupport.java:39)
at org.apache.karaf.shell.commands.basic.AbstractCommand.execute(AbstractCommand.java:33)
at Proxy90baf6b3_ea7e_447e_a609_7de43f4600a4.execute(Unknown Source)
at Proxy90baf6b3_ea7e_447e_a609_7de43f4600a4.execute(Unknown Source)
at org.apache.felix.gogo.runtime.CommandProxy.execute(CommandProxy.java:78)
at org.apache.felix.gogo.runtime.Closure.executeCmd(Closure.java:477)
at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:403)
at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108)
at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:183)
at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:120)
at org.apache.felix.gogo.runtime.CommandSessionImpl.execute(CommandSessionImpl.java:92)
at org.apache.karaf.shell.console.impl.jline.ConsoleImpl.run(ConsoleImpl.java:208)
at org.apache.karaf.shell.console.impl.jline.LocalConsoleManager$2$1$1.run(LocalConsoleManager.java:109)
at java.security.AccessController.doPrivileged(Native Method)
at org.apache.karaf.jaas.modules.JaasHelper.doAs(JaasHelper.java:57)
at org.apache.karaf.shell.console.impl.jline.LocalConsoleManager$2$1.run(LocalConsoleManager.java:102)
java.lang.ArrayIndexOutOfBoundsException: 176
at aQute.bnd.osgi.Clazz.classConstRef(Clazz.java:1862)
at aQute.bnd.osgi.Clazz.crawl(Clazz.java:1166)
at aQute.bnd.osgi.Clazz.doCode(Clazz.java:1134)
at aQute.bnd.osgi.Clazz.doAttribute(Clazz.java:945)
at aQute.bnd.osgi.Clazz.doAttributes(Clazz.java:910)
at aQute.bnd.osgi.Clazz.parseClassFile(Clazz.java:741)
at aQute.bnd.osgi.Clazz.parseClassFile(Clazz.java:494)
at aQute.bnd.osgi.Clazz.parseClassFileWithCollector(Clazz.java:483)
at aQute.bnd.osgi.Clazz.parseClassFile(Clazz.java:473)
at aQute.bnd.osgi.Analyzer.analyzeJar(Analyzer.java:2177)
at aQute.bnd.osgi.Analyzer.analyzeBundleClasspath(Analyzer.java:2083)
at aQute.bnd.osgi.Analyzer.analyze(Analyzer.java:138)
at aQute.bnd.osgi.Analyzer.calcManifest(Analyzer.java:616)
at org.ops4j.pax.swissbox.bnd.BndUtils.createBundle(BndUtils.java:161)
at org.ops4j.pax.url.wrap.internal.Connection.getInputStream(Connection.java:83)
at org.apache.felix.framework.util.SecureAction.getURLConnectionInputStream(SecureAction.java:524)
at org.apache.felix.framework.cache.JarRevision.initialize(JarRevision.java:165)
at org.apache.felix.framework.cache.JarRevision.<init>(JarRevision.java:77)
at org.apache.felix.framework.cache.BundleArchive.createRevisionFromLocation(BundleArchive.java:878)
at org.apache.felix.framework.cache.BundleArchive.reviseInternal(BundleArchive.java:550)
at org.apache.felix.framework.cache.BundleArchive.<init>(BundleArchive.java:153)
at org.apache.felix.framework.cache.BundleCache.create(BundleCache.java:277)
at org.apache.felix.framework.Felix.installBundle(Felix.java:2866)
at org.apache.felix.framework.BundleContextImpl.installBundle(BundleContextImpl.java:165)
at org.apache.karaf.bundle.command.Install.doExecute(Install.java:43)
at org.apache.karaf.shell.console.AbstractAction.execute(AbstractAction.java:33)
at org.apache.karaf.shell.console.OsgiCommandSupport.execute(OsgiCommandSupport.java:39)
at org.apache.karaf.shell.commands.basic.AbstractCommand.execute(AbstractCommand.java:33)
at Proxy90baf6b3_ea7e_447e_a609_7de43f4600a4.execute(Unknown Source)
at Proxy90baf6b3_ea7e_447e_a609_7de43f4600a4.execute(Unknown Source)
at org.apache.felix.gogo.runtime.CommandProxy.execute(CommandProxy.java:78)
at org.apache.felix.gogo.runtime.Closure.executeCmd(Closure.java:477)
at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:403)
at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108)
at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:183)
at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:120)
at org.apache.felix.gogo.runtime.CommandSessionImpl.execute(CommandSessionImpl.java:92)
at org.apache.karaf.shell.console.impl.jline.ConsoleImpl.run(ConsoleImpl.java:208)
at org.apache.karaf.shell.console.impl.jline.LocalConsoleManager$2$1$1.run(LocalConsoleManager.java:109)
at java.security.AccessController.doPrivileged(Native Method)
at org.apache.karaf.jaas.modules.JaasHelper.doAs(JaasHelper.java:57)
at org.apache.karaf.shell.console.impl.jline.LocalConsoleManager$2$1.run(LocalConsoleManager.java:102)
Bundle ID: 66
karaf@root()> bundle:install -s mvn:tk.wangkexiong.osgi/eventpublisher/1.0
Karaf :: Shell event Commands Started ...
Bundle ID: 67
karaf@root()> eventhandler:list
Registered EventHandler Topics

karaf@root()> eventhandler:add tk/wangkexiong
karaf@root()> event:publish tk/wangkexiong morning!

### Event received ###
tk/wangkexiong
name->morning!
event.topics->tk/wangkexiong

karaf@root()> event:publish xxx/yyy morning!
karaf@root()> eventhandler:add xxx/yyy
karaf@root()> event:publish xxx/yyy morning!

### Event received ###
xxx/yyy
name->morning!
event.topics->xxx/yyy

Generate KAR package

The programming for KAR package is very easy when using Maven karaf-maven-plugin. The Karaf feature file is already generated in skelton, we need to add dependency feature and bundles in the feature file. The plugin will help to include them all in the kar package. Edit the feature file named as eventkar/src/main/feature/feature.xml

1
2
3
4
5
6
7
8
9
10
11
12
<features name="${project.artifactId}-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.0.0">

<feature name='${project.artifactId}' description='${project.name}' version='${project.version}' resolver="${resolver}">
<details>${project.description}</details>

<feature>eventadmin</feature>
<bundle start-level='40'>wrap:mvn:org.scala-lang/scala-library/2.8.0</bundle>
<bundle>mvn:tk.wangkexiong.osgi/eventconsumer/1.0</bundle>
<bundle>mvn:tk.wangkexiong.osgi/eventpublisher/1.0</bundle>
</feature>

</features>

Let’s do a fresh build and the kar package is generated under eventkar/target/, which can be placed under $KARAF_HOME/deploy/.

1
$ mvn clean package

Conclusion

Using Maven do a great help for OSGI programming and deployment. However, we did not include basic concepts for OSGI/Bundles/OSGI Service Registry/Blueprint here. Please use Google for more information. And please note, the command shell in Karaf may be different from different version. Reading information from Karaf web site is necessary before your start.

p.s There is a Good PPT include almost everything for Karaf Programming, here is the link.