cancel
Showing results for 
Search instead for 
Did you mean: 

Serialization bug in scripts with JDK >= 1.6.0_43

matutano6
Champ in-the-making
Champ in-the-making

Hi,

I'm using Activiti 5.17.0, and I recently upgraded to JDK 1.7 from 1.6.0_26 and reached a problem with serialization in scripts. Searching forums, I found several articles regarding the issue. Some of them are:

Bug on JDK 1.7.0_17 when using ScriptTask in Activiti | Small steps with big feet 

couldn't find a variable type that is able to serialize [object global] 'nashorn.global' 

Script Task exception serizlization 

As I could see, the issue is being workarrounded with an explicit variable set (i.e. execution.setVariable("varName", value)), but in my case, I'm using ScriptExecutionListeners, wich still seem to fall in the bug regardless the explicit variable set.

I would like to ask if there is a specific way or workarround to continue using the ScriptExecutionListeners (and ScriptTaskListeners also). At the moment, I had to extract scripts listeners to a ScriptTask, but it generates confusion in the business undertanding when viewing the BPMN model.

As Oracle removed the bug report at http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=9000991, I think they aren't going to fix it or made the behaviour intentional for some reason. In that way, is there going to be a deffinitive fix included from the Activiti side in aome new release?

Any suggestion will be very appreciated. Thanks!

M

1 ACCEPTED ANSWER

afaust
Legendary Innovator
Legendary Innovator

Ahh, too bad the script is interpreted and not compiled. It looks like the problem in your case has nothing to do with the setVariable() and/or resultVariable at all. The issue originates from SimpleScriptContext.setAttribute which is only called for variable local variable assignments. The Activiti engine creates a custom script binding linked to the current execution. Whenever a local variable is defined in a script it will store that variable in the execution. The cause may be your use of the "var" keyword. In JavaScript all variables defined via "var" will be initialised with "undefined" at the start of their enclosing scope (script file / function). The statement

var rv = "hello";

actually results in two distinct runtime instructions

var rv;
rv = "hellO";

Please try running your process without the "var" keyword. Note that Activiti will still store all variables in the execution by default.

Another option would be to wrap the listener code in an anonymous function to avoid adding to the implicit global scope

(function(){
   var rv = "hello";
   execution.setVariable("result", rv);
}());

I have only ever used the Alfresco ECM specific script execution listener and this does behave quite a bit different in this context, without requiring any of these shenanigans (even after upgrades from Java 6 to 7 or 8).

View answer in original post

12 REPLIES 12

afaust
Legendary Innovator
Legendary Innovator

Why did you only upgrade from an extremely obsolete Java version to another obsolete Java version? Java 7 will have no more public updates and should be considered out of support for all intents and purposes.

If you do not use the "resultVariable" parameter on the ScriptExecutionListener it should not try to automatically store the result of the script execution. Then only the explicit setVariable operation will store data. I see no reason you needed to switch to ScriptTask if you did not use "resultVariable" before.

Before storing / using any variable from a script it is usually a good idea to check for undefined / null. Despite the tight integration of the JavaScript runtime into Java via JSR-223 you should not assume any value can just be stored. E.g. in the last two of the three links to other issues the specific developer should have made sure to convert the script values to Java values before storing them via a Java API exposed to JavaScript. For JS arrays, Nashorn (Java 8+) provides a utility function in the form of Java.to(array, 'List') to convert a script array to a Java array or List.

matutano6
Champ in-the-making
Champ in-the-making

Hi Axel,

thanks for you answer. I would like to clarify some of your questions.

In production environments, I'm using Debian stable, wich IFAIK has no Java 8 yet. In any case, Java 8 has not corrected the problem. The exception message changes a little, but it's still there until the last version of Java 8 I could try.

Regarding checking null and undefined variables, the problem has nothing to do with that. The bug exposes every time the script is executed, havig no null or undefined values at all. For example, this script fails:

<script>

   var rv = "hello";

   rv; // to be assigned to a varible named after parameter "resultVariable"

</script>

While this one does not:

<scipt>

   "hello"; // to be assigned to a varible named after parameter "resultVariable"

</script>

I would like also to share the results of another test I did yesterday: I can reproduce the problem with Oracle JDK >= 1.6.0_43 (1.7 and 1.8 too) and some version of IBM JDK 1.6, but it does work well with OpenJDK 1.7.0_121, and I assume with greater versions of OpenJDK will work too. This may help to others in my situation, who can migrate to OpenJDK.

At least for me, the doubt is still out there: is it a bug or is it a decision? And in the second scenario, shouldn't Activiti Engine provide a fix or removing "resultVariable" as an option?

Thanks again,

M

afaust
Legendary Innovator
Legendary Innovator

