Pages

Tuesday, July 31, 2007

A Case for Groovy Singleton

In my last post I've written about the new feature in Groovy API, the GroovySystem class. Now I would like to give it a try for the singleton pattern implementation inside the plug-in extension.

Why would I ever want to do that? The singleton class could be used for persisting the state between the same action calls. I wanted to try this solution just because it must be possible to implement the same thing in the different ways. Nothing else.

Here's an example:

def expando = new groovy.util.Expando()

class MySingleton {
String toString() { 'MySingleton: ' + hashCode()}
}

class MySingletonMetaClass extends MetaClassImpl {
private final static INSTANCE = new MySingleton()
MySingletonMetaClass() { super(MySingleton) }
def invokeConstructor(Object[] arguments) { return INSTANCE }
}

def registry = GroovySystem.metaClassRegistry
registry.setMetaClass(MySingleton, new MySingletonMetaClass())

expando.run = {action ->
expando.workbenchWindow.getActivePage().
showView("org.eclipse.soc.yummy.views.ScriptView")
println "hello groovy action: ${action}"
def single = new MySingleton()
println "${single}"
}

I just create a singleton using the GroovySystem and MetaClassImpl, so that when an action delegate gets called, I will have always the same instance of the singleton class.

Unfortunately, I didn't find a way to implement the same in case of different scripts, e.g. if I have two different scripting extensions. Perhaps I will have to provide a single class to the plug-in which could be used just for this purpose.

Monday, July 30, 2007

JSR 223 and Groovy 1.1 Beta 2

In Groovy version 1.1-beta-2 a new class groovy.lang.GroovySystem provides some nice features that I would like to use in my Google Summer of Code project. Namely, this is one way to implement a bundle activator for an eclipse plug-in. Not really an activator, actually, but sort of.

In a scripted bundle, the activator can be just a singleton class, and GroovySystem can be used to create a fancy singleton pattern in Groovy, like it is described at Groovy's homepage.

The problem I faced when I tried to initialize the scripting engine using javax.sript API, was that the engine implementation still thinks that groovy.lang.MetaClass is a class, but it is an interface in the latest version of Groovy.

So the stack trace of this exception was:

Exception in thread "main"
java.lang.IncompatibleClassChangeError:
at com.sun.script.groovy.GroovyScriptEngine
.(GroovyScriptEngine.java:63)
at com.sun.script.groovy.GroovyScriptEngineFactory
.getScriptEngine(GroovyScriptEngineFactory.java:87)
at javax.script.ScriptEngineManager
.getEngineByName(ScriptEngineManager.java:225)

The quickest fix I could do, is to download the scripting engine sources from the website, and comment out the line in GroovyScriptEngine's static initializer. After the recompilation I didn't get the stack trace any more, but I think I have to investigate a bit more, what could be affected by this change.

Thursday, July 19, 2007

The ViewPart Should Be Equal

I was preparing an example of a groovy view extension for my GSoC project tonight. First, I created a groovy class that extends org.eclipse.ui.part.ViewPart and added the required methods to that class:

class MyView extends ViewPart {
public void createPartControl(Composite composite) {
}
public void setFocus(){
}
}

def view = new MyView();
view

The first disappointment was that I couldn't write the method declarations in a groovy way, using def, because it was yelling that the abstract methods should be implemented. Thus I conclude that I cannot implement the abstract methods using def.

So far, so good... OK, when I executed the above script I got a nice stack trace, saying that "A part must be equal to itself", and/or "A part must have a meaningful equals method". This was a message from org.eclipse.ui.internal.PartTester#testWorkbenchPart(), that checks the brand new part for some strange things, like part.equals(new Integer(32)). Why 32, can anyone tell!?

So I implemented a goofy equals() for my script object, just to make it work. Here's the example code: view.groovy.

Wednesday, July 18, 2007

GSoC 2007: Equator Reached!

The midterm evaluation is complete and just over a month is left to do the project, and the deadline is approaching fast.

The last post about the facade implementation for each extension was just a concept showing what could be done for mapping the extension method calls to the script. But implementing a dedicated facade for each extension is not feasible just because there is an unlimited number of the extension types available.

As the previous post said, a proxy class solution could solve the problem so that I shouldn't write a dedicated facade for each extension. That said, we have a brand new solution in place: the ScriptExtensionProxy class, which implements the following interfaces:

  1. IExecutableExtension required for extension initial setup in ConfigurationElement#createExecutableExtension() calling IExecutableExtension#setInitializationData()

  2. IExecutableExtensionFactory required for proxy class construction using IExecutableExtensionFactory#create() call in ConfigurationElement#createExecutableExtension()

  3. InvocationHandler required to delegate the extension method calls to the script in InvocationHandler#invoke() method.

The example script still works the same way as previously, but no facade class required any more.

The declaration of an extension is changed a little bit, as I have to define a proxy class, an extension interface class and a script file in the class attribute in plugin.xml:

<action class="org.eclipse.soc.yummy.core.ScriptExtensionProxy: \
org.eclipse.ui.IWorkbenchWindowActionDelegate/action.groovy"
icon="icons/sample.gif"
id="ScriptAction"
label="&Sample Action"
menubarPath="sampleMenu/sampleGroup"
toolbarPath="sampleGroup"
tooltip="Hello, Eclipse world">
</action>
It looks a little messy though...

