Introduction
These are few notes about getting started with a (very) simple Spring-DM bundle in Eclipse. It assumes at least some understanding of what the OSGI framework is and its benefits, as well as regular Spring knowledge for the Spring part. I start by implementing the solution directly against the OSGI apis, and then move on to its Spring-DM counterpart.
Installation
First steps :
Creation of two bundles
The main purpose of OSGI being to develop modular applications and dynamically use services from other bundles, we're going to create two Bundles :
- org.naiade.messages : exposes a service that provides messages
- org.naiade.hello : consume the former bundle to print messages in the console
Create a new org.naiade.messages project as a Plug-In developement / Plug-in Project.
- On the first screen choose Equinox as a target platform instead of Eclipse
- On the second screen uncheck Generate An Activator (Spring-DM will give that for free...)
Message provider bundle
In the org.naiade.messages we provide a package and the interface for our service :
package org.naiade.messages;
public interface HelloMessageProvider {
public String getMessage() ;
}
and its default implementation :
package org.naiade.messages.impl;
import org.naiade.messages.HelloMessageProvider;
public class HelloMessageProviderImpl implements HelloMessageProvider {
public HelloMessageProviderImpl() {
super();
}
@Override
public String getMessage() {
return "Hello, Spring OSGI world !";
}
}
If you're not familiar with OSGI already you'll see that it enforces the good practice of always designing by interface. You can also note that the default implementation is in a different package. The main reason for this is that bundles specify which packages they expose to other bundles, while org.naiade.messages.HelloMessageProvider obviously will be exposed, there is no need to expose its implementation
In order to expose the interface, open the project's META-INF/MANIFEST.mf in Eclipse's
plugin development perspective, go to the Runtime tab and add org.naiade.messages as an exported package.
Consumer bundle
Create a new Plug-in project org.naiade.hello with the following class :
package org.naiade.hello;
import org.naiade.messages.HelloMessageProvider;
public class HelloSayer {
private HelloMessageProvider messageProvider;
public HelloSayer() {
super();
}
public void init() {
System.out.println(messageProvider.getMessage());
}
public void setMessageProvider(HelloMessageProvider messageProvider) {
this.messageProvider = messageProvider;
}
}
Now Eclipse will complain that org.naiade.messages.HelloMessageProvider cannot be found. Edit hello Plug-in MANIFEST in the Dependencies tab, add the org.naiade.messages Plugin as a dependency (you may have to refresh your project afterwards). Note that you can access only the packages from org.naiade.messages that it has specifically exposed. (try importing the class from org.naiade.messages.impl and Eclipse will complain).
Starting the Bundles
In order to illustrate strating the Bundles without Spring, we need to deal with the OSGI api by hand. To deal with the api we need to access the OSGI packages so in both projects, add a dependency on the org.eclipse.osgi plugin
In both projects, create (in the first package) an Activator class implementing BundleActivator.
Here's the class for org.naiade.messages :
package org.naiade.messages;
import org.naiade.messages.impl.HelloMessageProviderImpl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class MessagesActivator implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
context.registerService(HelloMessageProvider.class.getName(),
new HelloMessageProviderImpl(), null);
}
@Override
public void stop(BundleContext context) throws Exception {
}
}
Obviously what we're doing is telling the OSGI framework that we expose an implementation of HelloMessageProvider and that this implementation is the object
new HelloMessageProviderImpl(). Note that we don't do anything in the stop, that method is meant to perform cleanup of resources, closing connections etc (you know, for bundles that actually do something).
For the org.naiade.hello bundle :
package org.naiade.hello;
import org.naiade.messages.HelloMessageProvider;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
public class HelloActivator implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
ServiceReference ref = context
.getServiceReference(HelloMessageProvider.class.getName());
HelloMessageProvider messageProvider = (HelloMessageProvider) context
.getService(ref);
HelloSayer sayer = new HelloSayer();
sayer.setMessageProvider(messageProvider);
sayer.init();
}
@Override
public void stop(BundleContext context) throws Exception {
}
}
That code is requesting the context for a HelloMessageProvider and prints the provided message.
Reference the Activator for both projects in the first tab of MANIFEST.mf
Running the Bundle
Create a new Run configuration of type OSGI framework. What happens with the default target platform in Eclipse is that you are presented with all OSGI plugins of Eclipse itself to include or not in your build. Just uncheck all of the target platform for now and check your 2 own plugins, then run
You should be presented with a nice message
Springifying the bundles
While it's perfectly ok to use activators in order to publish and request services to and from the OSGI runtime, you are going to end up managing all the services by hand. That approach is to OSGI what the factory pattern by hand is to regular java. Hopefully if you are used to handling IOC with the Spring framework there is the Spring-Dynamic modules that (amongst other solutions) allows for
both declarative exposure of your Spring beans as OSGI services and dependecy injection of services grabbed from other OSGI bundles.
We're now going to see how you could implement the above projects using Spring-DM
Installing the platform
It is worth pointing that there are several ways to bootstrap Spring-DM projects, such as using maven archetypes, or installing Spring-IDE in Eclipse and using one of its predefined targets. However for the purpose of understanding what's going on, I like at least for the first time to try from scratch.
Download Spring-DM with dependancies from http://www.springsource.org/osgi. (lastest GA is spring-osgi-1.2.0 as of today) and unzip it somewhere.
Then go to the preferences of your eclipse, Plug-in Development - Target Platform, add a new platform of type "Nothing (Start with an empty definition)",
using the Locations tab, add 2 directories from where you installed Spring-osgi :
- spring-osgi-1.2.0/dist
- spring-osgi-1.2.0/lib
If you activate "Show Plug-in content", your target platform should now look like this
Writing the bundles contexts
First thing in Spring OSGI you won't need (at least not right now) to deal with Bundles activator. When the org.springframework.osgi.extender Bundle from Spring is started, it will listen for loading of all other bundles and manage them according to their context. Just delete both activators class and remove them from your bundle's MANIFESTs
We are now going to create in each bundles the directory META-INF/spring and
create there an applicationContext.xml. Note that you should be able to name the context anything, spring-osgi is going to try sourcing as context any xml file in
META-INF/spring. It is also possible to put it elsewhere than META-INF/spring but you would have to put that location as an attribute in the bundle MANIFEST.mf. We'll stick with the standard for now.
So the org.naiade.messages context is going to be :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">
<bean name="messageProvider" class="org.naiade.messages.impl.HelloMessageProviderImpl"
scope="prototype" />
<osgi:service id="messageProviderOsgi" ref="messageProvider"
interface="org.naiade.messages.HelloMessageProvider" />
</beans>
Notice that the MessageProvider bean is defined in the standard Spring way. The <osgi:service> part tells spring-osgi that this bean is to be exposed as a
org.naiade.messages.HelloMessageProvider service into the OSGI context.
You may note that Spring-osgi introduces a new namespace for osgi elements : http://www.springframework.org/schema/osgi. Spring context namespaces, which were introduced in Spring 2.0 to allow for spring contexts expansion and customization (things such as <util:list>, <context:component-scan> etc...) so that should make sense if you're already familiar with the concept.
Here is the context for the org.naiade.hello bundle :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">
<osgi:reference id="messageProvider" interface="org.naiade.messages.HelloMessageProvider" />
<bean id="helloSayer" class="org.naiade.hello.HelloSayer" scope="singleton"
init-method="init">
<property name="messageProvider" ref="messageProvider" />
</bean>
</beans>
The context is pretty straightforward : a reference to a org.naiade.messages.HelloMessageProvider is imported into that bundle, and that service is injected into the bean that's using it
Starting the SpringDM project
Go back to eclipse's preferences, Plug-in development - Target platform and make the spring target platform we defined earlier as default. Now if you've followed that tutorial from the beginning, your two bundles should complain. That's because Spring-osgi lib directory came with a version of Equinox (Eclipse's OSGI implementation) of 3.2.2. Just manually edit the bundle's manifest and remove the specific "3.5.0" version for the dependecy on equinox
Create a new OSGI Run Configuration by selecting your two bundles, the org.springframework.osgi.extender one, and clicking the add required bundles to
add only the extender's minimal set of dependencies.
Run it and you should see our Hello message !
to be improved and continued...





0 comments:
Post a Comment