The problem is that Activiti Engine cannot provide a fix for the way script engines are integrated in Java - they can only remove remove it as "too easy to shoot yourself in the foot with". The problem with the "resultVariable" handling is that it relies on the implicit return value of the last statement in the script. Depending on the script engine as well as its internal optimisations (e.g. compiling away code lines that don't "do anything") the result value might be something the developer / user did not consider (undefined in the case of this issue). Likely the Java 7 release has seen an update to the integrated Rhino engine that added optimisations / superflous code elimination to change the implicit return value in your scripts.

From what I know about the various script engines it should neither be considered a bug nor a specific decision - it is more a matter of poor documentation of conceptional limits / poor expectation management. For that reason I consider setVariable not as a workaround, but the canonical / correct approach in the first place, and have used it as such since starting with Activiti in 2012.

matutano6
Champ in-the-making
Champ in-the-making

Well, I tried avoiding "resultVariable" parameter and the problem still happens. The same process definition running with Sun JDK < 1.6.0_43 or OpenJDK works fine, but whe I use the above mentioned JVM, I get the exception.

So, at the moment, I cannot make script listeners to work. I tried this script without success (and without "resultVariable" parameter):

<script>

var rv = "hello";
execution.setVariable("result", rv);

</script>

The exception message says:

"couldn't find a variable type that is able to serialize sun.org.mozilla.javascript.internal.Undefined@1b437790"

I am facing another issue?

Thanks,

M

afaust
Legendary Innovator
Legendary Innovator

Hmm - are you sure you are running the process in the correct definition version? Now there definitely should not be an undefined anywhere. Can you try to get a stacktrace for the error to check that it is using the correct code path?

matutano6
Champ in-the-making
Champ in-the-making

I'm using an ad-hoc test definition and I undeployed every preivous version before trying a new one to be sure just one version is available at a time.

I'll get the stacktrace from the log and paste it in a further message.

matutano6
Champ in-the-making
Champ in-the-making

I uploaded the test definition I'm using, if you would like to see it. I'm pretty sure there are no undefines anywhere.

 

matutano6
Champ in-the-making
Champ in-the-making

And below is the relevant part of the stacktrace.

The code involved is this:

public VariableType findVariableType(Object value) {
    for (VariableType type : typesList) {
        if (type.isAbleToStore(value)) {
            return type;
        }
    }
    throw new ActivitiException("couldn't find a variable type that is able to serialize " + value);
}

I should do some debug to see what types are in "typesList" and why aren't any of them able to store the value, wich BTW, seems to be undefined regardless I cannot see how could that happen with the definition I'm using.

org.activiti.engine.ActivitiException: couldn't find a variable type that is able to serialize sun.org.mozilla.javascript.internal.Undefined@1b437790
        at org.activiti.engine.impl.variable.DefaultVariableTypes.findVariableType(DefaultVariableTypes.java:62)
        at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableInstance(VariableScopeImpl.java:690)
        at org.activiti.engine.impl.persistence.entity.ExecutionEntity.createVariableInstance(ExecutionEntity.java:1171)
        at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableLocal(VariableScopeImpl.java:604)
        at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableLocal(VariableScopeImpl.java:591)
        at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:485)
        at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:442)
        at org.activiti.engine.impl.scripting.ScriptBindings.put(ScriptBindings.java:83)
        at javax.script.SimpleScriptContext.setAttribute(SimpleScriptContext.java:228)
        at com.sun.script.javascript.ExternalScriptable.put(ExternalScriptable.java:182)
        at sun.org.mozilla.javascript.internal.ScriptableObject.defineProperty(ScriptableObject.java:1396)
        at sun.org.mozilla.javascript.internal.ScriptRuntime.initScript(ScriptRuntime.java:3238)
        at sun.org.mozilla.javascript.internal.Interpreter.initFrame(Interpreter.java:2695)
        at sun.org.mozilla.javascript.internal.Interpreter.interpret(Interpreter.java:844)
        at sun.org.mozilla.javascript.internal.InterpretedFunction.call(InterpretedFunction.java:162)
        at sun.org.mozilla.javascript.internal.ContextFactory.doTopCall(ContextFactory.java:430)
        at com.sun.script.javascript.RhinoScriptEngine$1.superDoTopCall(RhinoScriptEngine.java:116)
        at com.sun.script.javascript.RhinoScriptEngine$1.doTopCall(RhinoScriptEngine.java:109)
        at sun.org.mozilla.javascript.internal.ScriptRuntime.doTopCall(ScriptRuntime.java:3160)
        at sun.org.mozilla.javascript.internal.InterpretedFunction.exec(InterpretedFunction.java:173)
        at sun.org.mozilla.javascript.internal.Context.evaluateReader(Context.java:1169)
        at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:214)
        at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:240)
        at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233)
        at org.activiti.engine.impl.scripting.ScriptingEngines.evaluate(ScriptingEngines.java:87)
        at org.activiti.engine.impl.scripting.ScriptingEngines.evaluate(ScriptingEngines.java:69)
        at org.activiti.engine.impl.bpmn.listener.ScriptExecutionListener.notify(ScriptExecutionListener.java:45)

afaust
Legendary Innovator
Legendary Innovator

Ahh, too bad the script is interpreted and not compiled. It looks like the problem in your case has nothing to do with the setVariable() and/or resultVariable at all. The issue originates from SimpleScriptContext.setAttribute which is only called for variable local variable assignments. The Activiti engine creates a custom script binding linked to the current execution. Whenever a local variable is defined in a script it will store that variable in the execution. The cause may be your use of the "var" keyword. In JavaScript all variables defined via "var" will be initialised with "undefined" at the start of their enclosing scope (script file / function). The statement

var rv = "hello";

actually results in two distinct runtime instructions

var rv;
rv = "hellO";

Please try running your process without the "var" keyword. Note that Activiti will still store all variables in the execution by default.

Another option would be to wrap the listener code in an anonymous function to avoid adding to the implicit global scope

(function(){
   var rv = "hello";
   execution.setVariable("result", rv);
}());

I have only ever used the Alfresco ECM specific script execution listener and this does behave quite a bit different in this context, without requiring any of these shenanigans (even after upgrades from Java 6 to 7 or 8).