Skip to content

ComponentProvider Tutorial

jawi edited this page Dec 24, 2011 · 1 revision

Component Provider tutorial

This tutorial will explain all necessary steps on how to implement your own menu for the OLS client. It will describe how to add a Hello World menu, with a say it menu item to the main menu bar of the OLS client. Upon clicking the say it menu item, an obvious, but friendly message will be shown inside a dialog.

The intended audience of this tutorial are people with basic knowledge about Java (i.e.: you know how to create a class and have a basic understanding of the language itself). The level of this tutorial is beginner.

Last modified on December 24th, 2011. Status: work in progress.

The ComponentProvider

The main part of this tutorial will be the implementation of the nl.lxtreme.ols.api.ui.ComponentProvider interface. Implementations of this interface are supposed to keep a reference to the provided component, and return this same component during the lifetime of the implementation.

As mentioned, this tutorial is about implementing a Hello World menu, with a say it menu item. So, lets present our (simple) ComponentProvider instance:

  package ols.tutorial;
  
  import nl.lxtreme.ols.api.ui.ComponentProvider;
  import java.util.Date;
  import java.awt.event.*;
  import javax.swing.*;
  
  public class HelloWorldMenuProvider implements ComponentProvider {
    private JMenu component;
    
    public void addedToContainer() {
      // Nothing
    }
    
    public JComponent getComponent() {
      if (component == null) {
        // lazy create the returned instance...
        component = createMenu();
      }
      return component;
    }
    
    public void removedFromContainer() {
      // Nothing
    }
    
    private JMenu createMenu() {
      JMenu result = new JMenu("Hello World");
      JMenuItem item = new JMenuItem("say it...");
      result.add(item);
      item.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent event) {
          String msg = "Hello World! It is now: " + new Date();
          JOptionPane.showMessageDialog(null, msg);
        }
      });
      return result;
    }
  }

The class ols.tutorial.HelloWorldMenuProvider presented above is a simple implementation of the ComponentProvider interface. Both the addToContainer and removedFromContainer methods are empty (stub) methods, while the getComponent lazily creates its return value before returning it. The real "meat" of this class is in the createMenu method, which is responsible for creating the "Hello World" menu and adding a "say it..." menu item to this menu. When a user selects the "say it..." menu item, the added ActionListener is called. The actionPerformed method, in its turn, crafts a simple String-message and displays this message in a (Swing) dialog on screen.

The Manifest

With the creation of the HelloWorldMenuProvider class, you almost done already. The only thing left, is to provide some additional metadata for the OLS client to recognize this class as valid extension point. This metadata is provided by means of a so-called manifest, which is added by default for each created JAR-file. For the component provider as denoted in this tutorial, the following manifest is sufficient (it should be placed in META-INF/MANIFEST.MF):

  Manifest-Version: 1.0
  Bundle-Name: Hello World Menu Bundle
  Bundle-SymbolicName: ols.tutorial.helloworld
  Bundle-Version: 1.0.0
  Import-Package: nl.lxtreme.ols.api.ui,javax.swing
  OLS-ComponentProvider: Menu
  OLS-ComponentProviderClass: ols.tutorial.HelloWorldMenuProvider

The Manifest-Version item is mandatory and fixed to the value "1.0". The last two lines are important, as they are required for the OLS client to pick up your extension point in the correct way. The OLS-ComponentProvider entry describes to the client that you're providing a new menu component, while the OLS-ComponentProviderClass denotes the complete class name of your ComponentProvider implementation (ols.tutorial.HelloWorldMenuProvider in this case). The other entries are needed for OSGi, and can be ignored for now.

Putting it together

The OLS client defines a small API with some common base classes to be used by extension points. All of these classes are needed in order to succesfully compile your code and create a valid extension point out of it. These classes are available in the API-package: an archive named like: ols-X.Y.Z-api.zip (where 'X.Y.Z' denote the main version of the used API). This archive provides you with all JAR-files that you might need for your extension point. You need to add those JAR-files to the classpath of your Java compiler in order to succesfully build your extension point. As of Java 6, this can easily be done in the following way (note the wildcard):

  javac -cp "/path/to/ols-X.Y.Z/lib/*" ols/tutorial/HelloWorldMenuProvider.java

