Creating my very first Eclipse Plugin
I recently* completed my very first Eclipse Plugin, and I found the whole experience to be very interesting.
*(well about a month ago; took me longer to get writing this than intended)
This blog entry will focus on two main areas - my experience with Eclipse (as opposed to CFEclipse and similar), and the issues I encountered from a development perspective
The Editor Experience
Up until recently, my experience of Eclipse had really been via various editor packages - CFEclipse, WTP, XML Buddy, phpEclipse, and so on. I hadn't really used the pure Eclipse Java IDE.
So, the first thing that pleased me about Eclipse was the Package Explorer. This is similar to CFEclipse's Navigator view, except it is less directory/file-centric, and more of a package/class/function approach. When you're writing code, you should be able to forget about individual files and focus on how the structure of the application.
In an ideal IDE, you would flow seemlessly throughout the code, not needing to worry about using a navigator to locate and open the various files.
Eclipse is not yet at that sort of level, but it seems to be the closest I have come to working with code not files.
Next up: Formatting. Most developers have a style of formatting their code, and almost all of you are at best misguided, if not completely wrong. That includes specifically whoever setup the default formatting style for Eclipse. Fortunately, it allows you to customise the style to fit, with fairly extensive controls so you can specify what should and shouldn't be indented, where whitespace goes, how many blank lines to separate blocks with, and so on.
Once you have specified how your code should be formatted, in any file you can reformat the code to match your rules, with a couple of clicks or a simple key combination.
When actually creating a plugin, although you ultimately have a collection of configuration files and Java files, Eclipse collects all the configuration files into a Plugin Manifest Editor. This allows someone new to plugin configuration to be a little less intimidated, as you get things nicely layout out in appropriate groups, rather than having to figure out what the (three?) different configuration files actually do.
One thing that confuses me a little is why the tabs for the Manifest Editor are so small - half the size of other tabs - not a particularly big problem, but I wonder why there isn't just a few extra pixels of padding to reduce the precision required to change tabs.
Developing the Plugin
So, how about the actual process of developing the plugin? Well, before we get into that I better explain what the plugin was to do, and indeed why create the plugin at all.
For those that aren't aware, I have created a tool called QueryParam Scanner. It will scan through CFML code looking for database queries that appear to have missing cfqueryparam tags, and report back all the potential risks it finds. By default, this runs in a browser, and you have to type in the directory to go to, and configure the options each time you use it.
Creating the Eclipse plugin would allow it to easily be run against any resource with just a couple of clicks, plus enabling the configured preferences to be set once and re-used.
So, ultimately there are two key functions for the tool - the ability to set and use preferences, and the ability to right click an item and launch qpscanner in a browser view.
Initially, I wanted to get very basic functionality up and running. Right-click a directory and open a browser view. This was where I hit my first irritating problem.
I had some incredibly simple code, that should have worked according to the various Google pages I was looking at, and yet it wasn't.
URL url = new URL("http://www.google.com");
PlatformUI.getWorkbench().getBrowserSupport().createBrowser("myId").openURL(url);
I was getting odd generic errors, and wasn't getting anywhere by Googling them.
I don't remember now where I found the solution to this, but eventually I got it. The problem was that both those statements require exception handling code to be added, or they wont compile.
WTF? It's like CF telling you it can't do a cflocation because you haven't checked incase your URL is invalid.
Whilst proper exception handling is critical to good software, when all I'm doing is trying to get very basic code working, I don't want to fuss about working out what my exceptions will be like.
So, to get around that, I did something a bit bad, but it allowed me to continue - I just did empty error catching:
try
{
URL url = new URL("http://www.google.com");
}
catch( MalformedURLException e )
{
// do nothing
}
try
{
PlatformUI.getWorkbench().getBrowserSupport().createBrowser("myId").openURL(url);
}
catch( PartInitException e)
{
// do nothing
}
After making various bits of progress with the rest of the project, I did come back and add proper error handling. And of course made the URL dynamic. Here is the final function that the above code eventually turned into:
private void loadUrl( String BrowserId , String Url )
{
try
{
URL url = new URL( Url );
try
{
PlatformUI.getWorkbench().getBrowserSupport()
.createBrowser( BrowserId )
.openURL( url );
}
catch( PartInitException e)
{
showError( e , "There was a PartInitException" );
}
}
catch( MalformedURLException e )
{
showError( e , "There was a MalformedURLException for URL '".concat(Url).concat("'.") );
}
}
(I'm not sure why I let myself create both url and Url variables... I'll have to fix that!)
With a browser view eventually working, I created a preferences page. All this was rather simple, if more fiddly than with HTML. Each preference had to be setup across three different classes.
First, the constant:
public static final String P_CLIENTSCOPES = "ClientScopes";
Next, the default value:
store.setDefault( PreferenceConstants.P_CLIENTSCOPES , "form,url,client,cookie" );
And finally, the actual field:
addField
( new StringFieldEditor
( PreferenceConstants.P_CLIENTSCOPES
, "&Client Scopes"
, getFieldEditorParent()
)
);
That sets up a preference field and handles all the loading and saving of preferences for you, which is nice and helpful.
However, what is not helpful is the next problem I ran into. In addition to global preferences, I wanted to enable users to be able to configure different options per project, via the project's property page.
For reasons known only to the nuts who developed the two features, Property pages and Preference pages, although conceptually and contextually very very similar, are actually implemented in completely different ways. It was looking like all the effort I had put into creating the Preference pages would have to be exerted again in creating a template for Property pages.
That's a crazy situation, so I instead searched around, and after a while I found an article by Berthold Daum explaining how to use one set of code for both Preferences and Property pages, with an example class called OverlayPages. This allowed me to keep most of what I'd already done, with just a little bit of adaptation, and the end result was that not just for each project, but for every resource (i.e. project/directory/file) the user has the option to use the global workspace settings, or to override for that specific resource.
Once I had the overlay pages in place, and a launchable browser view, the rest was just a case of filling in the gaps and making sure it all worked as expected, and fiddling with the various options in the Plugin Manifest Editor.
Throughout the development of this plugin, I of course used Google when I got stuck. Initially, I had given up on the API docs included within Eclipse, as they seemed more of a reference for existing users. However, after a while I realised I had underestimated them, and there is actually good information even for newbie plugin developers.
In addition to Google, the API docs, and the article by Berthold, I have to thank Marc Esher (one of the MXUnit contributers) for his help in talking me through snippets of the MXUnit Eclipse Plugin, which gave me a good kick-start.
Marc also recommended the book Building commercial quality plugins - I've not yet had a chance to go to my local bookstore to check this out myself, but he says it's worth every penny, so if you're going to write an Eclipse plugin it's definitely worth investigating.
For anyone interested in seeing the source code for the plugin, it is of course all available, licensed as EPL. I'll be making my git repository public sometime in the next few days (or weeks, depending on how [dis]organised I am), but in the meantime you can get the source as a zip file.
With this being my very first Eclipse plugin ever, there's a good chance I didn't do everything the best way. If there are any experienced plugin developers out there, feel free to review my code and point out anything you think could be improved.
I hope this entry has been interesting and helpful. I would have liked to write more, but since I left it so long it's hard to recall everything I was planning to write about. However, if anyone has any specific questions, feel free to ask them. :)