Platinum Partner
eclipse,scripting

Scripting Eclipse with the Monkey Project: An Introduction

Whenever it comes to non standard tasks, scripting is handy. Linux has shell scripting, GIMP supports Python, modern text editors support macros. Now Eclipse users have no longer to be ashamed. There is new star in Eclipse.org constellation - the Monkey project. This new, yet mature project, gives you the way to automate and speed up development using scripts.

How many times you were switching to shell only to use grep/sed/awk team? How often you had and idea for new functionality, but complex Eclipse architecture stopped you from extending the platform? This article will give a brief look over this new capability, introduced to wide audience in Europa (Eclipse 3.3) release.

This article expects from you to have at least basic knowledge about Eclipse, and extension points.
Should you lack this knowledge, the following short publications are very useful:

Installation

Best way to start with Monkey is to use Eclipse 3.3. When you download a copy, start it up and use update site to install the project. Check Europa Discovery Site > Other Tools > Eclipse Monkey on features selection screen (see following screen)

[img_assist|nid=736|title=|desc=|link=none|align=left|width=500|height=495]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figure 1. The Monkey project - installation

Press "Select Required" button if you see an error message, that adds required dependencies. Proceed with installation and restart Eclipse if required.

After the installation is finished take a look at menu bar, you should notice new item named "Scripts" - all available scripts are exposed here. You may try one of the examples. To do this, click on "Scripts" menu and select "Examples". "Eclipse Monkey Examples" project gets created in your workspace. This is a set of example scripts, created to familiarize users with Monkey project. Again go to "Scripts" menu, this time select Hello > Bjorn item. Pop-up dialog with a message is shown as a result of script execution. Congratulations! The Monkey is working.

Anatomy of a Monkey script

Take a closer look at "Eclipse Monkey Examples" project. When you expand, you will see a "scripts" folder with files inside. Those are script files - one script per file. Whenever new script is added, deleted or there is a change to existing file, the Monkey rebuilds its menu. Now I will explain the scripts structure.

The example we just launched is stored in Hello__Bjorn.js file. Open it in an editor:

/* 
* Menu: Hello > Bjorn
* Kudos: Ward Cunningham & Bjorn Freeman-Benson
* License: EPL 1.0
*/
function main() {
text = "Hello Bjorn\n\n";
text += "The quick brown fox jumped over the lazy dog's back.";
text += "Now is the time for all good men to come to the aid of their country."
Packages.org.eclipse.jface.dialogs.MessageDialog.openInformation(
window.getShell(),
"Monkey Dialog",
text )
}

First thing to notice is JavaScript comment, this is a header. You have to remember that Menu field is mandatory. Its content is straightforward: elements separated by > character form menu location, last element is a script name.

[img_assist|nid=739|title=|desc=|link=none|align=left|width=213|height=154]

 

 

 

Figure 2. Location defined in a header makes script available in the menu.

Although other header fields are optional, it is a good manner to add Kudos and License.

Function main goes after the header. This is where the Monkey will start script execution. First the text variable with the message is created. Then you see a reference to a Java class: MessageDialog. This is use of Rhino concept: fully qualified Java class name prefixed with Package keyword. Rhino is one of the first scripting engines that brought JavaScript to Java world, and it's used in current Monkey implementation. The window keyword is a variable provided by scripting engine. It is one of many predefined variables, giving a way to interact with Eclipse. Later you will learn more about this, now just assume the window variable is an object giving an access to Eclipse workbench (org.eclipse.ui.IWorkbenchWindow). The window.getShell() method returns workbench window's shell - Shell is an Eclipse concept, representing window visible to the user - necessary to create MessageDialog.

Internals

After first bite of the Monkey it is time to show you how it works inside. Look at the following UML diagram.

[img_assist|nid=740|title=|desc=|link=none|align=left|width=486|height=449]

 

 

 

 

 

 

 

 

 

 

 

 


 

 

Figure 3. UML diagram presenting major Monkey elements.

When Eclipse platform starts, EclipsePlugin creates the "Scripts" menu and its content, then registers a listener (UpdateMonkeyActionsResourceChangeListener), to keep menu content synchronized with scripts. Menu items are represented by MenuRunMonkeyScript class, derived from org.eclipse.jface.action.IAction. Whenever you select from the menu a script to run, the action obtains script runner from the corresponding IMonkeyLanguageFactory implementation, and delegates request to IMonkeyScriptRunner implementation. Apart from producing script runner, the language factory's role is to understand the script's header and produce a ScriptMetadata object as a result from the getScriptMetadata method. Generally speaking ScriptMetadata represents fields defined in the header. Script runner is the one to run the script. For now there is only a JavaScriptRunner implementation available, allowing to create the Monkey scripts in JavaScript. Other languages are supported by forked project GroovyMonkey, which I will cover later.

