Add property to custom AssetLoader class

Hi there!

I need help with the following:

TL;DR
I have a custom asset loader class and I don’t know how to access app instance variable from it.

What I have:
I have an application with assetManager and a ScriptManager class (see below). This scriptManager class has a GroovyScriptEngine defined.

[java]public class ScriptManager {

// ...

private GroovyScriptEngine engine;

// ...

public GroovyScriptEngine getEngine() {
    return engine;
}

}[/java]

What I want:
I want to load *.groovy script files with assetManager:
[java] Script script = (Script) assetManager.loadAsset(“Scripts/helloworld.groovy”);
script.invokeMethod(“onHelloWorld”, null);[/java]

However… I need the engine from ScriptManager class in order to create the Script files to return from GroovyAssetLoader. But I don’t know how to access the scriptManager instance to call the getEngine() method in GroovyAssetLoader.

[java] public class GroovyLoader implements AssetLoader {
@Override
public Object load(AssetInfo assetInfo) throws IOException {
GroovyScriptEngine engine = scriptManager.getEngine();
Script script = null;

        try {
            script = engine.createScript(assetInfo.getKey().getName(), scriptManager.createScriptBinding());
        } catch (ResourceException e) {
            console.severe("Failed to load script: " + assetInfo.getKey().getName());
            e.printStackTrace();
        } catch (ScriptException e) {
            console.severe("Failed to load script: " + assetInfo.getKey().getName());
            e.printStackTrace();
        }

        return script;
    }
}[/java]

Problem:
How can I access the application instance’s scriptManager property in the custom asset loader?

The problem is on GroovyLoader class at line 4. How can I get the scriptManager instance that is a property of the application instance?

You can make either the ScriptManager a static variable / access method or devise another way you load your script (e.g. make a transient instance of the script generating class or load “raw” data that you still pass to the ScriptManager after loading).

Thanks for the reply. I decided to devise a new way because the GroovyScriptEngine only accepts the path to the script as String object. I can’t give it the script as a String object.

Well… My new plan is to make the ScriptManager a singleton:

[java]public class ScriptManager {
private GroovyScriptEngine engine;
private static ScriptManager instance = null;

private ScriptManager() {
    try {
        
        engine = new GroovyScriptEngine("path/to/script/root");

    } catch(IOException ie) {
        // catch and handle exception here
    }
}

public static ScriptManager getInstance() {
    if(instance == null) {
        instance = new ScriptManager();
    }

    return instance;
}

public GroovyScriptEngine getEngine() {
    return engine;
}

}[/java]

So, this way I can access the engine:

[java] public class GroovyLoader implements AssetLoader {
@Override
public Object load(AssetInfo assetInfo) throws IOException {
Console console = Console.getInstance();
GroovyScriptEngine engine = ScriptManager.getInstance().getEngine();
Script script = null;

        try {
            script = engine.createScript(assetInfo.getKey().getName(), ScriptManager.getInstance().createScriptBinding());
        } catch (ResourceException e) {
            console.severe(“Failed to load script: ” + assetInfo.getKey().getName());
            e.printStackTrace();
        } catch (ScriptException e) {
            console.severe(“Failed to load script: ” + assetInfo.getKey().getName());
            e.printStackTrace();
        }

        return script;
    }
}[/java]

?? This way you have to specify the path to your assets twice and don’t load the script content via the asset system, completely bypassing the whole point of the asset loader system?

What do you mean by that I have to specify the path to my assets twice? As for the bypassing the whole point… yes, my hands are tied because I can’t tell GroovyScriptEngine that here is variable X with contents of a script file as string and make a groovy.lang.Script object from it for me please.

Why not just use the Java built in support for scripting along with the groovy bindings? It sounds like it would be more flexible for your needs.

http://docs.oracle.com/javase/6/docs/api/javax/script/package-summary.html
http://docs.oracle.com/javase/6/docs/api/javax/script/Compilable.html#compile(java.lang.String)

Will there be a performance difference if I use the built-in script support instead of Groovy’s script engine?

@kruze said: Will there be a performance difference if I use the built-in script support instead of Groovy's script engine?

It is using Groovy’s script engine. You just have to have the right jar in the classpath.

I further inspected my code and what you’ve linked and I will use the built in script support that you’ve mentioned. It’s waaaaaay more flexible than the solution I’m using now. This way, I won’t bypass assetManager, because I will load the script file as string with it and pass this string to the engine creating the script object itself. Thanks for everyone!

Allright… I tried a little of this and a little of that and my final solution is the following:

I load the script text with asset loader:

[java]public class GroovyLoader implements AssetLoader {
@Override
public Object load(AssetInfo assetInfo) throws IOException {

    InputStream is = assetInfo.openStream();
    StringBuilder builder = new StringBuilder();
    int ch;

    while((ch = is.read()) != -1) {
        builder.append((char) ch);
    }

    return builder.toString();
}

}[/java]

Then I pass it to GroovyShell:

[java] String scriptText = (String) assetManager.loadAsset(scriptName);
Script script = shell.parse(scriptText);
[/java]

Later I can call whatever I want from the script file. An example from a custom Control:

[java]@Override
protected void controlUpdate(float tpf) {
try {
script.invokeMethod(“onUpdate”, tpf);
} catch(Exception e) {
// Handle exception as you need…
}
}[/java]

Thanks for the help!