ServiceNow Java Classes & Methods
A full list of Java classes whitelisted for use in global scripting (Tokyo)
Cutting to the Chase
Just want the list? Click here. I’ve made an effort to make the linked gist as “bookmarkable” as possible. There are 3 different lists on the page in total — denoted Z, Y, Z — please don’t miss any of them while scrolling!
Debriefing
This (long!) primer exists for the benefit of the ServiceNow noobs in the crowd. If you don’t need a refresher, you should skip on to Utilizing Java classes in ServiceNow. Don’t miss the legal disclaimer, though.
Java classes? In ServiceNow??
ServiceNow’s scripting environment hybridizes JavaScript and Java1. This hybrid behavior is mostly transparent and generally not important platform knowledge, but I’ll touch on it here anyway. It’s my blog, after all.
To oversimplify: if a named “PascalCase”2 reference exists within ServiceNow that isn’t a prototype3 defined in a Script Include, it’s probably a Java class. One such example would be the GlideDateTime
class:
// Type of a normal JavaScript prototype provided via Script Include
gs.info(GlideJSUtil.getJavaClassName(ArrayUtil));
// => org.mozilla.javascript.InterpretedFunction
// Type of a Java Class
gs.info(GlideJSUtil.getJavaClassName(GlideDateTime));
// => java.lang.class
FYI: Scoped applications heavily lock down access to Java classes. The information in this post is almost exclusively geared towards writing scripts in global scopes.
How does that even work?
Under the hood, instances of these Java classes follow the Java type system4 . This is quite different from JavaScript’s standard behavior of generalizing prototype instances as a single unified “Object” data type.
// Type of an instance of a normal JavaScript prototype
var ordinaryObject = new ArrayUtil();
gs.info(GlideJSUtil.getJavaClassName(ordinaryObject));
// => org.mozilla.javascript.NativeObject
// Type of an instance of the GlideDateTime class
var glideDateTimeObject = new GlideDateTime();
gs.info(GlideJSUtil.getJavaClassName(glideDateTimeObject));
// => com.glide.glideobject.GlideDateTime
This has significant implications for when JavaScript code overlaps with Java types, particularly when it comes to type coercion… but we’ll gloss over that for now since it’s not relevant to the topic of this post. Subscribe if you’d like to be notified when the Java <=> JavaScript Type Coercion post drops 😉.
What’s the point of using Java classes?
The point is that there’s a whole family of potentially useful Java-based methods that you won’t learn of by simply browsing the installed Script Include list. In fact, the majority of these Java classes aren’t covered at all in the official documentation.
This creates a huge gulf between those who know and those who don’t. Imagine wasting days to implement a base64 encoder in JavaScript… only to find out that your co-worker got the same job done in 5 minutes because they dug up a magic script:
var base64EncodedData = GlideStringUtil.base64Encode(foo);
You’d rather be the fellow with the magic scripts, yeah? Sometimes, the right Java class in the right place can make all the difference in the world; that’s why this post exists.
What’s the catch?
The catch is that most5 of these Java classes are undocumented. Undocumented means unsupported… perhaps even dangerous. So don’t expect ServiceNow support to help if you use such things and certainly don’t expect your boss to be forgiving if production gets bricked because you trusted some sketchy list from the internet.
… this is the part where I disclaim some stuff:
THE CODE AND DOCUMENTATION WITHIN THIS DOCUMENT ARE PROVIDED "AS IS," AND MAKE NO EXPRESS OR IMPLIED WARRANTY OF ANY KIND. THE AUTHOR SPECIFICALLY DISCLAIMS ALL INDIRECT OR IMPLIED WARRANTIES TO THE FULL EXTENT ALLOWED BY APPLICABLE LAW, INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF, NON-INFRINGEMENT, MERCHANTABILITY, TITLE OR FITNESS FOR ANY PARTICULAR PURPOSE. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY THE AUTHOR SHALL CREATE A WARRANTY.
Sufficiently spooked yet? Good. Hold on to that feeling and develop responsibly.
Utilizing Java classes in ServiceNow
Now that we understand there’s this special category of mostly hidden Java classes in ServiceNow, how can we make use of them?
Finding a Java method
First, of course, you need to find a Java method that you’re actually interested in using. To do this, you can consult my Whitelisted Class Methods list!
I’ll break down one of the entries to give us a better idea of how these work:
com.glide.sys.User:isLockedOut(java.lang.String) boolean
com.glide.sys.User
^ isLockedOut
| ^ (java.lang.String)
| | ^ boolean
| | | ^
| | | |
CLASS METHOD PARAMETER(S) RETURN TYPE
This tells me the following information:
There is a Java class called “com.glide.sys.User”
This class has a method called “isLockedOut” that requires 1 string parameter
It returns a true/false value
From this information, I can surmise that this method will tell me whether a given username belongs to a locked-out user.6
Using a Java method
Now that we know how to find Java methods, how do we actually use them? Technically speaking, it’s rather simple. Just three steps:
Get a reference to the desired Java class
Get an object instance of the class (if necessary)
Invoke a method
… easier said than done, so let’s do an exercise! For the sake of this exercise, let’s say that we want to use the following entry from the method list:
com.glide.sys.User:getTimeFormat() java.lang.String
Step 1: Getting a reference to the Java class
Option A: Using the “friendly name”
In some cases, ServiceNow is nice enough to provide a “friendly name” for a Java class. In those cases, that name will be listed in this table.
In the context of our exercise, we would search the right-hand side of the table for “com.glide.sys.User”. If there’s a match, then we can use the matching friendly name from the left-hand column (“GlideUser”) just like that!
gs.info(GlideUser);
// => [JavaClass com.glide.sys.User]
If a class is available with such a “friendly name”, it could possibly even be officially supported and documented! To find out, search for the friendly name in this list. Note that such officially supported classes can still have “hidden” or undocumented methods waiting to be discovered by looking at my method list — these hidden methods constitute something of a gray area, support-wise.
Option B: Using “Packages”
If no “friendly name” exists, we’re forced to fall back to an older way of interacting with Java classes: the “Packages” object. Here’s how that looks:
gs.info(Packages.com.glide.sys.User);
// => [JavaClass com.glide.sys.User]
It should be noted that the “Packages” object is considered deprecated. Support might tolerate using certain undocumented Java classes via the “friendly name” approach, but now that all flies out of the window. Support will tell you to go kick rocks if they see customer code that does this.
Option C: Enlisting the help of another Java class
Sometimes, there will be no direct pathway to a given Java type. In these cases, it is often still possible to get an instance of the desired class with the help of a different Java class method.
For example: we can get a Java byte array — something which cannot be created directly — via the getBytes
method of a Java String object:
var javaString = new Packages.java.lang.String('Hello World');
var helloWorldBytes = javaString.getBytes();
gs.info(GlideJSUtil.getJavaClassName(helloWorldBytes));
// => [B
// NOTE: This is the Rhino notation for an array "[" of Bytes "B"
Step 2: Getting an instance of the Java class
Option X: Don’t get an instance at all!
Many methods are used without an object, directly from the class reference7. The previously shown base64 encoding “trick” is one such example of this:
var base64EncodedData = GlideStringUtil.base64Encode(foo);
In the context of our exercise, we can see that “com.glide.sys.User:getTimeFormat()” doesn’t work like this:
GlideUser.getTimeFormat();
// CRASH: Javascript compiler exception: Java class "com.glide.sys.User" has no public instance field or method named "getTimeFormat".
Option A: Using the class constructor
Many classes allow for creating new objects on-demand. You need only find the proper constructor! To do this, you can consult my Whitelisted Class Constructors list!
In the context of our exercise, we can see the following entry in the constructor list:
com.glide.sys.User()
This translates to using the new
keyword on our class reference:
var glideUser = new GlideUser();
gs.info(GlideJSUtil.getJavaClassName(glideUser));
// => com.glide.sys.User
Note: some constructor entries will require arguments. For example, the following entry requires a date string to construct a GlideDateTime object:
com.glide.glideobject.GlideDateTime(java.lang.String)
This translates into JavaScript code like so…
var dateRaw = '2022-02-02 02:02:02';
var gdt = new Packages.com.glide.glideobject.GlideDateTime(dateRaw);
gs.info(GlideJSUtil.getJavaClassName(gdt));
// => com.glide.glideobject.GlideDateTime
Of course, we all know that “com.glide.glideobject.GlideDateTime”, has a “friendly name” of simply “GlideDateTime” — using the friendly name, when available, is always the most correct approach:
var dateRaw = '2022-02-02 02:02:02';
var gdt = new GlideDateTime(dateRaw);
gs.info(GlideJSUtil.getJavaClassName(gdt));
// => com.glide.glideobject.GlideDateTime
Option B: Using a getter method
Some classes aren’t meant to be instantiated more than once8 — in these cases, there won’t be any available constructor methods for you to use! Often, you’ll instead be expected to use a “getter” method.
These can be found by searching through my Whitelisted Class Methods list for any methods that return an object of the desired class type. Such “getter” methods usually belong to the same class that they are getting.
For example, this getter provides a reference to the pre-existing GlideSession object representing my ongoing login session. Note how the return type matches the class:
com.glide.sys.GlideSession:get() com.glide.sys.GlideSession
The above getter would translate to JavaScript like so…
var mySession = GlideSession.get();
gs.info(GlideJSUtil.getJavaClassName(mySession));
// => com.glide.sys.GlideSession
Note that getters can sometimes also exist on classes that already have traditional constructors9. In the context of our exercise, we see the following getter is available as an alternative to using the constructor:
com.glide.sys.User:getUserByID(java.lang.String) com.glide.sys.User
Let’s use some example code to illustrate why there are two different approaches for this particular class:
// The constructor always gets the session user
var constructedUser = new GlideUser();
gs.info(constructedUser.getName());
// => Christopher Crockett
// Using the getter instead provides a user object for someone else
var gottenUser = GlideUser.getUserByID('foobar.handle');
gs.info(gottenUser.getName());
// => Foobar Handle
Step 3: Invoke the method
This part’s easy. You simply invoke the method on your class or object!
Here’s how it all comes together in the context of our exercise:
com.glide.sys.User:getTimeFormat() java.lang.String
var userObject = new GlideUser();
var timeFormat = userObject.getTimeFormat();
gs.info(timeFormat);
// => HH:mm:ss
Note that methods will often require arguments to invoke, depending on the signature. For example, the following method can be used to update a given preference to another given value…
com.glide.sys.User:savePreference(java.lang.String, java.lang.String) void10
var preferenceName = 'navigator.collapsed';
var preferenceValue = 'true';
var userObject = new GlideUser();
userObject.savePreference(preferenceName, preferenceValue);
Note how JavaScript values may be used as parameters in place of certain Java types. To give an incomplete list:
JavaScript Object: java.lang.Object
JavaScript String: java.lang.String
JavaScript Boolean: java.lang.Boolean
JavaScript Number: java.lang.Integer, java.lang.Long, java.lang.Float, java.lang.Double
The underlying architecture is based on Mozilla’s Rhino engine, which is technically a JavaScript engine, but it implements the non-standard feature of exposing Java classes as special objects within JavaScript contexts.
“PascalCase”, meaning LikeThis or LikeThat. As opposed to “camelCase”, meaning likeThis or likeThat
This is the JavaScript terminology for the thing that most Script Includes provide. If you’re not familiar with the concept — for the purposes of this post — it’s fine if you mentally substitute “prototype” with “JavaScript class”
i.e. the data type of the constructed instance is the constructing class
Most, but by no means all. Don’t be afraid of undocumented Java classes, be afraid of undocumented Java classes.
I want to stress again that you need to be extremely careful with undocumented methods. There is no guarantee that this method actually does what the signature suggests — the onus is on you to rigorously test & verify each undocumented method you plan to use in a disposable environment, such as a PDI.
In systems programming terminology, these are called “static methods”
These are sometimes called “singleton objects”
The reason for why this is done varies on a case-by-case basis. As far as the GlideUser class is concerned… there are actually several equally valid ways to specify a user (e.g.: by Sys ID, by username, by email, etc.). Instead of forcing one “correct” way to do this through a String-based constructor, ServiceNow instead opted to provide a multitude of getters (one getter for for each possible approach).
void
is the Java way of specifying that there is no returned value
This line I think has a typo:
var helloWorldBytes = javaString.toBytes();
should be:
var helloWorldBytes = javaString.getBytes();