Wednesday, April 22, 2009

List of plugins needed for Eclipse RCP Help (Eclipse Version 3.4 - Ganymede)

It took me a LOT of time to compile the right set of plugins needed for Eclipse RCP Help. I could find few related articles but they were outdated for Ganymede. First of all there is no need for any compile time dependency to be added to your plugin.xml. If you have a basic RCP application, which has no help in it, you need to add these plugins to enable Help on top of the plugins which you already have. They only need to be in the target platform. Below are the plugins which you need to have in your target platform. Make sure to add them to your product as well.

javax.servlet
javax.servlet.jsp
org.apache.commons.el
org.apache.commons.logging
org.apache.jasper
org.apache.lucene
org.apache.lucene.analysis
org.eclipse.equinox.http.jetty
org.eclipse.equinox.http.registry
org.eclipse.equinox.http.servlet
org.eclipse.equinox.jsp.jasper
org.eclipse.equinox.jsp.jasper.registry
org.eclipse.help
org.eclipse.help.ui
org.eclipse.help.webapp
org.eclipse.help.base
org.eclipse.osgi.services
org.mortbay.jetty

Note: You no longer need org.eclipse.help.appserver and org.eclipse.tomcat plugins in your target platform in 3.4 (Ganymede).

Friday, April 17, 2009

Integrating Eclipse Logging with log4j

Introduction:

As you may know, Eclipse uses it's own logging API, which is different than log4j. All the eclipse core plugins use Eclipse logging. What can you do if you have dependencies which are already using log4j? Should you use Eclipse logging or log4j in your plugin? This article deals about the options available and how to integrate both of them.

Options:

RCP logging in Eclipse and dependencies in Log4j:

Have your plugins write to Eclipse log and maintain two different log files. i.e one from log4j and one (.log) from Eclipse. One problem with writing to Eclipse log is there is no external config file with which users can control the levels of logging and the classes/packages for which they want to see the log. Also, this also means that only Eclipse logs will show up inside your application (in Error Log view).

Third party plugins to see log4j:

While the above option does not look good, the good news is there are some eclipse plugins available here and they would show the log view inside eclipse. So, if your customers ever want to see the other logs inside the application, then you can use this.

Ultimate destination is log4j:

If you want your ultimate destination to be log4j, then you can go with this option. You can attach listeners to eclipse ILOG (see Platform.addLogListener(ILogListener)) and then your listener can then convert and write to your log4j. This is one way to get all the logging in to place i.e to your log4j, since your dependencies are already writing to log4j. Once you have this, you can use third party plugins (described above) to show all the logs in one view.

Ultimate destination is Eclipse Log:

If you want your ultimate destination to be Eclipse log, this is harder to achieve. I don't know of any option to convert your dependecy project's logs to Eclipse log. But, if you plugin is writing to log4j, then you can use the plugin (or just the idea) presented in this article. This plugin uses custom appenders and hierarchy event listeners and converts the log4j log to Eclipse Log. This is a great option to use if you do not have dependency plugins which write to log4j. Also, you can precisely control the log levels at run-time using this option.

Conclusion:

We saw several options to use and combine eclipse logging and log4j. You can also have your ultimate destination to be both log4j and Eclipse log by combining the above options presented.

Wednesday, April 15, 2009

JavaRebel - JVM HotSwap plugin

I recently encountered this and find it very useful in my UI development. I use the eclipse debugger not only for debugging but also to make changes to my code without restarting my application. But, the debugger uses JVM HotSwap and is very limited since it only allows to make changes to my method i.e I can add/change contents of the method, but if I remove a method, remove a constructor etc., then it fails to reload properly. JavaRebel comes out great in these areas. It is basically a JVM plugin (javaagent) which enables you to see changes to the code immediately without restarting the application.

Using JavaRebel

They offer a free trial (which is what I'm using now). Go here to download the zip file. Just unzip the file and add the following vm arguments to your application.

-noverify -javaagent:your/path/to/javarebel.jar

It is basically an advanced version of JVM HotSwap plugin. You can see the comparison matrix here in their web-site.

When you start the application, it prints info about JavaRebel and also whenever you make changes to the code, it also prints which classes are loaded dynamically. Right now, I'm using this for eclipse plugin development, but this can be used for other purposes like creating a web-application etc.,

Tuesday, April 14, 2009

Creating RCP Cheat Sheets (Eclipse 3.4 - Ganymede)

Cheat sheets guide the user through a series of steps to achieve a specific goal in the application. This is more like a hands-on tutorial and would be very useful for new users of the application. In this article, we will explore how to create cheats for your application.

Adding cheat sheet to the help menu:
  1. Add org.eclipse.ui.cheatSheets as dependency for the plugin.
  2. Add org.eclipse.ui.actionSets extension point
  3. Under this add a new action set for cheat sheets and give it the id org.eclipse.ui.cheatsheets.actionSet
  4. Create an action under the action set and give it the id org.eclipse.ui.cheatsheets.actions.CheatSheetHelpMenuAction. Give it a menubarpath (id of the help menu\path), label and style.
Now when you run your plugin, you can see Help->Cheat sheets menu. When you click on it, you can see the "Cheat Sheet Selection" dialog, but the contents would be empty since we haven't added the cheat sheet content yet.

Creating Cheat Sheet:
  1. Add org.eclipse.ui.cheatsheets.cheatSheetContent extension point.
  2. Go to File->New->Other->User Assistance->Cheat Sheet
  3. Select the folder where you want to create the cheat sheet, type in the file name and choose whether you want to create a simple or composite cheat sheet. We will create a simple cheat sheet in this article.
  4. When the editor comes up, it would just have Title and Introduction. Replace the defaults with your own title and introduction.
  5. Click on Register Cheat Sheet in the top right to register the cheat sheet before adding contents to it.
  6. In Register cheat sheet dialog, set the category (Optional - but highly recommended so that they are grouped properly in the "Cheat Sheet Selection" dialog) and enter description. This basically adds your cheat sheet under the cheatSheetContent in plugin.xml.
  7. After this you can add how many number of steps you want and each step can have many sub-steps under it.
  8. Apart from the instructions, we can also add links to the help content by using Help section in the cheat sheet editor.
  9. One of the main advantages in cheat sheet is user can either do the step manually and click on "Click when complete" or click on "Click to perform" to automatically perform the action. Click to perform can either be achieved by adding an action or by adding a command to the step.
  10. To add the command click on Command->Browse in cheat sheet editor and select the appropriate command. If required, enter the parameter to that command.
  11. We can also add an action to a step, but that is not currently supported by the cheat sheet editor. We can go directly to the xml and add the action though. An example of an action looks like this below: <action
    required="false"
    pluginId="com.xyz.myplugin"
    class="com.xyz.myplugin.actions.MyAction>

    Here we specify the action class and the plugin id it lives in. Required = "false" attribute displays the "Click when complete" link. If required is true, then it means user has to use the click to perform and so click when complete will not be displayed.
Advanced Options through XML:

Some of the advanced options are not available via the cheat sheet editor. We can add them directly to the XML since cheat sheet is persisted in XML anyway. Click on the source tab in cheat sheet editor to open the XML.
  1. In the XML, each step is represented by an item and each sub-step is represented by a sub-item.
  2. If a particular step involves bringing up a modal dialog, you can add dialog=true attribute in the item element in XML and that will make sure to bring the cheat sheet view along with the dialog so that cheat sheet view can be operated while in a modal dialog.
  3. We already saw action above. An item can also have perform-when, onCompletion, conditional-subitem and repeated-subitem elements. You can guess what they are used for from the name. Sample XML for some of these are shown below:
<item ...>
<conditional-subitem condition="${v1}">
<subitem when="x" label="Proceed to x" />
<subitem when="y" label="Proceed to y" />
<conditional-subitem>
</item>
The above code picks the sub-step based on the value of variable v1.
<item ...>
<subitem label="Sub step">
<perform-when condition="${v1}">
<action when="a" class="com.xyz.action1"
pluginId=
"com.xyz" />
<action when="b" class="com.xyz.action2"
pluginId=
"com.xyz" />
</perform-when>
</subitem>
</item>

The above code picks the action for the sub-step based on
the value of variable v1.

Conclusion:

We saw how to add cheat sheet to the menu and contribute
cheat sheet content. We also saw how to add the content
directly through the editor or using the XML. I hope this
article helps you to create cheat sheets in your
application.

References:
  1. Building Commercial-Quality Plug-ins by Eric Clayberg and Dan Rubel.
  2. All the articles on cheat sheets in my delicious bookmarks:http://delicious.com/search?context=userposts&p=cheat%20sheet&lc=1&u=rajak27

Creating, Loading and Using Eclipse RCP Extension Points (Eclipse 3.4 - Ganymede)

As you know, eclipse platform by itself is very small and most of the functionality is provided by surrounding plugins. These plugins interact via extension points. Extension points allow the user to extend/customize the functionality of other plugins. Everything in the platform, for example views, editors, perspectives, preferences etc., use extension points.

It is easy to use the extension points defined by other plugins. It is slightly more complex to define our own extension points so that it is consumed by other plugins. This article primarily focuses on creating new points.

Creating New Extension Points:

The assumption is you have a plugin project working and you need to create a new extension point. Open plugin.xml and click on the "Extension Points" tab. Extensions tab is where we use extension points defined by other plugins. Extension Points tab is where we create our own extension points.

Click on "Add" to create a new extension point. Fill out the extension point id and name. The schema is auto-created in location schema\id. You can change it if you want. Once you click ok, the extension point schema editor will be open. You can also open it at a later time by double clicking on the schema file (.exsd) in explorer.

Overview tab:

This tab defines the basics - plugin id, point id, name and documentation. The plugin id is concatenated with the extension id by Eclipse to create a unique identifier. Documentation is very helpful for consumers using the extension points. They are used by the Extensions editor while client plugins use the extension points.

Extension Point Schema Definition:

This is where we actually specify the schema of the extension point.
  1. Create a new element by clicking on the New Element.
  2. Create new attributes for that element using New Attribute.
  3. The attributes can be of any type - boolean, string, java etc., java type is used to define the java super class or interface, which clients have to extend or implement. You can also use both. The best practice is to use both so that if the interface changes (addition of a new method etc.,) we can add a default implementation in the abstract super class without breaking the client code.
  4. Once the elements and it's attributes are defined, the next step is to add it to the extension. Add a new sequence or choice to extension (by right clicking on extension node).
  5. You can add the element(s) underneath the sequence/choice and specify the min/max occurences.

Source tab:

You can view the actual XML schema for the extension point in this tab.

Exporting the necessary classes:

Once the schema is defined, make sure to export the abstract class/interface to client plugins if your extension point defines one. You can do this by going to plugin.xml->Runtime tab and under exported packages make sure to add the package which has this class/interface defined.

The next step is to load all the extension point implementations from the plugins available in the target platform. Before that, let's see how to use this extension point from a client plugin.

Using the Extension Point:

  1. Open the client plugin's plugin.xml
  2. Go to "Dependencies" tab. Add the main/base plugin (which defines the extension point) as it's dependency.
  3. Go to "Extensions" tab and click on "Add" to add the extension point.
  4. Now, right click on the extension point to add it's children/attributes and fill in the details in "Extension details".
  5. The extensions editor gives the options based on the extension point schema. You can also view the extension point schema and description from here.
  6. If the extension point needs a class implementation, you need to create the class and put the class name in Extension Details". You can click on the class attribute name and that will also open the "New class dialog" with relevant details filled in.
Loading the extension points:

Now, let's see how load all the extension points:

try {
IConfigurationElement[] config =
Platform.getExtensionRegistry().getConfigurationElementsFor(
IGREETER_ID); // 1
for (IConfigurationElement e : config) { // 2
Object o = e.createExecutableExtension("class"); // 3
if (o instanceof IGreeter) {
((IGreeter) o).greet(); // 4
}
}
}
catch (Exception ex) {
System.out.println(ex.getMessage());
}

Eclipse platform maintains a registry of all extension points available in the target platform. Point 1 loads all the extension points named IGREETER_ID from the platform registry. Point 2 iterates through those configs. Point 3 is the main thing. It looks for "class" attribute in the config element and loads that class. In this case, it is the implementation of interface (which this extension point requires clients to implement). You can also use getChildren and getAttribute methods to query the configuration element. Once thing to note here is that, we could not just do Class.forName here. We need to use this method to instantiate the objects. Once the object is created, we can call methods in the interface to do the operation.

Lazy Initialization:

One main thing to note here is, in the above code, I've instantiated the class immediately. I've done that only to show how to do it. But, it is a BAD practice to load the implementations until it is really needed since it loads the corresponding client plugin. Imagine, if all plugins did it this way at start-up, then it would take forever to start the application. Often we need info about the extension points, but we don't want to really load the plugin for that.

For example, a typical use case is "Show views" or "Show perspectives" or in our custom case maybe "Show available implementors". If we have to show this to a user, we don't need to process all of the plugins. Instead we could just query the registry and get all the configuration elements. From the configuration elements, we could get all the information defined by the client in their plugin.xml. A good way is to use the proxy pattern. Store the config element in a proxy object and query the attributes/elements to decorate the UI. Then, when user clicks on a specific implementation and when it is time to do the action call the execute method on the proxy object which will do this:

this.configElement_.createExecutableExtension("class")

So, we actually load the client lazily only when it is needed.

Conclusion:

We saw how to create an extension point, how to use it from a client plugin's perspective, loading available extension points and how to lazily load them. I hope this article helps you in working with extension points.

References:
  1. Building Commercial-Quality Plug-ins by Eric Clayberg and Dan Rubel.
  2. All the articles on extension points in my delicious bookmarks: http://delicious.com/search?context=userposts&p=extension&lc=1&u=rajak27