Red Hat Training

A Red Hat training course is available for Red Hat Fuse

2.8. Consumer Bundle

Overview

This section explains how to set up a Maven project for a consumer bundle.
The hello-consumer bundle exemplifies a consumer bundle, which imports OSGi services and accesses the services through the relevant API packages. This is the bundle that drives the sample application and, therefore, it relies on a blueprint lifecycle callback (through the blueprint bean element's init-method attribute) to initiate processing.
Note
Being a consumer is just a role, not an absolute category, so you will commonly come across bundles that behave both as a consumer and a provider. In the current example, however, hello-consumer behaves as a pure consumer.

Directory structure

The hello-consumer bundle has the following directory structure:
hello-consumer/
  |
  \--src/
     |
     \--main/
     |  |
     |  \--java/
     |  |  |
     |  |  \--org/fusesource/example/hello/consumer/
     |  |     |
     |  |     \--ConsumeHello.java
     |  |
     |  \--resources/
     |     |
     |     \--OSGI-INF/blueprint/
     |        |
     |        \--client.xml
     |   
     \--test/
The org.fusesource.example.hello.consumer package is public and all of its classes and interfaces are exported from the bundle. It would not matter, however, if you made this package private instead, because it is not needed by any other bundles.
The src/main/resources/OSGI-INF/blueprint directory contains a single blueprint file, client.xml. Any file matching the pattern, *.xml, in this directory is assumed to be a blueprint configuration file.

Sample consumer code

The hello-consumer bundle effectively drives the sample application, obtaining references to the HelloBoston and HelloParis OSGi services, and then invoking methods on these services to obtain localised greetings and times.
The hello-consumer bundle contains the class, ConsumeHello, which is a client of the OSGi services, HelloBoston and HelloParis. To gain access to the OSGi services, ConsumeHello defines the setter methods, getHelloBoston() and getHelloParis(), and relies on the blueprint framework to inject the references. The entry point is the init() method, which gets invoked after the ConsumeHello bean is created and injected with the service references. The ConsumeHello class is defined as follows:
// Java
package org.fusesource.example.hello.consumer;

import org.fusesource.example.hello.boston.HelloBoston;
import org.fusesource.example.hello.paris.HelloParis;

public class ConsumeHello {
    protected HelloBoston helloBoston = null;
    protected HelloParis helloParis = null;

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }
    
    public void init() {
        if (helloBoston==null || helloParis==null) {
            System.out.println("Initialization failed. Injected objects are null.");
            return;
        }
        
        String enGreeting = helloBoston.getGreeting();
        String bostonTime = helloBoston.getLocalTime().getLocalTime();
        System.out.println("Boston says:" + enGreeting + " at " + bostonTime);
        
        String frGreeting = helloParis.getGreeting();
        String parisTime = helloParis.getLocalTime().getLocalTime();
        System.out.println("Paris says:" + frGreeting + " at " + parisTime);
    }

    public HelloBoston getHelloBoston() {
        return helloBoston;
    }

    public void setHelloBoston(HelloBoston helloBoston) {
        this.helloBoston = helloBoston;
    }

    public HelloParis getHelloParis() {
        return helloParis;
    }

    public void setHelloParis(HelloParis helloParis) {
        this.helloParis = helloParis;
    }
    
}

Access OSGi service

The ConsumeHello class needs to obtain a reference to the HelloBoston service and a reference to the HelloParis service. Use the reference element to create proxies for the HelloBoston service and for the HelloParis service. Use the bean element to create a ConsumeHello instance and inject the helloBoston and helloParis proxies.
The ConsumeHello bean also requires an entry point to initiate processing. By setting the bean element's init-method attribute to init, you ensure that the blueprint framework calls the ConsumeHello.init() method after all of the bean's properties have been injected.
For example, the blueprint file, OSGI-INF/blueprint/client.xml, has the following contents:
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">

    <reference id="helloBoston"
        interface="org.fusesource.example.hello.boston.HelloBoston"/>

    <reference id="helloParis"
        interface="org.fusesource.example.hello.paris.HelloParis"/>

    <bean id="client"
          class="org.fusesource.example.hello.consumer.ConsumeHello"
          init-method="init">
        <property name="helloBoston" ref="helloBoston"/>
        <property name="helloParis" ref="helloParis"/>
    </bean>

</blueprint>

Maven dependencies

In the Maven POM file, the hello-consumer bundle defines dependencies on the following Maven artifacts:
  • time-util
  • hello-paris
  • hello-boston

Import and export rules

The following import and export rules apply to the hello-consumer bundle:
  • Exporting own packages—a client typically does not need to export its own packages, because a client does not usually expose an API.
  • Importing own packages—a client does not import its own packages.
  • Importing dependent packages—any external package dependencies must be imported.

Maven bundle plug-in settings

The Maven bundle plug-in is configured to export the package, org.fusesource.example.hello.consumer, although the export is unnecessary in this particular example. The Export-Package instruction also contains entries to block the export of any packages containing .impl or .internal. In this case, the bundle plug-in instructions are as follows:
<instructions>
  <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
  <Import-Package>*</Import-Package>
  <Export-Package>
    !${project.groupId}*.impl*,
    !${project.groupId}*.internal*,
	${project.groupId}.hello.consumer*;version=${project.version}
  </Export-Package>
</instructions>

Generated MANIFEST.MF file

When you build the bundle using Maven, the Maven bundle plug-in automatically generates the following MANIFEST.MF file:
Manifest-Version: 1.0
Built-By: JBLOGGS
Created-By: Apache Maven Bundle Plugin
Import-Package: org.fusesource.example.hello.boston;version="[1.0,2)",
 org.fusesource.example.hello.paris;version="[1.0,2)",org.fusesource.e
 xample.time;version="[1.0,2)",org.osgi.service.blueprint;version="[1.
 0.0,2.0.0)"Bnd-LastModified: 1296826333723
Export-Package: org.fusesource.example.hello.consumer;uses:="org.fuses
 ource.example.time,org.fusesource.example.hello.paris,org.fusesource.
 example.hello.boston";version="1.0"
Bundle-Version: 1.0.0
Bundle-Name: hello-consumer
Build-Jdk: 1.5.0_08
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.fusesource.example.hello-consumer
Tool: Bnd-1.15.0
Import-Service: org.fusesource.example.hello.boston.HelloBoston,org.fu
 sesource.example.hello.paris.HelloParis
The Import-Package header imports the external package dependencies—for example, org.fusesource.example.hello.boston.
The Export-Package header exports the package, org.fusesource.example.hello.consumer. In this case, however, the export is not really needed and the package could have been declared private instead (for example, using the Private-Package instruction).
The Import-Service header declares the OSGi services accessed by this bundle. The services are accessed respectively through the HelloBoston interface and through the HelloParis interface.