简体   繁体   中英

GroovyScriptEngine fails to load imports of running groovy script

Background :

I started playing with Groovy recently and am trying to embed a groovy script engine in an eclipse plugin to let my customers develop their own GUI extensions inside my eclipse-based product. This is very similar to the success story published on codehaus's website.

Problem

The groovy script (let's call it "main_eclipse.groovy") run from the eclipse plugin by a GroovyScriptEngine throws when trying to load a groovy class ("SwtGuiBuilder"), with the following error :

BUG! Queuing new source whilst already iterating. Queued source is 'file:/home/nicolas/workspace/groovy-test/src/gui/SwtGuiBuilder.groovy'

Question

Did anyone run into the same problem ? How can it be fixed ? Any help will be highly appreciated !

Some observations :

  • When using the groovy interpreter instead of a GroovyScriptEngine java object, I have no problem using my SwtGuiBuilder class (see script "main_groovy" here below).

  • My problem does not seem to be a classpath issue, since the file containing my SwtGuiBuilder class is mentioned in the thrown exception.

  • The error message is mentioned in two reported groovy bugs, GRECLIPSE-429 and GRECLIPSE-1037. I did not fully get the technicals details, but those bugs seemed to be related to performance issues when loading lots of classes, which is not relevant in my situation...

Details

SampleView.java

public class SampleView
{
public SampleView() { super(); }

public void createPartControl(Composite parent) 
{
    String    groovyScript = null;
    String [] groovyPath   = null;

    boolean shall_exit = false;
    do
    {      // ask user for params
        GroovyLocationDialog groovyLocationDialog= new GroovyLocationDialog(parent.getShell() );
        int return_code = groovyLocationDialog.open();
        if ( return_code != Window.OK )
            shall_exit = true;
        else 
        {
            groovyScript= groovyLocationDialog.getInputScriptName();
            groovyPath  = groovyLocationDialog.getInputScriptPath();

            // run it
            ScriptConnector scriptConnector = new ScriptConnector(parent);
            try                 { scriptConnector.runGuiComponentScript( groovyPath, groovyScript); }
            catch (Exception e) { e.printStackTrace(); }
            System.out.println("script finished");
        }
    }
    while ( ! shall_exit );
}

ScriptConnector.java

public class ScriptConnector
{
    private String[]  roots;
    private Composite window;
    private Binding   binding;

    public ScriptConnector( Composite window )
    {
         this.window    = window;
         Binding  scriptenv = new Binding();    // A new Binding is created ...
         scriptenv.setVariable("SDE", this); 
         scriptenv.setVariable("WINDOW", this.window); // ref to current window

         this.binding = scriptenv;
    }

    public void runGuiComponentScript(final String[] groovyPath, final String scriptName) 
    {
        GroovyScriptEngine gse = null;
        this.roots     = groovyPath;
        try 
        {
            // instanciating the script engine with current classpath
            gse = new GroovyScriptEngine( roots, this.getClass().getClassLoader() );
            gse.run(scriptName, binding);      // ... and run specified script
        }
        catch (Exception e) { e.printStackTrace(); }
        catch (Throwable t) { t.printStackTrace(); }
    }
}

main_eclipse.groovy

package launcher;

import org.eclipse.swt.SWT
import org.eclipse.swt.widgets.*
import org.eclipse.swt.events.*;
import org.eclipse.swt.layout.RowLayout as Layout

// This import will fail...
import gui.SwtGuiBuilder;


WINDOW.layout = new Layout(SWT.VERTICAL);
def builder = new SwtGuiBuilder(WINDOW);
builder.Label ( style=SWT.NONE, text = 'Simple demo of Groovy and SWT')
builder.Button( style=SWT.PUSH, text = 'Click me' , action = { println "Click !" } )

SwtGuiBuilder.groovy

package gui;

import org.eclipse.swt.events.*
import org.eclipse.swt.widgets.Button
import org.eclipse.swt.widgets.Composite
import org.eclipse.swt.widgets.Label


class SwtGuiBuilder
{
    private Composite _parent

    public SwtGuiBuilder(Composite parent) { _parent = parent }

    public void Button( style = SWT.PUSH, text= null, action = null )
    {
        def btn = new Button(_parent, style)
        if ( text != null )
            btn.text = text
        if (action != null)
            btn.addSelectionListener( new SelectionAdapter() { void widgetSelected( SelectionEvent event ) { action(); } } );
    }

    public void Label( style = SWT.NONE, text = '' )
    {
        def lbl = new Label(_parent, style)
        lbl.text = text
    }
}

main_groovy.groovy

package launcher;

import org.eclipse.swt.SWT
import org.eclipse.swt.widgets.*
import org.eclipse.swt.events.*;
import org.eclipse.swt.layout.RowLayout as Layout

// ... But this import is handled properly !
import gui.SwtGuiBuilder;

def display = new Display()
def WINDOW = new Shell(display)
WINDOW.text = 'Groovy / SWT Test';

WINDOW.layout = new Layout(SWT.VERTICAL);
def builder = new SwtGuiBuilder(WINDOW);
builder.Label ( style=SWT.NONE, text = 'Simple demo of Groovy and SWT')
builder.Button( style=SWT.PUSH, text = 'Click me' , action = { println "Ya clicked me !" } )

WINDOW.pack();
WINDOW.open();

while (!WINDOW.disposed) {
    if (!WINDOW.display.readAndDispatch())
        WINDOW.display.sleep();
}

Stack trace

BUG! Queuing new source whilst already iterating. Queued source is 'file:/home/nicolas/workspace/groovy-test/src/gui/SwtGuiBuilder.groovy' at org.codehaus.groovy.control.CompilationUnit.addSource(CompilationUnit.java:460) at org.codehaus.groovy.control.CompilationUnit.addSource(CompilationUnit.java:433) at groovy.util.GroovyScriptEngine$ScriptClassLoader$3.findClassNode(GroovyScriptEngine.java:195) at org.codehaus.groovy.control.ClassNodeResolver.resolveName(ClassNodeResolver.java:124) at org.codehaus.groovy.control.ResolveVisitor.resolveToOuter(ResolveVisitor.java:863) at org.codehaus.groovy.control.ResolveVisitor.resolve(ResolveVisitor.java:377) at org.codehaus.groovy.control.ResolveVisitor.visitClass(ResolveVisitor.java:1407) at org.codehaus.groovy.control.ResolveVisitor.startResolving(ResolveVisitor.java:202) at org.codehaus.groovy.control.CompilationUnit$1.call(CompilationUnit.java:713) at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:1015) at org.codehaus.groovy.control.CompilationUnit.doPha seOperation(CompilationUnit.java:647) at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:596) at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:279) at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:258) at groovy.util.GroovyScriptEngine$ScriptClassLoader.doParseClass(GroovyScriptEngine.java:247) at groovy.util.GroovyScriptEngine$ScriptClassLoader.parseClass(GroovyScriptEngine.java:229) at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:244) at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:202) at groovy.util.GroovyScriptEngine.loadScriptByName(GroovyScriptEngine.java:514) at groovy.util.GroovyScriptEngine.createScript(GroovyScriptEngine.java:564) at groovy.util.GroovyScriptEngine.run(GroovyScriptEngine.java:551)

My configuration :

  • Linux Ubuntu 14.04 x86

  • Groovy Version: 2.3.2

  • JVM: 1.7.0_55

  • Eclipse Kepler SR2 - Build 20140224-0627

  • Eclipse Groovy plugin v2.0.7

Instead of GroovyScriptEngine, I've used the GroovyShell class (groovy code below but easy enough to change back to java), CompilerConfiguration allows you to specify the classpath.

    def config = new CompilerConfiguration(classpath: classpath)
    def binding = new Binding()

    def result = new GroovyShell(binding, config).evaluate("""
def foo='bar'
""")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM