Writing applications that can be embedded in IDEs
Well, every time I think I'll be able to blog more often, something happens. So, I will try not to apologize about it and get straight to the point. :-)
A cool thing I did recently was to write a set of NetBeans plugins that adds support for Thinlet in the IDE, called ThinNB. One important feature that it provides is a visual editor for Thinlet xml files. In order to implement it, instead of reinventing the wheel, I've decided to base my work on ThinG, a standalone application created by Dirk MÃ¶bius that already did that. There were a few changes I had to do it so it could be embedded in NetBeans and that probably would need to be done to most applications if they were to be converted into IDE plugins, so it is worth talking about them.
ThinG's code is actually well-written, so I didn't have to refactor it in order to expose a single class to my plugin, but this might not be true for other applications. If you are writing an application that might be used as a plugin in the future, make sure everything is properly encapsulated and that the right methods are exposed through your "main" class (which does not have to be the one with the
public static void main(String) method).
The changes I did concerned four main areas:
- I/O System
If you are running in embedded mode, there is already a menu bar, a status bar and a toolbar being displayed. It is important these features can be turned off in your application when running in this mode. What I did was to add a
boolean parameter to the main class constructor and used it in the section that assembled the UI.
Besides that, since many useful status bar messages were generated by ThinG, I wanted those to be shown at NetBeans's status bar. I solved this problem by creating a simple
StatusBar interface with a single method,
setText(String) and writing two implementations: one that simples use the Thinlet widget when running as a standalone application and another one that uses NetBeans APIs to display it in the IDE.
If there were menus or toolbar buttons that needed to be made available to the end user, it would probably be necessary to write some listener interfaces so it was possible to enable/disable them as required.
If you intend to use your application inside an IDE, it is better to think twice before using
java.io.File. IDEs have their own I/O abstraction for some reasons, such as adding listeners to files, performing I/O "transactions" or working with virtual filesystems.
Fortunately, ThinG didn't use
java.io.File a lot and I could create a simple interface,
thing.spi.FileWrapper, to abstract what was actually being used from
java.io.File. Again, I wrote two implementations of it, one that is part of ThinG itself and one that lives in the NetBeans plugin.
There is a decision to be made about sharing settings: do you want both the IDE and the standalone application to share the same settings or to keep them separate? If you pick option one and your application uses the Preferences API, it is quite straightforward to implement it.
If you want to follow the second path, you just need to define yet another interface for that purpose. Since I wanted to add a few settings and hide one from the end user, I ended up creating a
I am not sure this is true for other IDEs, but NetBeans has its own API for logging. In order to integrate with it, whether your application use
java.util.logging or Commons Logging, you just need to provide an adapter class that "translates" between the logging APIs. Since this issue is basically NetBeans-specific, please take a look at net.java.dev.thinnbeditor.ThinletVisualEditorInstall and net.java.dev.thinnbeditor.logging.LoggerAdapter if you want to know more about it.
ThinG is now both a standalone and an embeddable application after a few changes. It shouldn't be hard to use it as a plugin for Eclipse, IDEA or JDeveloper now, for example. I hope the principles I've explained here can help other people out there as well.