Enhance Eclipse RCP with Scripting[ZT]

JSR-223 defines various kinds of interactions between scripting languages and the Java platform. Learn how to enhance the Eclipse platform and its Rich Client Platform applications with JSR-223 scripting capabilities.


ava Specification Request (JSR) 223 defines a set of APIs and a related framework to integrate the Java platform with scripting languages. The APIs are part of the standard library that ships with Java SE 6, so you get free scripting support whenever you run applications on a SE 6 JVM. This also applies to applications built upon the Eclipse platform.

JSR-223 defines various kinds of interactions between scripting languages and the Java platform, including:

  • Embedding interpreted scripts into Java applications
  • Modifying and controlling Java objects from within a scripting context
  • Using the Java language to write and expose script interpreters

This article demonstrates how to enhance the Eclipse platform and the applications built upon it with the power and benefits of scripting languages, taking advantage of these new Java SE 6 capabilities. You will learn how to extend the Eclipse Platform with scripts to enable the following:

  • Automate repetitive tasks you usually perform within your integrated development environment
  • Perform fast user interface (UI) prototyping by modifying both the UI and the control logic on the fly
  • Allow users to customize your applications beyond the common sets of preferences, enabling users to add small snippets of logic into their preferred scripting or domain specific languages (DSLs).

Most of these benefits derive from the nature of scripting languages. They generally are dynamically typed, in some cases are specific to a particular domain of problems, usually are simpler to learn and write than compiled languages, and not bound to a write-compile-run cycle like older languages such as Java and C.

What You Need
You need only a basic understanding of JSR-223 internals to follow this article. In fact, it is sufficient to understand only the following piece of code:

Map<String,Object> vars =
getScriptVariables(); // fictional method
String scriptBody = getScriptBody(); // fictional method
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine engine = sem.getEngineByExtension("js");

for (String key : vars.keySet())
engine.put(key, vars.get(key));

// ScriptException handling code omitted

The code basically does three things:

  1. It creates a javax.script.ScriptEngineManager (searching it by supported extensions), which is responsible for the discovery and instantiation of scripting engines. (Engines are the components that evaluate and effectively run scripts.)
  2. It sets a bunch of script variables with the ScriptEngine.put(String,Object) method, thereby defining a binding between a Java object and the scripting environment so the script will be able to manipulate such objects. More generally, the script can control the Java environment.
  3. It evaluates a given script with the eval(String script) method.

The JVM discovers available engines using the Service Provider mechanism, which involves scanning for particular configuration files in the META-INF/services directory of the jar files available to the application. To make a scripting engine available within your application, you need only add to the classpath a correctly configured jar file containing that script engine. This article uses some of the ones provided by the scripting project at dev.java.net, such as the Ruby and Groovy engines.

This is an overly reductive use of the scripting APIs to be sure, but it serves the tutorial purposes of this article. (Refer to the Related Resources section in the left-hand column for a link to more detailed documentation.)

The Scripting Plug-in and Its Fragments
In the accompanying source code for this article, the merge between scripting languages and Eclipse (see Sidebar 1. The Eclipse Rich Client Platform) goes through the definition of the com.devx.scripting plug-in (see Sidebar 2. Anatomy of an Eclipse Plug-in), which provides the rest of the platform with common access to scripting resources and a set of plug-in fragments-one for every scripting language the application is going to support as shown in Figure 1.

Click to enlarge

Figure 1. The com.devx.scripting Plug-in Architecture

Every fragment contributes a given interpreter (Ruby, JavaScript, AppleScript, etc.) and its respective JSR-223 engine, which exposes the interpreter through the javax.script scripting API. Interpreters and JSR-223 wrappers can be bundled together or developed and shipped separately.

This plug-in setup offers various advantages, including the following:

  • You can control explicitly which languages are added to the platform by limiting the number of distributed fragments.
  • The user can slim down your application's installation by selecting from the update site only the scripting languages he or she needs.

The plug-in defines the IScript interface, which represents the script shown in following listing:

public interface IScript {
// @return the URI which points to the script code.
public String getURI();
// @return the script extension
public String getExtension();
// @return a Reader which points to the script code
public Reader getReader() throws IOException;
// @return the script unique id
public String getId();
// @return the namespace (plug-in id) of the script
public String getNamespace();
// @return run the script in a modal context?
public boolean isModal();

The scripting plug-in exposes the com.devx.scripting.ScriptSupport class, which defines public methods for common script-related needs that arise within the Eclipse platform. These needs include running a script in the context of a progress monitor (such as the one shown while compiling sources in Eclipse), or retrieving a list of the supported languages by querying the ScriptEngineManager. The following listing shows part of the public interface of the class (Refer to the source code for the implementation):

public void runScript(final IScript script,
Map<String,Object> params) throws ScriptException;
public List<Language> getSupportedLanguages();

Execution of External Scripts
With only these basic elements, you can already provide a way to run custom scripts inside your Eclipse application. As an example, you can contribute an Eclipse action that allows the user to choose a script from the file system and run it within the platform. Figure 2 and Figure 3 preview the final results.

Figure 2. The 'Run Script' Action

Figure 3. The 'Run Script' File Selector Allowing Scripts of All Supported Types
The action shows a file selector that filters only the available scripting languages by querying the com.devx.scripting.ScriptSupport class, which in turn asks the javax.script.ScriptEngineManager for the supported languages. Finally, the javax.script.ScriptEngineManager scans the plug-in classpath and its fragments using the Service Provider mechanism.

To obtain the previewed result, you define an extension to the org.eclipse.ui.actionSets extension point, which provides an additional menu action to the application, implemented by the com.devx.scripting.actions.RunScriptAction class shown in Listing 1.

That's it. You can now run Ruby, Groovy, and other scripts within your application, taking advantage of their power and peculiarities. You can prepare scripts that perform a bulk change on part of the workspace or you can have part of your build and deploy process written in a scripting language and invoked by the developer from within the platform when needed.

Scripted Contributions to the Platform
Now you're ready to take a step forward and explore a way to directly access and modify the Eclipse platform using a scripting language and use scripts to add contributions to the platform. This requires the definition of a binding layer between scripted contributions and the platform. Such layer will have:

