Setting up

This set of instructions will take you through modifying and running the code in the tutorial. To run through the tutorial you will need:

Before beginning the tutorial code we need to check that you can run the OSGi platform and applications. We will refer to the directory that you extracted the zip or tar file in as [toplevel]. We will use Linux commands in the instructions - if you don't know what the equivalent Windows commands are please ask.

To check that you are able to run the OSGi sample code run these commands from a shell prompt:

cd [toplevel]/scripts
./start_platform.sh

You should see the Equinox OSGi console come up. The 'ss' command lists the state of the platform. Issue the 'ss' command:

osgi> ss
You should see 8 bundles (numbered 0-7).

Leave this running in one window as we will return to it later. Open a new window/shell, we will use the new shell to run some samples.

In the new shell, change directory to [toplevel]/bin and copy the following three files into the [toplevel]/dropins directory:

org.apache.aries.tutorials.blueprint.greeter.api-0.1-SNAPSHOT.jar
org.apache.aries.tutorials.blueprint.greeter.server.osgi-0.1-SNAPSHOT.jar
org.apache.aries.tutorials.blueprint.greeter.client.osgi-0.1-SNAPSHOT.jar

This installs those bundles in the OSGi framework. Back in the OSGi console window, type 'ss' again, you should see three new bundles in the 'INSTALLED' state. Start all three new bundles starting with the greeter.server.osgi bundle. To do this, issue the 'start' command followed by the bundle ID:

osgi> start [ID]

Where 'ID' is the number of the bundle.

When you start the greeter.client.osgi bundle you will see some output - we will come back to this later in the tutorial.

Back in the other shell, navigate to [toplevel]/dropins and delete everything - this will uninstall the bundles.

Finally check the OSGi console to confirm that the three bundles have gone by issuing another 'ss' command.

Later in the tutorial you will be using Maven to build the sample code.

Converting the OSGi Client.

The projects org.apache.aries.tutorials.blueprint.greeter.client.osgi, org.apache.aries.tutorials.blueprint.greeter.server.osgi and org.apache.aries.tutorials.blueprint.greeter.api together comprise a simple application demonstrating publication and consumption of an OSGi service. By following the steps below, you will gradually migrate the application to use blueprint, which will demonstrate how blueprint can help to simplify such projects.

In the next section ('Minimum conversion') we will go through the minimum number of steps necessary to convert the client code to use blueprint. In the section after that ('Finishing the client') we will go through some more extensive changes to the client which extend its use of blueprint.

Minimum conversion

In this section we will take the minimum number of actions to convert the greeter client application to use blueprint.

First, take a look at the the client class, the source is located here:

[toplevel]/source/org.apache.aries.tutorials.blueprint.greeter.client.osgi/src/main/java/org/apache/aries/tutorials/blueprint/greeter/client/osgi/ClientBundleActivator.java

This class (activator) does two things:

  1. Instantiates the test class (GreeterTestClient)
  2. Invokes the 'doRequests()' method from the GreeterTestClient class.

Removing the activator

In the following steps we will develop blueprint to carry out the tasks that are currently handled by the activator, the first step is to delete the activator class. For example, on linux:

rm [toplevel]/source/org.apache.aries.tutorials.blueprint.greeter.client.osgi/src/main/java/org/apache/aries/tutorials/blueprint/greeter/client/osgi/ClientBundleActivator.java

The next step is to edit the file [toplevel]/source/org.apache.aries.tutorials.blueprint.greeter.client.osgi/pom.xml, and remove the line:

<Bundle-Activator>org.apache.aries.tutorials.blueprint.greeter.client.osgi.ClientBundleActivator</Bundle-Activator>

Replacing the functionality of the activator with blueprint

If you were to rebuild the bundle at this point it would still build and deploy, but without the activator the test class would not be invoked and there would be no output when the client bundle was started. In this step we will use blueprint to replace what the activator was doing - creating an instance of the test class and calling one of its methods.

Start by adding an OSGI-INF/blueprint directory under the org.apache.aries.tutorials.blueprint.greeter.client.osgi/src/main/resources folder:

mkdir -p [toplevel]/source/org.apache.aries.tutorials.blueprint.greeter.client.osgi/src/main/resources/OSGI-INF/blueprint

In the newly created blueprint directory, create a blueprint.xml file containing the following blueprint 'template'.

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
</blueprint>

Note: The name of the file can be anything you like that ends in .xml - blueprint.xml is a popular choice.

Now, add a bean element to represent the test class; remember the activator was used to create an instance of this class, now we will use blueprint to do the same. Add the bean element shown below within the blueprint XML element.

<bean id="clientBean"
    class="org.apache.aries.tutorials.blueprint.greeter.client.osgi.GreeterTestClient"> </bean>

The blueprint container will attempt to instantiate the class (GreeterTestClient) with a zero argument constructor by default. However - there is no zero argument constructor in the GreeterTestClient class; the only constructor has the signature GreeterTestClient(BundleContext b, String clientID). We can tell blueprint to use this by adding some arguments inside the bean like this:

