3

I am attempting to run Python code on a Coldfusion server using Java. I am familiar with CFML but an absolute beginner with Java.

I can instantiate the objects and list their methods ok, however I am getting stuck with different object types.

The example I am trying to get to work in Coldfusion is

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class JSR223 {

    public static void main(String[] args) throws ScriptException {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("python");
        engine.eval("import sys");
        engine.eval("print sys");
        engine.put("a", 42);
        engine.eval("print a");
        engine.eval("x = 2 + 2");
        Object x = engine.get("x");
        System.out.println("x: " + x);
    }
}

What I have so far in CFML

 ScriptEngine        = CreateObject("java", "javax.script.ScriptEngine");
 ScriptEngineManager = CreateObject("java", "javax.script.ScriptEngineManager");
 ScriptException     = CreateObject("java", "javax.script.ScriptException");

The part I am stuck on

 ScriptEngine engine = new ScriptEngineManager().getEngineByName("python");

How can I create that in CFML?

Edit:

 engine = ScriptEngineManager.getEngineByName("python");
 writeDump(engine);

Gives me an error: variable [ENGINE] doesn't exist

How does the other class, ScriptEngine fit in with this?

ScriptEngine ScriptEngineManager

Update

I can load other Python classes so I think that the jar is installed correctly. ie with the following code I can dump the interp object.

 interp = CreateObject("java", "org.python.util.PythonInterpreter");

However even then calling a method gives me this error

 java.lang.NullPointerException
2
  • run Python code on a Coldfusion server Out of curiosity, is it something that could/should be done in pure cfml? Commented Oct 26, 2016 at 1:17
  • This is more of an exercise than anything else. I would like to have the ability to access python libraries that don't exist in CFML. Commented Oct 26, 2016 at 12:42

2 Answers 2

2

The keyword new invokes the class constructor. ColdFusion does not support new with java objects. Instead, use the psuedo-method init():

The init method is not a method of the object, but a ColdFusion identifier that calls the new function on the class constructor.

A literal translation of that code is to chain the calls. Invoke init() first, to create a new instance. Then call getEngineByName() on that instance:

engine = createObject("java", "javax.script.ScriptEngineManager").init().getEngineByName("python");

Though for better readability, you may want to break it up:

ScriptEngineManager = createObject("java", "javax.script.ScriptEngineManager").init();
engine = ScriptEngineManager.getEngineByName("python");

As an aside, in this specific case, you can technically omit the call to init(). ColdFusion will automatically invoke the no-arg constructor as soon as you call getEngineByName():

...If you call a public non-static method on the object without first calling the init method, ColdFusion makes an implicit call to the default constructor.

Update based on comments:

If engine is not defined, that means the "python" engine was not found. Be sure you have added the the jython jar file to the CF class path (or loaded it via this.javaSettings in your Application.cfc). Once it is registered, the code should work correctly. For some reason it does not work if you load the jar dynamically through ACF's this.javaSettings. However, it works fine if you place the jython jar in WEB-INF\lib and restart CF. Try adding the jar to the physical CF class path, rather than loading it dynamically and it should work correctly.

It also works from CF if you manually register the engine first (see below). Not sure why that extra step is necessary when ScriptEngineManager is invoked in CF, but not from Eclipse.

ScriptEngineManager = createObject("java", "javax.script.ScriptEngineManager").init();
factory = createObject("java", "org.python.jsr223.PyScriptEngineFactory").init();
ScriptEngineManager.registerEngineName("python", factory);
engine = ScriptEngineManager.getEngineByName("python");
// ...

How does the other class, ScriptEngine fit in with this?

Unlike CF, Java is strongly typed. That means when you declare a variable, you must also declare its type (or class). The original code declares the engine variable as an instance of the ScriptEngine class. Since CF is weakly typed, that is not necessary. Just declare the variable name as usual. The getEngineByName() method automatically returns a ScriptEngine object (by the definition in the API).

Sign up to request clarification or add additional context in comments.

9 Comments

I edited the question to show where it wasn't working. I'm not clear how the ScriptEngine class fits into this.
If engine is not defined, that means the python engine was not found. Does the original example work for you in java?
jython is working ok. I added cfdumps of ScriptEngine and ScriptEngineManager.
(Edit) jython is working ok Not necessarily. The javax.script.* are core libraries, ie always exist. Just because those classes are working, does not mean Jython is working. It is a custom library which has to be loaded explicitly. If you are getting undefined, then it is not loaded/working.
Tested Jython and looks good. Can create org.python.util.PythonInterpreter in CFML.
|
1

I was able to get Leigh's example working in Lucee using the following code and putting the jar in the same directory as the .cfm file. I originally put it in the same folder as the Lucee jar but it was only partially working.

ScriptEngineManager = createObject('java','javax.script.ScriptEngineManager').init();
factory = createObject('java','org.python.jsr223.PyScriptEngineFactory','jython-standalone-2.7.0.jar').init();
ScriptEngineManager.registerEngineName("python", factory);
engine = ScriptEngineManager.getEngineByName("python");

engine.eval("x = 2 + 2");
x = engine.get("x");

writeDump(x);

3 Comments

Thanks for posting the working Lucee code. RE: originally put it in the same folder.... You should be able to put jar files anywhere that is accessible to the CF engine. Maybe you forgot to specify the full path to the jar file in createObject()?
That could be it. I didn't specify the full path in createObject, however the initial object creation still worked.
Yes, any of the javax.script.* classes would always work. That lib is bundled with the JRE and does not require any extra jars. For custom lib's, like jython, it is a good to specify a full path to the jar, even if it is not required. Then you do not have to guess how ACF/Lucee resolves the relative path. If portability is a concern, just use ExpandPath("./jython-standalone-2.7.0.jar").

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.