DOM

Scripts would be useless without ability to interact with Eclipse. In the Monkey world you use DOM - a plain Java object delivered to script's context. This way users may expose any methods and objects they want. DOMs are contributed to the Monkey through extension points. The following sections will explain the details.

Generic DOM

An example is best help to teach. Let's create a simple DOM: ExampleDOM. There will be only two methods in there:

package com.onjava.monkey.dom;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* Example DOM for use in monkey scripts.
*/
public class ExampleDOM {

private static final SimpleDateFormat dateFormat =
new SimpleDateFormat();

/** Print current date . **/
public void printDateNow(){
System.out.println(dateFormat.format(new Date()));
}

/** Print debug message. **/
public void printDebug(String msg){
System.out.println("DEBUG: "+msg);
}
}

To make this object available for a script, we need to implement org.eclipse.eclipsemonkey.dom.IMonkeyDOMFactory interface. This is a factory interface with only one method to implement:

public interface IMonkeyDOMFactory {
public Object getDOMroot();
}}

In our example we want return ExampleDOM class instance. It is stateless thus we may always return the same instance.

package com.onjava.monkey.dom;

import org.eclipse.eclipsemonkey.dom.IMonkeyDOMFactory;

/**
* Simple DOM factory.
*/
public class DOMFactory implements IMonkeyDOMFactory {

private ExampleDOM dom;

public DOMFactory() {
dom=new ExampleDOM();
}

public Object getDOMroot() {
return dom;
}
}

Now we contribute DOMFactory to the org.eclipse.eclipsemonkey.dom extension point to make script runner aware of our DOM. We have to provide following values:

  • variableName - name under which the DOM instance will be registered in script's context.
  • class - Factory that will produce DOM.
  • resource - Class name reflecting DOM root's class

This is what goes into plugin.xml:

<plugin>
<extension
point="org.eclipse.eclipsemonkey.dom">
<dom
variableName="example"
class="com.onjava.monkey.dom.DOMFactory"
resource="com.onjava.monkey.dom.ExampleDOM"
>
</dom>
</extension>
</plugin>

Don't worry if you feel a little bit lost. I made the code above available as a plug-in, try it to fill the gaps in your understanding. There is source and binary distribution available.

Language specific DOM

Apart from generic DOM, JavaScript engine is supporting objects designed to be used with JavaScript. The way how you can contribute them is not too much different from generic DOM contribution. Important thing to know is that DOM contributed to org.eclipse.eclipsemonkey.lang.javascript.javascript_dom extension point, will be available only to Monkey scripts run by JavaScript engine. It is not a big problem for now as this is the only supported scripting language.

DOM usage and summary

DOM have to be packed as a plug-in an published on an update site in advance to use. The ExampleDOM is available at this update site: http://monkey.brain-bakery.com/update-site/ as a com.onjava.monkey.dom plug-in. The reference is established in a script header. Whenever want to use DOM you have to add "DOM" entry. Following is an example with a reference to ExampleDOM. Please, create new script file ExampleDOM.js and paste following content.

/*
* Menu: OnJava > ExampleDOM
* Kudos: OnJava
* License: EPL 1.0
* DOM: http://monkey.brain-bakery.com/update-site/com.onjava.monkey.dom
*/
function main() {
example.printDateNow
example.printDebug(“This is debug message.â€)
}}

What will Monkey do when it sees header from preceding code? Try it yourself, by starting the script. Select from "Scripts" menu OnJava > ExampleDOM item. You should see warning dialog, complaining about missing DOM.

[img_assist|nid=741|title=|desc=|link=none|align=left|width=500|height=171]

 

 

 

 

 

 


Figure 4. The "Missing DOM" dialog.

Click "Install Plug-in". Assuming above is ExampleDOM we created before, script should put two messages in console view: Current date and debug message.

What just happened is a part of Monkey magic. The plug-in that delivers DOM may be downloaded and made available to a script, thanks to entry in a header. This is what Monkey sees:

  • http://monkey.brain-bakery.com/update-site/ - update site location
  • com.onjava.monkey.dom - plug-in that contains DOM

The plug-in is downloaded from the update site, DOMs are made available in script's context. This mechanism is not perfect. Be aware of fact there is no support for versioning DOM objects. It may happen that a script will stop working, because of changes in remote DOM.

