Tuesday, June 16, 2009

VisualVM OSGi plugin

I wrote a simple OSGi management console as a VisualVM plugin, based on a concept similar to the OSGi JConsole plugin that my advisor asked me to do last year.
In this post I will describe the main steps for constructing the OSGi bundle and the VisualVM plugin, including the usage of a table from the JFluid API (so you plugin can look pretty much like the standard VisualVM stuff) and how simple it is to use JMX from your VisualVM plugin.

If you want to skip the development steps, you can install and try the plugin by downloading the built sfuff here.

If you want to check the step-by-step, please continue reading.

There is not too much advanced stuff here, but there are three pre-requisites for a complete understanding of this post:
- A minimum of familiarity with VisualVM plugin development (You may want to try the Hello VisualVM plugin before)
- Basic JMX knowledge (JMX Tutorial here)
- Basic OSGi knowledge (This main purpose of the plugin is not very useful to you if you don’t use OSGi, but you still can check out the JMX + VisualVM stuff).

I’ll show in this post:
- Brief description of the plugin functionality
- How I used Netbeans to create an OSGi bundle exposing some management functionality via JMX
- An overview of how I developed VisualVM plugin to access the JMX stuff above for accessing/managing OSGi applications
- Some of my findings that I’ve think useful to describe here

What I’ve used to develop, build and run the code show here:
1. Plugin Functionality Overview
This plugin can be an alternative to the OSGi consoles that exist, or yet another toy for your VisualVM plugin set.
The functionality of the plugin (screenshot below) is simple:
  • Listing of all bundles in an OSGi application
  • Installation of bundles
  • Perform a start/stop/update/uninstall in a bundle from a list
  • Visualize bundle information (Manifest headers, list of services, navigate through bundle contents).
2. Accessing OSGi via JMX
In order to have a plugin in another process (the VisualVM) to access the OSGi platform, we needed to expose some interfaces for that. JMX is an easy way to expose management interfaces for Java applications, and there is built-in support in the VisualVM for using it.

In this section I describe the development of the OSGi bundle that exposes this funcionality.

I created a Netbeans project as a Java class library (I have called mine OSGiJMX)
- Next I needed to reference a jar with the OSGi classes.
- Right click on the project, go to project properties > libraries > add libraries, I needed to create a new one pointing to the felix.jar which contained the needed OSGi API
- OSGi bundles need special manifest attributes. So I’ve added my own manifest to src/META-INF/MANIFEST.MF with the following content:
Manifest-Version: 1.0
Bundle-Version: 1.0.0
Bundle-Name: JMX OSGi
Bundle-Description: A JMX OSGi Example
Bundle-ManifestVersion: 2
Bundle-Vendor: Kiev Gama
Bundle-SymbolicName: osgijmx
Bundle-Activator: osgijmx.impl.Activator
Import-Package: org.osgi.framework,javax.management
Export-Package: osgijmx, osgijmx.impl

- I needed to change the project ant build file so it would use my manifest instead of the default one. On the files tab of your project (usually on the left side of the Netbeans IDE), open the build.xml
<import file="nbproject/build-impl.xml"/>
<!--ADD LINE BELOW -->
<property name="manifest.file" value="src/META-INF/MANIFEST.MF"/>


Basic operations should be made available via JMX MBeans, so our VisualVM plugin can access information concerning an executing OSGi platform and its bundles.
The MBean that my advisor provided me for the JConsole plugin previously mentioned could be used, but it was a single MBean instance that mixed bundles and platform methods. At that time a friend from my lab suggested to create an MBean for each bundle instance. That’s what I finally did now. So, I’ve defined two MXBean interfaces:
  • One for the OSGi Platform methods (kinda like a proxy to BundleContext which gives access to the OSGi platform)
  • Another for bundle related methods (worked like a proxy to a Bundle instance).
The code for the interfaces:
BundleMXBean and OSGiPlatformMXBean
The code of their implementation:
BundleMXBeanImpl and OSGiPlatformMXBeanImpl
the bundle Activator

The OSGiPlatformMXBean will hold a list to all Bundle MXBean instances (thanks to Eammon McManus’ blog).
OSGi is a dynamic platform: bundles can change their state, new services may arrive, etc. So, this JMX bundle should listen to Bundle events in order to add or remove the bundle MBeans in case of bundle installation or uninstallation, respectively, as well as notify (via JMX) any changes of bundle state so the VisualVM plugin GUI could be updated and always have consistent information. Currently the code does not have JMX notification of service events (a temporary workaround would be select/deselect the bundle from the list so its services list would be updated).

If you’ve followed all the steps you can build the project and test the bundle (or maybe download it from here).
Run Felix, and type start .
If you start JConsole and connect it to Felix, you should be able to visualize all tof our OSGi MBeans in the MBeans tab.

3. OSGi VisualVM Plugin Development
The VisualVM plugin will depend on the JMX interfaces of the project described on the previous section, so it would need to be built and deployed with the plugin as well. My choice was to create a Module Suite, for aggregating the library and the VVM plugin.

So, you'll end up with four Netbeans projects:
  • The OSGi bundle from previous step
  • The Module Suite (just an umbrella project for the two below)
  • A library wrapper for the OSGi bundle
  • The VisualVM plugin
3.1 Steps for creating the Module Suite:
-New Project > Netbeans Modules > Module Suite


- Next, choose the target Netbeans platform (in our case is VisualVM 1.1.1)

3.2 Creating the Library Wrappper module

-
Choose a Netbeans Project > Library Wrapper Module
- Select the libraries (the jar files) to be wrapped. In this case I've wrapped the OSGiJMX.jar and the felix.jar in the same module. - In the next step you must indicate which Module Suite will contain your wrapped library (as indicated in the figure below). Then you can go to the next and final step where you define your codebase.


3.3 Creating the VisualVM Plugin

- Create a new project > Netbeans modules > Module
- Important: Choose the module suite create before, so your plugin module will be able to resolve the classes of the wrapped library under the same Module Suite (but we still need to configure that)


After this step was finished, I've created a module installer:
- Right click on the plugin project, New > Module Installer
- I've named it installer and added the code that follows
public class Installer extends ModuleInstall {
@Override
public void restored() {
BundlesViewProvider.initialize();
}
}
Besides the Installer and the BundlesViewProvides, the other classes for the VVM plugin were:
Dependencies
After this, we would need to add the module dependencies concerning the imported types. Right click on the project > Properties > Libraries. Then you select the dependencies (check the "Show non-API modules" on the pop-up dependency window) listed in the figure below:


At this point, trying to run/build the plugin gives the following error:

"The module osgijmx.vvm is not a friend of \profiler3\modules\org-netbeans-lib-profiler-ui.jar"

We can solve this by editing the JFluid module dependency and setting it as an "Implementation Version"


Ok, but why depend on the JFluid-UI classes?
Well, these are the classes used for developing the pretty Visual VM built-in plugins. So, in order to get our plugin to look similar
to them we used the JFluid-UI. The result can be seen in the table on the figure below where we inspect Fuji.

Here's the code for creating the table (JExtendedTable from JFluid UI):
        bundleTable = new JExtendedTable(tableModel);
bundleTable.setBorder(BorderFactory.createEmptyBorder());

bundleTable.setRowSelectionAllowed(true);
bundleTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
bundleTable.setGridColor(UIConstants.TABLE_VERTICAL_GRID_COLOR);
bundleTable.setSelectionBackground(UIConstants.TABLE_SELECTION_BACKGROUND_COLOR);
bundleTable.setSelectionForeground(UIConstants.TABLE_SELECTION_FOREGROUND_COLOR);
bundleTable.setShowHorizontalLines(UIConstants.SHOW_TABLE_HORIZONTAL_GRID);
bundleTable.setShowVerticalLines(UIConstants.SHOW_TABLE_VERTICAL_GRID);
bundleTable.setRowMargin(UIConstants.TABLE_ROW_MARGIN);

Using JMX from a VisualVM plugin

The usage of JMX was straightforward, without any problems. From our DataSourceView the Application instance can be used to retrieve the MBeanServerConnection:
public BundlesView(Application application) {
super(application, "OSGi", new ImageIcon(Utilities.loadImage(IMAGE_PATH, true)).getImage(), 60, false);
mbs = JmxModelFactory.getJmxModelFor(application).getMBeanServerConnection();
...

After that, the MBeanServerConnection can be used to retrieve a proxy to the MXBean
ObjectName objName = new ObjectName("osgijmx:type=framework");
OSGiPlatformMXBean osgiProxy = JMX.newMXBeanProxy(mbs, objName, OSGiPlatformMXBean.class);

The invocations of the methods were ok, but I had some problems with a MXBean method that returned a Map[] :
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class

But I still had errors changing the return type to a Map of Map.
Surely issues with JMX’s Open type translation. The above methods did not work called from my proxy (any help from JMX experts would be great…), but the Map getBundleHeaders() did work…

I tried using an MBeanServerConnection.getAttribute and it did work. However it obliged me to navigate through TabularData instead of a java.util.Map, just the way JConsole does.

Building and deploying
Inside the Netbeans we can run the plugin, but if we want to deploy it in the VVM we need to build the nbm files. Just right click the Module suite project, and hit Create NBMs.
Going to the updates folder of the Module Suite project we can find the two generated files.
For their deployment on the VVM, the steps are the same from
here.

I've tested it just in Felix-based applications like Glassfish and Fuji, and it worked fine.

For deploying in Glassfish the OSGi bundle with the JMX probes:

GLASSFISH_HOME\bin\asadmin deploy --type osgi YOUR_PATH\OSGiJMX.jar

This is it. Maybe a long post, but this is the end of it...

Suggestions for new features are welcome, as well as source code for doing so.

References:
Getting Started Extending the Visual VM
JMX Tutorial
OSGi Javadoc
Inter-MXBean references