<argument ref="blueprintBundleContext"/>
<argument value="My blueprint client ID"/>

These arguments will be fed to the constructor. The string "My blueprint client ID" will be used directly, while the 'blueprintBundleContext' references a special bean - we are not going to worry about this now, it's beyond the scope of the tutorial.

If we were to build and deploy now, we'd discover the GreeterTestClient is constructed, but the 'doRequests' method is not invoked.

For this last part of the puzzle, we'll use the blueprint bean 'init-method' attribute, which specifies a method to be called after a bean is constructed. To use the 'init-method', add

init-method="doRequests"

to the bean element. You should now have something like this in your blueprint xml file:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
  <bean id="clientBean" class="org.apache.aries.tutorials.blueprint.greeter.client.osgi.GreeterTestClient"
      init-method="doRequests">
    <argument value="My blueprint client ID"/>
    <argument ref="blueprintBundleContext"/>
  </bean>
</blueprint>

Finally we need to build and re-deploy the client we have just changed.

To build the code, run the script build.sh (or build.bat) in the [toplevel]/scripts directory. The commands are:

./build.sh org.apache.aries.tutorials.blueprint.greeter.api
./build.sh org.apache.aries.tutorials.blueprint.greeter.client.osgi
./build.sh org.apache.aries.tutorials.blueprint.greeter.server.osgi

To run your sample, from the the [toplevel]/scripts directory:

./run.sh

this will copy the jar files generated in the build step to [toplevel]/dropins and hence install the application.

Now look back to the OSGi console, start the bundles (server first!), and check that they run. Clean the dropins directory again by removing all the files from it.

At this point we have a blueprint client running without a bundle activator. However, when we look at the content of the GreeterTestClient class, it's doing all sorts of OSGi service related things that blueprint could do much more simply. In the next section we will see how use of blueprint can lead to much a much cleaner service invocation.

Finishing the client

The client code (GreeterTestClient) was written to demonstrate a few common approaches to invoking an OSGi service. For example, there is a proxy class, hiding a tracker, which passes the requests on to the service (see inner class GreeterMessageServiceProxy). The really quite nasty 'look it up and use it all in one hit.. ' approach is demonstrated in the method doRequestUsingImmediateLookup().

Those experienced with OSGi will recognise the usual hidden selection of service usage bugs, for example, failing to unget the service in all cases, not handling situations when the service goes away after being looked up, etc.

Blueprint can help us avoid most of the common errors demonstrated in this code. So, let's get started and convert this client code to use blueprint.

Removing the old test implementations

The current contents of the client is written to show various OSGi ways of invoking a service. Since we're going to have blueprint take care of this for us, we can remove the old OSGi service API usage.

In the class:

[toplevel]/source/org.apache.aries.tutorials.blueprint.greeter.client.osgi/src/main/java/org/apache/aries/tutorials/blueprint/greeter/client/osgi/GreeterTestClient.java

Remove the following methods:

Also remove:

Now, you should have removed everything from the class except:

Preparing for blueprint

In this section we will add code that makes the client a lot more like a java-bean by adding a setter for the proxy and removing the old bundle context related elements from both the Java code and blueprint xml.

Continuing to edit the GreeterTestClient class, remove all but the first two lines of the 'doRequests' method, it should now just contain:

System.out.println("Proxy based...");
doRequestUsingProxy();

Now add a setter for the proxy,

public void setProxy(GreeterMessageService p){
  this.proxy = p;
}

and, since we're not using it any more, remove the BundleContext from the class constructor. Like this:

public GreeterTestClient(String clientID) {
  this.clientID = clientID;
}

Finally, save and close the GreeterTestClient class.

The last step is to modify the blueprint xml to reflect these changes. Edit the blueprint xml you created earlier:

[toplevel]/source/org.apache.aries.tutorials.blueprint.greeter.client.osgi/src/main/resources/OSGI-INF/blueprint/blueprint.xml

Remove the following line from the blueprint xml

<argument ref="blueprintBundleContext"/>

This ensures that the blueprint matches the modified constructor in the GreeterTestClient class.

Adding blueprint to the client

At this point we have code which will compile, but is not yet complete. We have a client that will instantly null pointer if we attempt to run it. Looking back at the GreeterTestClient class, when the doRequests() method is invoked by the init-method declaration in the blueprint xml, this in turn calls doRequestUsingProxy() which calls getGreetingMessage() on the proxy variable. However, this variable is initialised to null and never changed.

We need to make sure we set the 'proxy' variable. We can do this using blueprint!

Back in the xml file:

[toplevel]/source/org.apache.aries.tutorials.blueprint.greeter.client.osgi/src/main/resources/OSGI_INF/blueprint/blueprint.xml

Add a 'reference' element, that refers to the greeter service. To do this add the folowing xml inside <blueprint> but not inside the <bean> element.

