Can you help me out if the following code will work in a multi-threaded application.
Here is my Java Script that will be evaluated by Nashorn
var Thread = Java.type("java.lang.Thread");
var referenceNumberValid = "0000";
var referenceNumberInvalid = "0001";
function validate (parameters) {
var isValid = false;
var statusCode;
var referenceNumber = parameters.referenceNumber;
var validateNumber = referenceNumber.substr(0, 7);
var sum = 0;
for (ctr = 0; ctr < validateNumber.length; ctr++) {
sum += parseInt(validateNumber.substr(ctr, 1));
}
var checkDigit = sum % 10;
isValid = parseInt(referenceNumber.substr(7, 1)) == checkDigit;
statusCode = isValid ? referenceNumberValid : referenceNumberInvalid;
print("Thread: " + Thread.currentThread().getId() + ", isValid: " + isValid + ", referenceNumber: " + referenceNumber + ", validateNumber: " + validateNumber + ", sum: " + sum + ", checkDigit: " + checkDigit + ", statusCode" + statusCode);
return statusCode;
}
This is how I create my script engine
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(myScript);
While Testing using Testng this works without any errors
@Test(dataProvider = "validReferenceNumbers", timeOut = 3000)
public final void testValidReferenceNumber(String referenceNumber) throws NoSuchMethodException, ScriptException {
Map<String, String> parameters = new HashMap<>();
parameters.put("referenceNumber", referenceNumber);
Invocable invocable = (Invocable) engine;
Object result = invocable.invokeFunction("validate", parameters);
Assert.assertEquals(statusCode, "000");
}
@DataProvider(name = "validReferenceNumbers")
private Iterator<Object[]> validReferenceNumbers() throws FileNotFoundException {
Iterator<Object[]> testData = null;
// please assume that will be initilized correctly
return testData;
}
Test Results1 - note that some data are repeated
Thread: 10, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 11, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 12, isValid: true, referenceNumber: 17028884, validateNumber: 1702888, sum: 34, checkDigit: 4, statusCode0000
Thread: 13, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 14, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 15, isValid: true, referenceNumber: 17098881, validateNumber: 1709888, sum: 41, checkDigit: 1, statusCode0000
Thread: 16, isValid: true, referenceNumber: 17098881, validateNumber: 1709888, sum: 41, checkDigit: 1, statusCode0000
Thread: 17, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 18, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 19, isValid: true, referenceNumber: 17058887, validateNumber: 1705888, sum: 37, checkDigit: 7, statusCode0000
Thread: 20, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 21, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 22, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 23, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 24, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 25, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 26, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 27, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 28, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 29, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 30, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 31, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 32, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 33, isValid: true, referenceNumber: 17058887, validateNumber: 1705888, sum: 37, checkDigit: 7, statusCode0000
Thread: 34, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 35, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 36, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 37, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 38, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 39, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 40, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 41, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 42, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 43, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 44, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 45, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 46, isValid: true, referenceNumber: 07034880, validateNumber: 0703488, sum: 30, checkDigit: 0, statusCode0000
Thread: 47, isValid: true, referenceNumber: 07034880, validateNumber: 0703488, sum: 30, checkDigit: 0, statusCode0000
Thread: 48, isValid: true, referenceNumber: 07009882, validateNumber: 0700988, sum: 32, checkDigit: 2, statusCode0000
But if I add "parallel=true" to the @DataProvider my scripts starts to fail
@Test(dataProvider = "validReferenceNumbers", timeOut = 3000)
public final void testValidReferenceNumber(String referenceNumber) throws NoSuchMethodException, ScriptException {
Map<String, String> parameters = new HashMap<>();
parameters.put("referenceNumber", referenceNumber);
Invocable invocable = (Invocable) engine;
Object result = invocable.invokeFunction("validate", parameters);
Assert.assertEquals(statusCode, "000");
}
@DataProvider(name = "validReferenceNumbers", parallel=true)
private Iterator<Object[]> validReferenceNumbers() throws FileNotFoundException {
Iterator<Object[]> testData = null;
// please assume that will be initilized correctly
return testData;
}
Test Results2 - note that some data are repeated to show that the same reference number could fail randomly during the test
Thread: 29, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 22, isValid: false, referenceNumber: 18058888, validateNumber: 1805888, sum: 1, checkDigit: 1, statusCode0001
Thread: 25, isValid: false, referenceNumber: 18058888, validateNumber: 1805888, sum: 1, checkDigit: 1, statusCode0001
Thread: 21, isValid: true, referenceNumber: 17098881, validateNumber: 1709888, sum: 41, checkDigit: 1, statusCode0000
Thread: 27, isValid: false, referenceNumber: 18028885, validateNumber: 1802888, sum: 1, checkDigit: 1, statusCode0001
Thread: 20, isValid: true, referenceNumber: 17098881, validateNumber: 1709888, sum: 41, checkDigit: 1, statusCode0000
Thread: 26, isValid: true, referenceNumber: 17058887, validateNumber: 1705888, sum: 37, checkDigit: 7, statusCode0000
Thread: 24, isValid: true, referenceNumber: 17028884, validateNumber: 1702888, sum: 34, checkDigit: 4, statusCode0000
Thread: 23, isValid: false, referenceNumber: 18028885, validateNumber: 1802888, sum: 1, checkDigit: 1, statusCode0001
Thread: 28, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 30, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 32, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 34, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 31, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 36, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 38, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 40, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 42, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 44, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 33, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 46, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 48, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 35, isValid: false, referenceNumber: 18058888, validateNumber: 1805888, sum: 30, checkDigit: 0, statusCode0001
Thread: 50, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 37, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 39, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 41, isValid: true, referenceNumber: 17058887, validateNumber: 1705888, sum: 37, checkDigit: 7, statusCode0000
Thread: 52, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 54, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 43, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 45, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 56, isValid: true, referenceNumber: 07009882, validateNumber: 0700988, sum: 32, checkDigit: 2, statusCode0000
Thread: 58, isValid: true, referenceNumber: 07034880, validateNumber: 0703488, sum: 30, checkDigit: 0, statusCode0000
Thread: 47, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 49, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 51, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 53, isValid: true, referenceNumber: 18028885, validateNumber: 1802888, sum: 35, checkDigit: 5, statusCode0000
Thread: 55, isValid: true, referenceNumber: 18058888, validateNumber: 1805888, sum: 38, checkDigit: 8, statusCode0000
Thread: 57, isValid: true, referenceNumber: 07034880, validateNumber: 0703488, sum: 30, checkDigit: 0, statusCode0000
Can anyone confirm if I can Initialize the ScriptEngine and allow it to be used in a multithreaded application. As you can see I use global script variables but those will not be modified during its execution. As our design revolves around a web service that calls this script internally.
P.S. In my understanding @DataProvider(parallel=true) is the proper way to test the multithreading, so If I am wrong please point it out.
Edit Can anyone tell me how I can modify my code/script so that I can initialize the ScriptEngine and allow it to be used in a multi-threaded application. As I do not want to parse the script files over and over again.
As I previously thought that if the script does not use global variables to keep state it would be OK, but apparently that is not enough.
Thanks
You can initialize a ScriptEngine
and use it in a multithreaded application.
Instead of engine.eval(myScript);
you'll want to create a CompiledScript
instance that you can later evaluate into Bindings
instances:
Compilable compilable = (Compilable) engine;
CompiledScript script = compilable.compile(myScript);
And instead of using your engine
as an Invocable
you'll need to create a Bindings
instance for each thread/test, evaluate the compiled script into it, get the script object mirror that wraps the function, and then call the function:
@Test(dataProvider = "validReferenceNumbers", timeOut = 3000)
public final void testValidReferenceNumber(String referenceNumber) throws ScriptException {
Map<String, String> parameters = new HashMap<>();
parameters.put("referenceNumber", referenceNumber);
Bindings bindings = engine.createBindings();
script.eval(bindings);
ScriptObjectMirror scriptObjectMirror = (ScriptObjectMirror) bindings.get("validate");
Object result = scriptObjectMirror.call(null, parameters);
/* insert result assertions here */
}
Sources: