Helios comes with an OSGi console that is accessible from the GUI directly. This reminded me of the existence of an API to add custom commands to the console. I found this to be a simple and powerful concept; yet I perceive this feature to be rarely used in practice.
With equinox being used on the server more and more, adding your own custom application management commands is a simple way to provide for executing application specific maintenance tasks. How about a command that gathers all logs, creates a heap dump, zips everything, puts it onto an FTP server and sends you the download link by mail? Or perhaps you simply want to run some sanity checks?
In this post, we’ll create a custom command for the OSGi console that simply executes a JUnit test and prints the results.
More importantly, we’ll have to snoop around in internal eclipse packages to figure out how to attach the command to the existing console. We’ll try to do our snooping effectively. And we’ll exploit the dynamic nature of OSGi to streamline our development/build/test cycle (and thats gonna be fun).
PreparationFirst, you need to grab the Helios Classic Editon build1 as this version includes the sources as well. We’ll use the OSGi console for the currently running eclipse instance that is available via the UI since 3.6M72
Now start eclipse, create a new workspace and go to the workbench.
Open the console view (Alt-Shift-Q,Q) and select “OSGi console” from the view menu. Â You now have an OSGi console that allows you to interact with the running eclipse3.
Type “help” to get a list of available commands. You should do that even if you are a eclipse veteran, the list of available commands has grown considerably during the last years. There are commands to start/stop/install/refresh bundles, to view registered services, to configure runtime properties, to query the extension registry, to start and stop equinox applications and enable and disable components.Let’s assume we want to be able to run integrity tests on a live system to check whether incoming issue reports could be due to some irregularities with system load, data or deployment issues. Lets also assume that application has a set of JUnit tests to do so. We want to be able to log onto the console and see what the results of our test suite are.
Test FirstNo, not that test first. To add a command to run our tests we need tests first, see? We simply create a new OSGi bundle targeting the equinox runtime using the “New project” wizard and add some tests to it. Make sure to have at least on failing test as we’ll print only failures from our command.
We also need some piece of code to let us run our tests. Luckily, JUnit provides a simple API for that4.
Monkey See, …The basic steps are to implement the org.eclipse.osgi.framework.console.CommandProvider interface and to register it with the OSGi service registry. But this is covered in at least twoÂ tutorials by Chris Aniszczyk5 and his evil, quake playing twin6. You should definitely read them. Less chitchat than this post.
First, we need an entry point, a piece of code we know that has something to do with the console. So what do we know? Well, there is a console view, let’s go hunting for it’s implementation. To make sure that our searches include the code from the platform plug-ins, we will add them to the Java search. Â Helios has a handy new button in the Plug-Ins view for that. 9
Fire up the search dialog 10, select the Plug-in Search tab, enter “org.eclipse.ui.views” as search string and select “Search for-> Extension Point”, “Limit to -> References” and “External Scope -> All”.
This search gives us 28 matches. One match is the one we are looking for: org.eclipse.ui.console.
Let’s open the declaration and switch over to the extensions tab. We can see the contribution the console view we were looking for, but also something that looks even more promising: An extension point called “org.eclipse.ui.console.consoleFactories”. Let’s see who contributes to this extension point. Choose “Find References” from its context menu.
Only four matches. I think we can rule out the one in team.cvs and debug.ui. Â We’ll find what we are looking for in the org.eclipse.pde.ui bundle11.
The contribution contains the implementation class, so we open this class next. Copy the qualified name of the class, press Ctrl-Shift-T and paste the class name.
In the console factory, we find that a new console is instantiated in the getConsole() method. Let’s open the implementation by Ctrl-Click on the class name.
A first glance at this class does notÂ immediately reveal where the console commands come from. We need to dig a little deeper aka start guessing and poking around wildly. To be able to run a command, the console input needs to be evaluated, thats for sure. So lets see if we can find where the input is read. In the OSGi console constructure, a new anonymous ConsoleSession is created. The ConsoleSession has a method called getInput() that returns an input stream. Let’s see who calls this method: place the cursor on the method name and press Ctrl-Alt-H to open the call hierarchy.
The first&direct caller, FrameworkConsole, seems not overly interesting in terms of command definitions. Let’s see what the next one, ConsoleManger looks like. A little hidden, somewhere in the middle we find a field of the type FrameworkCommandProvider. Now we’re pretty close to what we want to know. This is the place were – at least some – console commands are defined. The constructor’s comment pretty much tells us what to do:
So we need to implement a CommandProvider and register it with the service registry. For each command we want to implement, we need to have a method called “_<command name>”. We’ll implement only one command that calls our test runner. Additionally, I think we should implement the the getHelp() method to describe our command to the user.
It may be useful for your analysis to make changes to eclipse plug-ins to see how they behave. There is a simple recipe how you can do this without the risk of doing much harm: Import the respective plug-in from the target platform to the workspace, then create a launch configuration containing the workspace instead of the target platform bundle. You can now edit the standard eclipse bundle to gain a deeper understanding. When you are done, simply delete the bundle from the workspace (and from the disk as well).
…Â Monkey DoUse the bundle project we have created in the first chapter. Add a new dependency to “org.eclipse.osgi.framework.console”ï»¿, the package containing the CommandProvider interface.
Create a new class using the new class wizard, have it implement CommandProvider and generate the inherited methods.
Implement the getHelp() method so the output looks similar to the other commands. Have a look at the FrameworkCommandProvider for reference. Note that you can use Alt-Left/Alt-Right to navigate in the edit/browse location history. Useful as you can easily switch back and forth between your class and the reference class without using your mouse.
We call our command “check”, so we also need to implement “check(CommandInterpreter intp)”. The command interpreter is used to output the results from our tests. Take a look at the “*” methods in FrameworkCommandProvider for examples.
The complete class looks like this12:
Now we need to register/de-register our command provider in the Activators start() resp. stop methods:Having implemented all this, it’s time to look at our options for giving it a test drive
Strange LoopsWe can either
- start our bundle as eclipse application, open the console view and test our command
- create a minimal run configuration and start the framework with the console and test there
- build the bundle and install it into the running framework (the running eclipse instance)
- or even install the bundle directly from it’s files in the workspace
Then we can use the console to install the bundle from the workspace. First, check that our command is not yet available by calling our Â ”check” command. Our command is not found, so the help contents are displayed. Note that our command’s help text is missing as well. Then we can install the bundle if we know the project’s path on the harddrive.
We have now Â installed a bundle that we are currently developing into our running eclipse instance. Isn’t that awesome? Of course you need to be careful with this approach as you can pretty much screw things up when fiddling with the hot bundle configuration of your IDE, but I think this approach actually makes sense when you are integrating with the IDE rather than building a standalone application.Besides, I just love self-referential stuff.
What I mean by “strange loop” is â€” here goes a first stab, anyway â€” not a physical circuit but an abstract loop in which, in the series of stages that constitute the cycling-around, there is a shift from one level of abstraction (or structure) to another, which feels like an upwards movement in a hierarchy, and yet somehow the successive “upward” shifts turn out to give rise to a closed cycle. That is, despite one’s sense of departing ever further from one’s origin, one winds up, to one’s shock, exactly where one had started out. In short, a strange loop is a paradoxical level-crossing feedback loop. – Douglas R. HofstadterSee alsoÂ http://www.xkcd.com/555/, don’t forget to read the tooltip.
ConclusionBesides highlighting the Host OSGi console included in Helios and trying to revive the memory of custom OSGi console commands, this article showed you how you can find out how stuff is done in eclipse by following the monkey see/monkey do rule and gave some tips how to apply this rule in practice.
I’d be happy to see more custom OSGi console commands in the wild. As I said in the teaser, I still think this concept is underused. Also remember that the OSGi console can be configured to be accessible remotely using telnet. Now don’t tell me you can’t think of use cases for that.
I’ll push the complete sources of the example to github during the next days. In the meantime: Let me know if you get stuck.
I hope you enjoyed reading and that I gave you one or the other idea that may help you in future projects. Thanks for reading.
- http://www.eclipse.org/downloads/packages/eclipse-classic-360/heliosr ↩
- http://download.eclipse.org/eclipse/downloads/drops/S-3.6M7-201004291549/eclipse-news-M7.html#osgiconsole ↩
- The screenshots were taken on 3.6M7, it looks a little different in the final release. The draft of this post has seen some idle cycles… ↩
- http://www.junit.org/junit/javadoc/4.3/org/junit/runner/JUnitCore.html ↩
- http://www.ibm.com/developerworks/library/os-ecl-osgiconsole/index.html ↩
- Â http://eclipsesource.com/blogs/2008/08/01/tip-custom-osgi-equinox-console-commands/ ↩
- Good Artists Copy, Great Artists Steal – Pablo Picasso, probably. Also look for T.S. Eliot quotes in that context. Steve Jobs stole it, too. In the late eighties, if you judge by his hair. ↩
- Valiantly deciphering XML (plugin/application context/deployment descriptors) to learn how dependencies are wired at runtime, too. Provided the tooling is good. Otherwise it can be quite frustrating. Luckily the PDE tooling for navigating plugin.xml & manifest files is excellent. The search also feels a lot faster on Helios. ↩
- You can do the same in pre-helios builds: In the Plug-ins view, select all plug-ins and choose ‘add to Java search’ from the context menu ↩
- Ctrl-H ↩
- This is not pre-recorded. I’m making it up as we go along. Perhaps I left out some dead ends, though. See if you can spot them in the screen shots. ↩
- Yep, we simplified the getHelp() method. Using constants is good style, Â but only if you use them more than once. KISS, for this example. ↩
- In a weird, nerdy way, at least. ↩
- http://blog.wolfgang-werner.net/building-on-equinox-and-restlet-1/#Running ↩