by MarcinMaciukiewicz
09/14/2007
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 shame. 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 scritps.
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 all who lack of knowledge read the following short publications:
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)

Figure _. The Monkey project - installation
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.
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.

Figure _. Location defined in a header makes script available
in the menu.
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.
After first bite of the Monkey it is time to show you how it works inside. Look at the following UML diagram.

Figure _. UML diagram presenting major Monkey
elements.
EclipsePlugin creates "Scripts" menu and its
content, then registers 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
producting script runner, language factory's role is to
understand script's header and produce
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 script. For now there is
only JavaScriptRunner implementation available,
allowing to create the Monkey scripts in JavaScript. Other
languages are supported by forked project GroovyMonkey, which I
will cover later.
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. Following sections will explaing details.
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 an 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
classThis 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.
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 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 meet header from preceding code? Try
by yourself, by starting the script. Select from "Scripts" menu
OnJava > ExampleDOM item. You should see warning
dialog, complaining about missing DOM.

Figure _. The "Missing DOM" dialog.
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. Plug-in that delivers DOM may be downloaded and made available to a script, thanks to entry in a header. This is what Monkey see:
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:
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:
> 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:

Figure _. Example script's location.
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.

Figure _. Search result.
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! ---}
Since 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.

Figure _. Scripts publishing made easy.
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:

Figure _. GrooveMonkey in action.
As I mentioned at the begining, the project is still evolving. To keep yourself in sync with 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.