- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
Preface
As ServiceNow developers, we mostly use JavaScript to implement the requested business logic. I used the word mostly, because there are some cases when other languages, like TypeScript can also be used. But let's focus on JavaScript for now.
Before I continue, I would strongly recommend using existing OOTB solutions as many cases as possible.
Of course it is not always possible to avoid creating custom solutions in a project — or at least I've just never seen that. 🙂
Returning to the JavaScript topic, it's important to remember that ServiceNow is built on Java platform, and the JavaScript codes in the system are actually executed by the underlying Java runtime.
In this post, I would like to explain how a Java application can run JavaScript code, focusing on the possibility of using Java classes from within JavaScript.
Safe harbor 🙂: Everything in this post is based on my knowledge and experience as a Java developer. This will be a general introduction to how a Java-based application like ServiceNow can execute JavaScript code, purely for educational purposes. Please note that this information cannot be directly applied to ServiceNow itself, as its source code is not public.
I believe, that this article will help to get a bit deeper understanding of how it works. So let's get started.
What is Rhino?
Rhino is a JavaScript engine written in Java. It allows you to run JavaScript code inside Java programs. You can also call Java methods from JavaScript when using Rhino. This makes it useful for adding scripting to Java applications.
It's simple isn't it? 😊
Java and Javascript together
My friend, little Rhino, gave a high-level explanation of what it is, which provides great fundamental knowledge. You don’t need to know more to understand the concept.
Now, let’s see how it looks in reality — in a Java application.
package mypackage.rhinosample;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
public class JSTest {
public void testSimpleJSEval() {
// Define a simple JavaScript expression to evaluate
String scriptStr = "var greeting = 'Hello, Rhino!'; greeting;";
// Enter a new Rhino context for JavaScript execution
Context rhino = Context.enter();
try {
// Set the JavaScript language version (ECMAScript 6)
rhino.setLanguageVersion(Context.VERSION_ES6);
// Create and initialize a standard JavaScript scope (global object)
Scriptable scope = rhino.initStandardObjects();
// Evaluate the JavaScript code within the current scope
Object result = rhino.evaluateString(scope, scriptStr, "JavaScript", 1, null);
// Print the result of the JavaScript evaluation to the program output
System.out.println(result);
} finally {
// Ensure the Rhino context is properly exited to free resources
Context.exit();
}
}
}
These are just a few of lines of code, but the effect is much greater.
This is how we can print the "Hello, Rhino" to the console using Javascript in a Java application. Take a look at the program's execution console:
--------------------------------[ jar ]---------------------------------
--- resources:3.3.1:resources (default-resources) @ MyMainClass ---
Copying 3 resources from src/main/resources to target/classes
--- compiler:3.11.0:compile (default-compile) @ MyMainClass ---
Nothing to compile - all classes are up to date
--- exec:3.1.0:exec (default-cli) @ MyMainClass ---
Hello, Rhino!
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Let's see step by step, what happens during the code execution:
- Entering the Rhino context. (The context stores the execution state of the JavaScript engine, so it is required that the context be entered before execution begins.)
- Setting up the JavaScript version for the Rhino context.
- Setting up the scope (Scope... Have you ever heard about this? I have several thoughts on this topic, maybe I will write about it in the future.)
- Evaluating the JavaScript code (There are some parameters, which are used by the function, but for now this information is not relevant to us.)
- Printing the result to the console.
This is the simplest way to evaluate JavaScript code with Rhino. There are many additional capabilities and great features.
In my opinion, this is one of the main reasons why ServiceNow is so powerful for coding — because it uses this super feature.
Do you know what GlideRecord or GlideSystem (gs) are? These are also Java classes. And there are a lot of further ones.
Adding Java class to the Rhino context
Let me show you another example of how you can use Java class functions in the Rhino context.
Here is my GlideSystem class (Maybe it's a bit simpler than the original. 🙂 )
package mypackage.rhinosample;
public class MyGlideSystem {
public MyGlideSystem() {
}
// Print the input content to the console
public void print(String content) {
System.out.println(content);
}
}
Now, let's add this Java class to the Rhino context. The following code snippet shows the how to do this.
package mypackage.rhinosample;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
public class GlobalScopeTest {
public void testJSEval() {
// Simple JavaScript code that calls a method on a Java object
String scriptStr = "gs.print('Hello, this is a Java class function call.')";
// Enter a new Rhino context for JavaScript execution
Context rhino = Context.enter();
try {
// Set the JavaScript language version (ECMAScript 6)
rhino.setLanguageVersion(Context.VERSION_ES6);
// Create and initialize the global JavaScript scope (standard objects)
Scriptable globalScope = rhino.initStandardObjects();
// Create an instance of a Java class to expose to JavaScript
MyGlideSystem gs = new MyGlideSystem();
// Wrap the Java object for use in the JavaScript environment
Object gsJsObject = Context.javaToJS(gs, globalScope);
// Make the Java object available in JavaScript as the global variable 'gs'
ScriptableObject.putProperty(globalScope, "gs", gsJsObject);
// Evaluate the JavaScript code within the prepared scope
rhino.evaluateString(globalScope, scriptStr, "JavaScript", 1, null);
} finally {
// Ensure the Rhino context is properly exited to release resources
Context.exit();
}
}
}
The important part of the logic is in the middle of the code above. First we need to initialize the MyGlideSystem class and expose it to the JavaScript context, with the name gs. During the script evaluation the gs reference represents the instance of the MyGlideSystem class and its public methods can be used.
Below you can see the program output:
--------------------------------[ jar ]---------------------------------
--- resources:3.3.1:resources (default-resources) @ MyMainClass ---
Copying 3 resources from src/main/resources to target/classes
--- compiler:3.11.0:compile (default-compile) @ MyMainClass ---
Changes detected - recompiling the module! :source
Compiling 8 source files with javac [debug target 21] to target/classes
--- exec:3.1.0:exec (default-cli) @ MyMainClass ---
Hello, this is a Java class function call.
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
There is another way, how a Java class can be added to the Rhino context:
I have defined another class, called MyDataClass, which has one public function.
package mypackage.rhinosample;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyDateClass {
public MyDateClass() {
}
/**
* Returns the current system date and time as a formatted string.
*
* @return A string representing the current date and time in the specified
* format.
*/
public String getFormattedCurrentDate() {
SimpleDateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
return dateFormat.format(new Date(System.currentTimeMillis()));
}
}
The function getFormattedCurrentDate returns the current date and time in the following format: YYYY-MM-dd HH:mm:ss
Let's change the Java code, where the Rhino context was defined a little and add this class to it — but not as an instance.
public void testJSEval() {
// Define JavaScript code that creates a Java object and calls a method on it
String scriptStr = """
let dateCls = new MyDateClass();
gs.print(dateCls.getFormattedCurrentDate());
""";
// Enter a new Rhino context for JavaScript execution
Context rhino = Context.enter();
try {
// Set the JavaScript language version (ECMAScript 6)
rhino.setLanguageVersion(Context.VERSION_ES6);
// Create and initialize the global JavaScript scope
Scriptable scope = rhino.initStandardObjects();
// Create a Java object and expose it to JavaScript as 'gs'
MyGlideSystem gs = new MyGlideSystem();
Object gsJsObject = Context.javaToJS(gs, scope);
ScriptableObject.putProperty(scope, "gs", gsJsObject);
// Prepare a JavaScript snippet that makes a Java class available in the JS context
StringBuilder jsCode = new StringBuilder();
jsCode.append("const MyDateClass = Packages.mypackage.rhinosample.MyDateClass;");
// Append the actual JavaScript logic to be executed
jsCode.append(scriptStr);
// Evaluate the full JavaScript code within the given scope
rhino.evaluateString(scope, jsCode.toString(), "JavaScript", 1, null);
} finally {
// Exit the Rhino context to release resources
Context.exit();
}
}
I would highlight the following part of code:
jsCode.append("const MyDateClass = Packages.mypackage.rhinosample.MyDateClass;");
Have you already seen something similar in ServiceNow? The keyword Packages is like a gateway between Java and JavaScript. By using the full path of a Java class and defining it in the JavaScript code, Rhino tries to find it during evaluation. I named it MyDateClass, so in the JavaScript code I can use it as a class name, instantiate it, and use its public functions.
The following code snippet shows, how it can be used in JavaScript.
let dateCls = new MyDateClass();
gs.print(dateCls.getFormattedCurrentDate());
When we run the Java application, the current date appears in the expected format in the output.
--------------------------------[ jar ]---------------------------------
--- resources:3.3.1:resources (default-resources) @ MyMainClass ---
Copying 3 resources from src/main/resources to target/classes
--- compiler:3.11.0:compile (default-compile) @ MyMainClass ---
Changes detected - recompiling the module! :source
Compiling 8 source files with javac [debug target 21] to target/classes
--- exec:3.1.0:exec (default-cli) @ MyMainClass ---
2025-07-07 14:37:07
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Of course, the Packages.[package.name.path].ClassName expression can be used directly in JavaScript code. But you also have the option to predefine it, so when you write the JavaScript code, these classes are already available in the JavaScript context.
I can imagine that ServiceNow uses a similar solution to make Java classes available to us. I'm sure that their implementation is much more complex. 🙂
Static functions
The last important element of this blog post is static functions. First, let's clarify what static functions are:
A static function in Java belongs to the class, not to an object. This means you can use it without creating an object of the class. Static functions are often used for simple tasks like calculations or printing messages.
I will extend the MyDateClass with a static function called getCurrentTimeInMillisec, which returns the current time in milliseconds.
package mypackage.rhinosample;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyDateClass {
public MyDateClass() {
}
/**
* Returns the current system date and time as a formatted string.
*
* @return A string representing the current date and time in the specified
* format.
*/
public String getFormattedCurrentDate() {
SimpleDateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
return dateFormat.format(new Date(System.currentTimeMillis()));
}
/**
* Returns the current system time in milliseconds since the Unix epoch.
*
* @return The current time in milliseconds.
*/
public static long getCurrentTimeInMillisec() {
return System.currentTimeMillis();
}
}
Only the JavaScript code was changed in the Rhino handler class in order to call the static function:
public void testJSEval() {
// Define JavaScript code that calls a static method from a Java class
String scriptStr = "gs.print('Current time in millisec: ' + MyDateClass.getCurrentTimeInMillisec());";
// Enter a new Rhino context for JavaScript execution
Context rhino = Context.enter();
try {
// Set the JavaScript language version (ECMAScript 6)
rhino.setLanguageVersion(Context.VERSION_ES6);
// Create and initialize the global JavaScript scope
Scriptable scope = rhino.initStandardObjects();
// Create a Java object and expose it to JavaScript as 'gs'
MyGlideSystem gs = new MyGlideSystem();
Object gsJsObject = Context.javaToJS(gs, scope);
ScriptableObject.putProperty(scope, "gs", gsJsObject);
// Build the JavaScript source dynamically
StringBuilder jsCode = new StringBuilder();
// Make the Java class available in the JavaScript context
jsCode.append("const MyDateClass = Packages.mypackage.rhinosample.MyDateClass;");
// Append the actual JavaScript code to execute
jsCode.append(scriptStr);
// Evaluate the full JavaScript code within the given scope
rhino.evaluateString(scope, jsCode.toString(), "JavaScript", 1, null);
} finally {
// Exit the Rhino context to release resources
Context.exit();
}
}
The output of Java application shows the time in milliseconds:
--------------------------------[ jar ]---------------------------------
--- resources:3.3.1:resources (default-resources) @ MyMainClass ---
Copying 3 resources from src/main/resources to target/classes
--- compiler:3.11.0:compile (default-compile) @ MyMainClass ---
Changes detected - recompiling the module! :source
Compiling 8 source files with javac [debug target 21] to target/classes
--- exec:3.1.0:exec (default-cli) @ MyMainClass ---
Current time in millisec: 1751892582042
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
So, in the case of static functions, it is not necessary to call the constructor of the class using the keyword "new" — the function can be called directly.
There are several static functions in ServiceNow, like:
- GlideUser.getUserByEmail("itil@example.com")
- JSUtil.nil("TEST")
- ...
Closing words
The purpose of this post was not to go deep into the world of Java. I just wanted to give a high-level overview of the relationship between Java and JavaScript, so we can roughly understand how the things we use on a daily basis might work in the background.
I wrote roughly because this is just my own concept. Maybe the real implementation of ServiceNow is completely different — but for me, this explanation makes sense.
I think it is very exciting to understand the technical background of something that we use in more or less complex solutions and trust for its efficiency.
I hope this post helped you get a better understanding of how JavaScript works in a Java-based environment like ServiceNow. If you have any thoughts or know more about how this works exactly in ServiceNow, feel free to share your feedback!
Thanks for reading! 🙂
- 1,185 Views
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.