Now you know how powerful Monkey scripts can be. With proper set of DOM contributions a script can access any part of Eclipse. For your convenience there are some DOM available with Monkey distribution:

  • Generic (no need to mention in header)
    • resources - org.eclipse.eclipsemonkey.doms.resources.Resources
    • workspace - org.eclipse.eclipsemonkey.doms.workspace.WorkspaceDOMFactory
  • JavaScript specific (DOM url: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript)
    • io - org.eclipse.eclipsemonkey.lang.javascript.doms.io.IO org.eclipse.eclipsemonkey.lang.javascript.doms.io.File wrapper around java.io.File org.eclipse.eclipsemonkey.lang.javascript.doms.io.WebRequest
    • resources - org.eclipse.eclipsemonkey.lang.javascript.doms.resources.Resources
    • views - org.eclipse.eclipsemonkey.lang.javascript.doms.views.Views
    • editors - org.eclipse.eclipsemonkey.lang.javascript.doms.editors.Editors

Your own script

You are ready to write your very first script. Here is the idea: Whenever I'm stuck while coding I use one of code search websites to find examples, related to subject I'm working on. We will use Koders website. These are goals for the script:

  • take current editor's selection as an input
  • search at Koders website for selected phrase
  • show the result in Eclipse view.
  • be available in "Scripts" menu, as > Search > Google Code item.

We start with script's header. This is what we need:

/* 
* Menu: Search > Koders
* Kudos: OnJava.com
* License: EPL 1.0
* DOM: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript
*/

Store the script in a file your workspace. Create a new project named "OnJavaMonkey" and new "scripts" folder within. Create new file CodersSearch.js beneath and paste lines above. Go and check "Scripts" menu, similar items should be there:

[img_assist|nid=742|title=|desc=|link=none|align=left|width=270|height=326]

 

 

 

 

 


 

 

 

 

 

Figure 5. Example script's location.

Monkey plug-in reads open projects, and checks for "scripts" folder. Any file with js extension is read, parsed and placed in the menu. Script is available but not runnable. We add the function main:

 

function main() 
{
var sourceEditor = editors.activeEditor;

var range = sourceEditor.selectionRange;
var deleteLength = range.endingOffset - range.startingOffset;
var selection = sourceEditor.source.substring(range.startingOffset,range.endingOffset);

webView = views.getView("KodersWebView");
webView.showView(true);
webView.setTitle("Koders");
webView.url = "http://www.koders.com/default.aspx?btn=Search&la=*&li=*&s="+selection;
}

Now I want you to open a new editor (New > Untitled Text File) and type String.substring. Select what you have typed and run the script from Search > Koders; menu.

[img_assist|nid=743|title=|desc=|link=none|align=left|width=500|height=324]

 

 

 

 

 

 

 

 

 

 

 


Figure 6. Search result.

The result shouldn't be much different from what you see on the screen shot: List of all "String.substring"occurences in all code indexed by Koders

Sharing scripts

Scripts may be shared very easily. When you publish on your blog or a wiki page, anyone may copy and paste script into his workspace. There is only one rule you need to know. Scripts have to be enclosed by following Jabberwocky-inspired lines:

  --- Came wiffling through the eclipsey wood ---
... script goes here ...
--- And burbled as it ran! ---}

Once you remember that, you are free to share your scripts. For your convenience there is "Copy for publication" menu available when you right click on a script inside the project navigator.

[img_assist|nid=744|title=|desc=|link=none|align=left|width=460|height=182]

 

 

 

 

 

 

Figure 7. Scripts publishing made easy.

You may want to check the following scripts marketplace: http://monkey.brain-bakery.com/. This is the website when you can easily publish your work and search through others submissions.

GroovyMonkey

GroovyMonkey is a fork project started by James E. Ervin in May 2006. The project was initially created to test support of many additional languages: BeanShell, Python, Groovy and Ruby. Today it is more like test bed for future features, here is some of them:

  • contribution to outline view, giving you a handy tree view with a list of objects available in your scripts
  • enhanced header fields giving you more control over script execution (e.g. you may mark your script as UI Job)
  • additional DOM trees

[img_assist|nid=745|title=|desc=|link=none|align=left|width=500|height=338]

 

 

 

 

 

 

 

 

 

 

 

Figure 8. GrooveMonkey in action.

The set of GroovyMonkey features is far more than just a few tweaks, it deserves own article. This is definitively a project you want to try. Just remember to uninstall or disable the Monkey, before you start installing GroovyMonkey.

Summary

As I mentioned at the begining, the project is still evolving. To keep yourself in sync with the latest changes you should take a look at the sources. Installing them is easier than you may think. Go to project's homepage and check.

Now when you are familiar with the Monkey, it is the time to start creating your own scripts. You have learned script's syntax and concept of DOMs contribution and sharing. This knowledge enables you to make development more productive. Start making your life easier and remember to share your scripts with others.

Resources

Published at DZone with permission of {{ articles[0].authors[0].realName }}, DZone MVB. (source)

Opinions expressed by DZone contributors are their own.

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}