  • Extension points that mimic the standard Eclipse ones such as scriptedView instead of a standard view contribution item to provide Eclipse views backed by a script
  • Proxy classes that implement the standard Eclipse interfaces such as org.eclipse.ui.part.ViewPart and delegate method calls to the underlying script
  • An extension to the org.eclipse.ui.startup extension point to perform all the plumbing and binding between the scripting contributions and the platform at startup time .

Consider the sample case of an Eclipse view backed by a JavaScript implementation. Figure 4 shows the entire cycle for the sample case and differentiates between actions performed at configuration/startup time and those performed at runtime. (See the sample code for other types of contributions, such as Eclipse action sets.)

Click to enlarge

Figure 4. The Cycle That Handles Scripted Contributions

The first step is defining the contribution, as shown in the following listing:

name="JavaScript Calculator">

The extension is very similar to the standard org.eclipse.ui.views. The only difference is the additional <script> element, which defines the underlying script (the uri attribute can point to either a resource within the plug-in by using a relative URI or an external resource using the file:// scheme).

When the application starts up, the class com.devx.scripting.ScriptingStartup, registered as an extension to the org.eclipse.ui.startup extension point, scans available scripted contributions and dynamically adds them to the platform via the IExtensionRegistry.addContribution() method. Note that dynamic contributions require a special permit, which you give by adding the -Declipse.registry.nulltoken=true command-line option when launching the application.

The ScriptingStartup class translates between the scripted contribution and the contribution that will act as a proxy for your scripts. Listing 2 shows the getContribution() method involved in the translation process.

As you can see, the code just copies the scripted contribution and translates it into a standard contribution to org.eclipse.ui.views. The view implementation class com.devx.scripting.view.ScriptProxyView will delegate to the underlying script the calls made to the view by the Eclipse platform. For example, the following listing shows the delegation of the rendering process to the script, performed when Eclipse calls createPartControl(Composite parent) to draw the view:

scriptParent = new Composite(parent,SWT.NONE);
scriptParent.setLayout(new FillLayout(SWT.HORIZONTAL));

// this call returns the script associated with this view
IScript script = getScript();

Map<String,Object> params = new HashMap<String,Object>();
params.put("parent", scriptParent);
new ScriptSupport().runScript(script, params);

This approach enables the developer to perform fast UI prototyping, since a change to the script is immediately reflected in the view (he or she can just close and reopen it).

You are now able to use the benefits of scripting languages to achieve results that are complex to obtain with traditional Java code, such as a simple calculator. Using JavaScript and its eval() function, you do not have to worry about writing code to evaluate the mathematical expressions submitted by the user. Figure 5 shows the final result, and Listing 3 shows the JavaScript code that produces it.

Click to enlarge

Figure 5. The JavaScript Calculator

The refresh button will reload the script and immediately apply changes to the UI.

Scripting languages can help draw interfaces within Eclipse in many other scenarios. For example, you can take advantage of the builder paradigm by using Groovy to build SWT interfaces or this Ruby library to build SWT interfaces with Ruby.

Towards Full Integration of Scripting Languages Within Eclipse
Now that you know how easy the integration between Eclipse and the new scripting capabilities offered by the Java platform is, you can wrap engines into the Eclipse plug-in architecture and invoke scripts from the Eclipse environment. You have also learned a way (not necessarily the best way) to use scripting languages at the very core of the Eclipse platform to provide extensions to standard extension points, such as views and action sets.

In addition to the approach described here, other projects bring scripting support to the Eclipse platform, namely the Eclipse Monkey and the EclipseShell projects. They were omitted from this discussion mainly because at the time of writing they weren't yet updated to support JSR-223, which is the main focus of the article. However, both feature some interesting ideas that have in part been ported into the sample code (such as monkey doms, which use Eclipse extensions to provide custom objects to the scripts). It's worth spending some time with them.

posted on 2007-05-22 23:19 XiaoLi 阅读(1468) 评论(0)  编辑  收藏 所属分类: Eclipse





商业性使用-禁止演绎 2.5 中国大

Books I've Translated




《RESTful Web Services中文版》
RESTful Web Services中文版

51.la 专业、免费、强健的访问统计