Overall, providing the scripting support for developing plug-ins in Groovy requires only three classes at the moment: a proxy, a script execution engine, and a helper class for initialization code that is actually used in the proxy class only. LOC = 355.


Next, I will have to make many examples to see what is missing in ScriptExecutor and to find out which workbench objects should I expose to the script. I still need to refer to Dash for this purpose.

Sunday, July 8, 2007

Eclipse plug-ins and JSR-223

Recently I was trying to bind the eclipse plug-in world with JSR-223 and Groovy via javax.script API for my GSoC project. Here's how it turned out.

First I need to have a class that deals with all that javax.script stuff: let there be ScriptingExecutor. Its functionality isn't very advanced - it just delegates the calls to the scripting API and maintains several fields for holding the state of the script. STOP! Why would I need to keep track of the state at all? The problem is that not all the scripting languages with the same semantics/features.

Let's see the example: an action facade and a script that implements this action. I'm using the Expando object to create a class with the same methods as for the action: run(IAction action), selectionChanged(IAction action, ISelection selection), dispose(). The reason why i would like to pass the new object back to the plug-in is that when a selection is changes I would like to save the reference for further processing in the run(IAction) method. The script below has a visibility problem and cannot use def selection to save the state.

def selection

def run(action){
println "selection = ${selection}" // this won't work!!!
}

def selection(action, selection){
this.selection = selection
}

The Groovy User Manual says:
Executing this code you get an exception talking about a missing property or field.
The only things the method has access to are:
* the binding,
* attributes defined by the base class, and
* the dynamic properties defined by the MetaClass (explanations for these will follow).
So I decided to use Expando as a base class for saving the state. Simple.

Next, there are 2 options of delegating the plug-in extension to the script object:

  1. Using the invokeMethod/invokeFunction methods of the javax.script.Invocable interfaces. These can be used just for simply calling the functions from the evaluated script. or...

  2. Using getInterface(Class clazz) to create an object that would implement the required interface, of instance IActionDelegate.

I'm not sure which option is really better so I'm using the first one for now.

Next, I should try to implement several facades and scripts for the extensions, so that I could see the common parts and unify them in one. Implementing a facade for every single extension does not seem to be very relevant, perhaps the getInterface(Class clazz) method will help to skip that... Or I will need some sort of Proxy solution to skip all the facades.

Also I'm referencing Eclipse Monkey project as an inspiration. The reason is that for smoothing the script development a set of predefined variables could be very handy, so the user could use them out of the box. And that is what Eclipse Monkey does: it exposes some internal object into the script context so that a script contributor could use them.

Wednesday, July 4, 2007

The Summer of Code goes on

I feel the guilt because of not posing about the GSoC event so far. Perhaps should start blogging more about what I am doing at all.

For the project I have 3 goals now, sorted by priority:

1. Provide the ability to write the plug-ins in a scripting language.
Basically it means that a new plug-in could be implemented in any scripting language, supported by JSR-223. My mentor, Wayne, purposed a tricky solution to bind a script to a popup action using a syntax feature for the action definition in plugin.xml:
<action
class="scriptexecutor.actions.ExecuteScriptAction:my.script"
icon="icons/sample.gif"
id="scriptexecutor.actions.SampleAction"
label="&Sample Action"
menubarPath="sampleMenu/sampleGroup"
toolbarPath="sampleGroup"
tooltip="Hello, Eclipse world"/>

An action class ExecuteScriptAction will be provided by the supporting plug-in, and my.script is something that is implemented by the new plug-in developer.
ExecuteScriptAction will act as a facade to the scripting world, setting the required variables to the script context and running the script itself, using javax.scripting API.
public class ExecuteScriptAction 
implements IWorkbenchWindowActionDelegate,
IExecutableExtension {

private URL script;

...

@Override
public void setInitializationData(IConfigurationElement config,
String propertyName, Object data)
throws CoreException {
Bundle bundle=Platform.getBundle(config.
getContributor().getName());
Enumeration entries = bundle.findEntries("/",
data.toString(), true);
if (entries == null) return; // The script wasn't found.
URL entry = null;
if (entries.hasMoreElements()) entry =
(URL)entries.nextElement();
this.script = entry;
}

...
}

So basically, setInitializationData() sets the parameter to the action class and after that we can do whatever we would like to with the script file when IActionDelegate#run() is called for this class. Most probably I will have to pass a pointer to the current workbench to the script context, like Eclipse Monkey does.

2. Enable custom processing for plugin.xml, to be able test the new plug-ins without starting the 2nd eclipse instance.
This was actually the initial idea, but it seems that enabling scripting for the bundled plug-ins is more valuable. Probably it is better to bundle this functionality as a separate plug-in even. Processing plugin.xml will involve parsing the configuration and registering the extensions to the same workbench where the brand new plug-in is being developed.
IExtensionRegistry registry = Platform.getExtensionRegistry();
IExtensionPoint point = registry.getExtensionPoint
(EXTENSION_POINT_ID);
IExtension[] extensions = point.getExtensions();

for (IExtension element : extensions) {
// register the extension
}

3. The console, for providing the interactive (re)loading of the plug-in. I'm not very sure yet, will it be a Apache Derby ij style console, or will it be a set of controls, but definitely it is a good feature to implement to smooth plug-in prototyping.

Now, after a good vocation in Croatia, will have to speed up things a bit to be in time with the implementation.

Disqus for Code Impossible