This is an old revision of the document!
Table of Contents
SensorWeaver Developer Guide
SensorWeaver is Java software. Middleware components are available as OSGi bundles, available publicly in Maven repositories. This guide shows how to use SensorWeaver Middleware in your software projects.
General requirements:
- Knowledge of Java, OSGi, Maven.
- Java Development Kit 7
- An IDE, preferably with Maven support
- For testing/deployment
A project containing most of the examples shown in this guide can be downloaded at this location
Development environment setup
You'll need Java 8 JDK to compile and run your project. Download and install one for your system.
SensorWeaver components are distributed using Maven WNLab repositories.
In order to access released and development JARs you need to configure your development environment.
The best way to achieve this is to add third-party repos to your global maven settings.xml in the default profile. This removes the need of configuring each maven project.
Configure your Maven settings.xml
according to the following:
- settings.xml
<profiles> <profile> <id>wnlab</id> <repositories> <!-- WNlab Repos --> <repository> <id>wnlab-snapshots</id> <url>http://ala.isti.cnr.it:8081/nexus/content/repositories/wnlab-snapshots</url> <releases> <enabled>false</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>warn</checksumPolicy> </releases> <snapshots> <enabled>true</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>fail</checksumPolicy> </snapshots> </repository> <repository> <id>wnlab-releases</id> <url>http://ala.isti.cnr.it:8081/nexus/content/repositories/wnlab-releases</url> <releases> <enabled>true</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>warn</checksumPolicy> </releases> <snapshots> <enabled>fail</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>fail</checksumPolicy> </snapshots> </repository> </repositories> </profile> </profiles> <activeProfiles> <activeProfile>wnlab</activeProfile> </activeProfiles>
As an alternative, you can include just the repositories tag listed above, inside of your Maven project pom.xml.
Maven repositories include documentation and source code artifacts.
Source code can be accessed in read-only in WNLab Subversion repository if needed.
Project setup
Create a Maven project using the IDE of choice.
The project must generate an OSGi bundle, so:
- Set packaging type to bundle
- Set Java 7 compiler with maven compiler plugin
- Include the maven bundle plugin in order to produce a jar bundle with OSGi manifest.
- pom.xml
<packaging>bundle</bundle> <!-- Other declarations --> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.3.7</version> <extensions>true</extensions> <configuration> <instructions> <Bundle-Name>${project.name}</Bundle-Name> <Bundle-Description>${project.description}</Bundle-Description> <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName> <Bundle-Provider>Your Company Name</Bundle-Provider> <!-- Here you can put other manifest headers --> </instructions> </configuration> </plugin> </plugins> </build>
Tip: You can find a list of manifest headers on the OSGI wiki
Then add the middleware-api dependency to your project pom.xml:
- pom.xml
<dependencies> <dependency> <groupId>it.cnr.isti.sensorweaver.middleware</groupId> <artifactId>middleware-api</artifactId> <version>2.0.0</version> </dependency> </dependencies>
Tip: Check the latest middleware-api version on WNLab Nexus Repo or use maven version ranges to be up to date with the latest API.
Getting an instance of the Middleware service
Inside of the OSGi runtime environment, the Middleware stands as an OSGi service. Clients that needs Middleware services have to obtain an instance through the OSGi registry. There are several ways to achieve this. Here we list two solutions:
- Querying the service registry programmatically using BundleActivator
- Blueprint
Bundle Activator
You'll need OSGi dependencies in your pom:
- pom.xml
<dependency> <groupId>org.osgi</groupId> <artifactId>org.osgi.core</artifactId> <version>4.3.1</version> </dependency>
Then create a java class for your bundle activator:
- MySensorPublisherActivator.java
package it.cnr.isti.sample; import it.cnr.isti.sensorweaver.middleware.api.Middleware; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; public class MySensorPublisherActivator implements BundleActivator { private ServiceReference<Middleware> middlewareReference; private MySensorPublisher publisher; public void start(BundleContext context) throws Exception { middlewareReference = context.getServiceReference(Middleware.class); Middleware middleware = context.getService(middlewareReference); //Middleware instance can be passed to other components at this point publisher = new MySensorPublisher(middleware); publisher.start(); } public void stop(BundleContext context) throws Exception { publisher.stop(); context.ungetService(middlewareReference); } }
Declare your bundle activator in your Manifest by configuring the maven bundle plugin:
- pom.xml
<plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.3.7</version> <extensions>true</extensions> <configuration> <instructions> <!-- ... Other manifest headers ... --> <Bundle-Activator>it.cnr.isti.sample.MySensorPublisherActivator</Bundle-Activator> </instructions> </configuration> </plugin>
Blueprint
Blueprint ease the components wiring task and removes the need of having OSGi dependencies in the project. In order to use blueprint the developer must take into account that a blueprint specification implementation will be required at runtime. Middleware 2.0 is based on blueprint, therefore a blueprint implementation is needed anyhow.
You won't need OSGi dependencies in your pom, so remove them if unused.
Let's assume that you want to wire the Middleware instance with your component, MySensorPublisher
, when the bundle starts.
- MySensorPublisher.java
package it.cnr.isti.sample; import it.cnr.isti.sensorweaver.middleware.api.Middleware; public class MySensorPublisher { private Middleware middleware; public MySensorPublisher(Middleware middleware) { this.middleware = middleware; } public void start() { // This method is called when the bundle starts, your application logic starts here. } public void stop() { // This method is called when the bundle stops, use it to clean resources, close connections, etc. } }
Assuming that you're following Maven conventions on project structure, create the folder src/main/resources/OSGI-INF/blueprint
.
Create a services.xml
file inside of this folder with this content:
- services.xml
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0 http://aries.apache.org/schemas/blueprint-cm/blueprint-cm-1.1.0.xsd" default-activation="lazy"> <reference id="middleware" interface="it.cnr.isti.sensorweaver.middleware.api.Middleware" availability="mandatory" timeout="10000" /> <bean id="mySensorPublisher" class="it.cnr.isti.sample.MySensorPublisher" activation="eager" init-method="start" destroy-method="stop" depends-on="middleware" > <argument ref="middleware" /> </bean> </blueprint>
This configuration basically tells the blueprint runtime to
- create an instance of
MySensorPublisher
when the bundle starts (activation=eager) by wiring it with a proxy reference of the Middleware using the constructor - call the method
start
of the instance when the bundle is started and the bean is initialized - call the method
stop
of the instance when the bundle gets stopped
That is enough to automatically manage the lifecycle and wiring of your component. For additional documentation on blueprint you can take a look at Apache Aries or the OSGi compendium specifications for OSGi 4.x.x.
API and usage
Middleware service methods executes asynchronous tasks when called. Whenever a method, is called a Future
object is returned (documentation).
This solution removes the need to implement callbacks for every method call, improving code readability.
The client can synchronize with the end of the middleware task by calling the get
method on the Future
instance, retrieving a result of the computation, when expected, or an asynchronous task execution exception.
Here is the Middleware API:
package it.cnr.isti.sensorweaver.middleware.api; public interface Middleware { public Future<DataFeedClient> announceDataFeed(DataFeedDescriptor descriptor); public Future<RegistrationToken> registerDiscoverer( DataFeedDiscoverer discoverer); public Future<RegistrationToken> registerListener(DataFeedListener listener); }
announceDataFeed
allows to announce the availability of a new data feed on the Middleware. It returns, asynchronously, aDataFeedClient
instance that is used for sending data feed messages and unregister the datafeed.registerDiscoverer
allows to register aDataFeedDiscoverer
instance, a callback object that is notified about lifecycle events related to a set of data feeds.registerListener
allows to register aDataFeedListener
instance, a callback object that is notified each time a message is produced by adata feed
The complete set of API can be found in the middleware-api bundle.
Publish a Data Feed
In order to announce a data feed, you have to describe:
- A set of properties (e.g.: hardware identification of sensor, monitored feature, etc.)
- Message elements that will be contained in each data feed message
You can use a DataFeedDescriptor
builder in order to accomplish this, like in the following example:
import it.cnr.isti.sensorweaver.middleware.api.datafeed.descriptor.DataFeedDescriptor; import it.cnr.isti.sensorweaver.middleware.api.datafeed.descriptor.DataFeedBuilder; import it.cnr.isti.sensorweaver.middleware.api.common.descriptor.Property; /** ... **/ private DataFeedDescriptor buildDescriptor() { DataFeedBuilder builder = DataFeedDescriptor.builder(); builder.property("hardwareId", "ABCD1234"); builder.property("sensorType", "temperatureSensor"); builder.parameter("temperature", new Property("Unit of Measure","Celsius")); builder.parameter("timestamp", new Property("Format","ISO8601")); DataFeedDescriptor descriptor = builder.build(); return descriptor; }
The descriptor can then be used to announce the availability of the datafeed to other distributed Middleware clients:
import it.cnr.isti.sensorweaver.middleware.api.datafeed.DataFeedClient; /** ... **/ private void announceSensor() throws ExecutionException, InterruptedException { DataFeedDescriptor descriptor = buildDescriptor(); DataFeedClient dataFeedClient = middleware.announceDataFeed(descriptor).get(); }
Publish Data Feed data
Once the client has a DataFeedClient
it can start publishing messages, like in the following example:
import it.cnr.isti.sensorweaver.middleware.api.common.MessageBuilder; /** ... **/ private void sendMessage() { MessageBuilder messageBuilder = dataFeedClient.buildMessage(); messageBuilder.entry("temperature", Integer.toString(datasource.getValue())); messageBuilder.entry("timestamp", DateTime.now().toString()); messageBuilder.send(); }
In order to publish a message for a given data feed you must produce values for all of the message elements defined previously in the data feed descriptor.
Unregistering a Data Feed
To unregister a data feed
you just have to call the method unregister
on the related DataFeedClient
.
private void unregister() throws InterruptedException, ExecutionException { dataFeedClient.unregister().get(); }
The operation notifies components listening to lifecycle events of the data feed that it went down. Sending new messages with the same data feed client will result in a runtime error.
Discover Data Feeds
In order to discover new data feeds you need to define a DataFeedDiscoverer
first, like the following:
import it.cnr.isti.sensorweaver.middleware.api.datafeed.DataFeedDiscoverer; /** ... **/ public class TemperatureSensorDiscoverer implements DataFeedDiscoverer { public void onFound(DataFeedDescriptor descriptor) { startListening(descriptor); } public void onRemoved(DataFeedDescriptor descriptor) { stopListening(descriptor); } public DescriptorFilter getFilter() { return new DescriptorFilter(new Property("sensorType", "temperatureSensor")); } /** other methods **/ }
The discoverer provides a filter which tells the Middleware about which data feeds the discoverer has to be notified on data feed lifecycle events. The filter matches with announced data feed descriptors that have the same property values.
To activate a discoverer you must register it on the Middleware;
Registration discovererToken = middleware.registerDiscoverer(myDiscoverer).get();
The registration of a discoverer returns a token which can be used later for unregistration:
discovererToken.unregister().get();
Listening to data feeds
In order to start listening for a data feed the middleware client must provide a DataFeedListener
.
Each listener will listen to messages sent by a specific data feed.
RegistrationToken registrationToken; /** ... **/ private void startListening(DataFeedDescriptor descriptor) throws InterruptedException, ExecutionException { registrationToken = middleware.registerListener(new LoggingListener(descriptor)).get(); } private void stopListening() throws InterruptedException, ExecutionException { registrationToken.unregister().get(); }
The same unregistration mechanisms of DataFeedDiscoverer
applies to DataFeedListener
.
Testing your app
Applications can be easily tested in the Karaf OSGi environment, using provided logging and debugging tools.
SensorWeaver provides a preconfigured Apache Karaf Bundle that you can use for development purposes.
Alternatively you can download your preferred Apache Karaf release and use the middleware-dev
feature from the SensorWeaver karaf feature repository to start up your container.
Configuration coming with both solutions assume that you have an MQTT broker online on port 1883 on your local machine.
Please refer to the Administrator Guide in order to setup and configure your OSGI environment.