<reference id="greeterService" interface="org.apache.aries.tutorials.blueprint.greeter.api.GreeterMessageService"
  timeout="10000" availability="mandatory"/>

This sets up a blueprint component with the ID 'greeterService', which will appear to its users as an instance of the interface specified, with its implementation being backed by an OSGi service claiming to meet that interface.

Note: This is the most simple possible blueprint service - there are many more powerful ways to use services within blueprint.

Notice that we added the 'availability="mandatory"' attribute. This will ensure that the client will not start if if there are no services available implementing the interface. If you started the client before the service in any of the previous steps you would have seen a stack trace, making the service mandatory avoids this issue.

The last step is to inject the 'greeterService' reference into our test client using a property element. Add this line inside the bean definition

<property name="proxy" ref="greeterService"/>

This will invoke the 'setProxy' method we added earlier and pass in a blueprint service proxy, hooked up to any OSGi service implementing the GreeterMessageService.

The complete blueprint.xml file should now look like this:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
  <reference id="greeterService" interface="org.apache.aries.tutorials.blueprint.greeter.api.GreeterMessageService"
    timeout="10000" availability="mandatory"/>
  <bean id="clientBean" class="org.apache.aries.tutorials.blueprint.greeter.client.osgi.GreeterTestClient"
      init-method="doRequests">
    <argument value="My blueprint client ID"/>
    <property name="proxy" ref="greeterService"/>
  </bean>
</blueprint>

That is all we need to do to the client. At this stage, rebuild the client as you did at the end of the minimum conversion and watch it running in the OSGi console. The steps are repeated here for convenience.

To build the code, run the script build.sh (or build.bat) in the [toplevel]/scripts directory. If you are using your own Maven repository the commands are:

./build.sh org.apache.aries.tutorials.blueprint.greeter.client.osgi

To run your sample, from the the [toplevel]/scripts directory:

./run.sh

this will copy the jar files generated in the build step to [toplevel]/dropins and hence install the application.

Now look back to the OSGi console, start the bundles (server first!), and check that they run. Clean the dropins directory again by removing all the files from it.

We're done with the client, and it's time to turn our attention to the server side of things. Converting the server is very similar to converting the client, but a lot simpler, as the only OSGi related bits in the current server are limited to instantiating and registering the service.

Converting the OSGi service

The service creation and registration is once again being handled by a Bundle Activator, this time it's at..

[toplevel]/org.apache.aries.tutorials.blueprint.greeter.server.osgi/src/main/java/org/apache/tutorials/blueprint/greeter/server/osgi/ServiceRegisteringActivator.java

Have a quick look at what this class is doing, make a quick note of how it creates and initialises the service implementation, and how it manages the service registration.

Delete the class. We'll be replacing it entirely with a blueprint version.

Start by adding an OSGI-INF/blueprint directory to the source/org.apache.aries.tutorials.blueprint.greeter.server.osgi/src/main/resources folder.

In the blueprint directory, create an xml file called blueprint.xml.

In the xml file, add the main blueprint element and usual xml boilerplate.

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
</blueprint>

Let's start by having blueprint create the instance of the implementation of our service. Add a bean element with the service implementation class "GreeterMessageServiceImpl" as its target class.

<bean id="serverBean" class="org.apache.aries.tutorials.blueprint.greeter.server.osgi.GreeterMessageServiceImpl"> </bean>

Now we need to initalise it, just as the original activator did. The activator set the sender id in the Service by using the 'setSender' method. We can have blueprint do this for us with a property element.

<property name="sender" value="Blueprint Greeting Service"/>

Great... we have a server bundle, which if built and deployed, would create an implementation of the service and set the sender id on it. Unfortunately, no-one would be able to see it as we have not published it to the OSGi registry. The blueprint '<service>' element is required to publish the service:

<service id="greeterService"
  interface="org.apache.aries.tutorials.blueprint.greeter.api.GreeterMessageService"
  ref="serverBean">
</service>

Putting it all together, you should have ended up with something a little like this...

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
  <bean id="serverBean" class="org.apache.aries.tutorials.blueprint.greeter.server.osgi.GreeterMessageServiceImpl">
      <property name="sender" value="Blueprint Greeting Service"/>
  </bean>

  <service id="greeterService"
      interface="org.apache.aries.tutorials.blueprint.greeter.api.GreeterMessageService"
    ref="serverBean">
    </service>
</blueprint>

Finally, before you rebuild, you will need to edit the file:

[toplevel]/source/org.apache.aries.tutorials.blueprint.greeter.server.osgi/pom.xml

and remove the line:

<Bundle-Activator>org.apache.aries.tutorials.blueprint.greeter.server.osgi.ServiceRegisteringActivator</Bundle-Activator>

When you build and deploy the server bundle now, we should be able to see it all working as expected again.

Congratulations! You have completed the final step in converting the greeter application to use blueprint.