The API (version 0.9.4) currently consists of the following JAR-files:

  • api-1.0.4.jar, the main API classes, '''always''' needed to compile your extension point;
  • util-1.0.5.jar, some common utility classes, provided for convenience, ''optionally'' needed;
  • base-1.0.0.jar, provides some base classes for creating new tools, ''optionally'' needed, unless you're building a new tool;
  • test.util-1.0.0.jar, provides some testing utilities, ''optionally'' needed;
  • org.osgi.compendium-4.2.0.jar, provides the OSGi compendium classes, which are all services of OSGi that are optional;
  • org.osgi.core-4.2.0.jar, provides the core OSGi classes, which are needed if you want to interact with the OSGi framework.

Maven

While it is relatively easy to compile and craft your own extension point bundle, it can be easier to make use of build tooling, such as Maven to do this for you. Maven works with certain conventions, and as long as you stick to these conventions, Maven provides you powerful tooling that "simply works". All configuration for Maven is placed in a so-called POM-file, which explains several things:

  • The name and version of the project itself;
  • The dependencies needed to build the project;
  • How the resulting artifact should be created.

For our HelloWorldComponentProvider implementation, the corresponding pom.xml file looks like:

  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <name>OLS Component Provider Tutorial</name>
    <groupId>nl.lxtreme.ols.test</groupId>
    <artifactId>ols.componentprovider.tutorial</artifactId>
    <packaging>bundle</packaging>
    <version>1.0.0</version>
    <dependencies>
     <dependency>
        <groupId>nl.lxtreme.ols</groupId>
        <artifactId>api</artifactId>
        <version>1.0.4</version>
        <type>jar</type>
        <scope>compile</scope>
     </dependency>
     <dependency>
        <groupId>nl.lxtreme.ols</groupId>
        <artifactId>util</artifactId>
        <version>1.0.5</version>
        <type>jar</type>
        <scope>compile</scope>
     </dependency>
     <dependency>
        <groupId>org.osgi</groupId>
        <artifactId>org.osgi.core</artifactId>
        <version>4.2.0</version>
        <type>jar</type>
        <scope>compile</scope>
     </dependency>
     <dependency>
        <groupId>org.osgi</groupId>
        <artifactId>org.osgi.compendium</artifactId>
        <version>4.2.0</version>
        <type>jar</type>
        <scope>compile</scope>
     </dependency>
       <dependency>
        <groupId>nl.lxtreme.ols</groupId>
        <artifactId>test.util</artifactId>
        <version>1.0.0</version>
        <type>jar</type>
        <scope>test</scope>
     </dependency>
     <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.8.2</version>
        <type>jar</type>
        <scope>test</scope>
     </dependency>
    </dependencies>
    <build>
       <plugins>
          <plugin>
             <groupId>org.apache.felix</groupId>
             <artifactId>maven-bundle-plugin</artifactId>
             <version>2.3.4</version>
             <extensions>true</extensions>
             <configuration>
                <instructions>
                   <OLS-ComponentProvider>Menu</OLS-ComponentProvider>
                   <OLS-ComponentProviderClass>ols.tutorial.HelloWorldMenuProvider</OLS-ComponentProviderClass>
                </instructions>
              </configuration>
           </plugin>
        </plugins>
    </build>
  </project>

In order to "comply" to the Maven conventions, you should set up your directory hierarchy as follows:

  ./
  +- src/main/java
  |   + ols/tutorial
  |       + HelloWorldMenuProvider.java
  `- pom.xml

With this all in place, the only thing needed is a mvn clean install in the top-level directory (./), and wait for this command to finish. The first time you run this command, it takes a while because it needs to download some stuff from the Internet. The result of this command should be a (newly) created target folder, containing (among others) the file ols.componentprovider.tutorial-1.0.0.jar, which is your newly created extension point!

Running

In order for the OLS client to pick up your extension point, it need to be copied to your plugins directory of your OLS-client installation. This can be done while the client is running, so you do not need to restart it if already running. If everything went ok, after a few seconds, the log output of the OLS client should display something like:

  [4/12/11 12:51:59 PM - INFO  - util.osgi.BundleServiceObserver]: New service (ols.tutorial.HelloWorldMenuProvider) registered ...

And the new menu should appear in your client's main menu bar. Selecting the "say it" menu item from this menu should greet you with a world-famous quote.

If you remove the same JAR-file from the plugins folder, the client should remove the menu from the menu bar, and your log output should display something like:

  [4/12/11 1:29:20 PM - INFO  - util.osgi.BundleServiceObserver]: Service (ols.tutorial.HelloWorldMenuProvider) unregistered ...

Now, go on and let your creativity flow...