- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎08-10-2016 03:20 PM
NOTE: This post contains question, and answer. It's marked as a question so that others can find it more easily. Skip to the next green checkmark for the solution!
My experience here relates to a Wizard, which I know not everyone uses -- however, the issue is generalizable, and not specific to Wizards.
After upgrading an instance to Helsinki from Geneva, I noticed that our Wizards weren't working. The first page would load, you'd select an option, and click next; but rather than redirecting you to the resource that it's supposed to, it redirects you to a blank white page. Strange.
I verified that it wasn't a client-side issue through the console, which showed no errors. In fact, it showed suspiciously few lines at all.
After checking the error log, replicating the issue, re-checking the log, and repeating to filter out any unrelated (non-repeating) errors, I found this was the only error thrown when the page failed to render:
org.mozilla.javascript.ConsString cannot be cast to java.lang.String: java.lang.ClassCastException: org.mozilla.javascript.ConsString cannot be cast to java.lang.String: com.snc.expert.ExpertPanelRedirectURL.generateRedirect(ExpertPanelRedirectURL.java:41)
com.snc.expert.ExpertInstance.next(ExpertInstance.java:297)
com.snc.expert.ExpertProcessor.process(ExpertProcessor.java:26)
com.glide.processors.AProcessor.runProcessor(AProcessor.java:412)
com.glide.processors.AProcessor.processTransaction(AProcessor.java:187)
com.glide.processors.ProcessorRegistry.process(ProcessorRegistry.java:165)
com.glide.ui.GlideServletTransaction.process(GlideServletTransaction.java:49)
com.glide.sys.ServletTransaction.run(ServletTransaction.java:34)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java.lang.Thread.run(Thread.java:745)
The "Expert Panel Redirect URL" thing it's talking about is apparently the name of the "answer" variable set on the "Redirect Panel" of the wizard. Our code on this page looked like this:
var request_type=wizard.request_type;
var url = "";
url = "u_batch_scheduling.do?blabla&sysparm_query=^u_type=" + request_type;
answer=url;
So, line-by-line...
- We declare a variable, request_type, and we set it to the value selected in the "request_type" field on the wizard. Since this is server-side, we know that TECHNICALLY, we've got a "GlideElement" object here. However, GlideElement objects have always been super easily castable, or more commonly, coercible into String objects in JS, and Java (Rhino) seems to perform this coercion prior to processing the result, or at least to have its' own version of coercion that it applies when attempting to treat a GlideElement as a string -- so, no problem.
- Initialize the variable url to a java.lang.String datatype.
- Set the value of url to a string, concatenated with the variable request_type which we set on line 1.
Now, in JS (you know, the language we're writing in), this would cause an automagic coercion of the request_type variable, to a java.lang.String, which easily concatenates with another string to form a single string consisting of the string-values of both concatenated original strings. - Finally, we set answer to url which should now contain the concatenation of the anonymous inline string beginning with u_batch_blabla, and the String-value of the request_type variable. This answer variable is then used as the source URL to redirect the frame to, in the Wizard processor.
The problem here, is on line 3.
What's the Problem?
So, as you might be aware, ServiceNow upgraded the version of Rhino (the underpinning architecture ServiceNow runs on, which runs Java rather than JS) which very awesomely unlocked ECMAScript 5 I believe, or ES5 JavaScript. This opens up loads of new functionality (and enables the possibility of using things like the native Array.prototype.indexOf() functionality, rather than relying on the ArrayUtil class (though I don't think that that specifically, is an option yet).
Unfortunately, the new version of Rhino comes with some caveats. Specifically, it no longer gracefully handles string coercion under certain circumstances.
In typical "it's a feature, not a bug" style, Mozilla have said that they're not intending to resolve the issue, which seems to exist in every version of rhino-js-1.7R4 and later.
As you probably know, ServiceNow is constantly talking back and forth between JAVA and JAVASCRIPT. And the languages - despite the name - are RADICALLY different from one another, in everything from object models to syntax. And sometimes it doesn't know its' "Java.lang.String.prototype" from its' JavaScript String.prototype. Due to some serious oddities in the way that JS... is... this causes all kinds of issues (such as this one).
When you concatenate a string with another variable containing a string (or a variable cast to a string from another object, such as a GlideElement -- which is what happened in the script above) -- you don't ACTUALLY have a "String" object. Instead, you have a "ConsString" (java.lang.Object prototype, which implements java.lang.CharSequence). It is essentially a serialized character sequence which, as it turns out, is technically different from a "String". Not materially, mind you. And it can easily be cast or coerced into a String. But Rhino's latest implementation in ServiceNow, has stubbornly decided not to allow such coercion anymore (which kinda makes sense, Java doesn't technically HAVE coercion - JS does).
This (below) is the part where I tell you the solution!
Luckily, the solution to this, is simply to explicitly cast these variables to a String object, BEFORE returning them (and by returning, I mean handing them back to Rhino -- setting an answer variable is the same).
Taking our script above as an example, we can fix this issue with the simple addition of just one line, right before returning the value. (the new line 4 below):
var request_type=wizard.request_type;
var url = "";
url = "u_batch_scheduling.do?blabla&sysparm_query=^u_type=" + request_type;
url = url.toString();
answer=url;
(In this case, we could also have done answer.toString(), but that isn't possible when returning).
Unfortunately it seems that you can't just wrap the declaration and toString() that, to save the extra line:
So, to reiterate -- The work-around is simply to explicitly cast your concatenated strings (when concatenated with a data type that is not TECHNICALLY a JS String), to strings, using ".toString()".
--------------------------
I'm Tim Woodruff, founder of the SN Guys, and Princ. writer for SN Pro Tips. Since this was a little bit too specific to turn into an entire article for the site, I figured I'd post it here instead; but if you enjoyed it, I humbly welcome you to check out the blog, or you may feel free to contact us if you have any ServiceNow related needs that we can assist with.
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎08-10-2016 03:21 PM
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎08-10-2016 03:21 PM
Replying so I can mark this question as answered. 😛
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎09-21-2016 12:25 AM
Thanks for posting this!
I am having the same issue with one of my older wizards, even getting the same error in the log. I even have an open HI case that is currently going through "head scratching" phase, so this has given me a whole new path to head down.
Thinking in general terms, I would expect this to cause significant issues across the board.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎09-21-2016 01:01 PM
Glad to help!
Yeah, there are a few other places where it causes issues, but in many cases, ServiceNow seems to be pre-sanitizing strings (where it can determine that they're strings) before handing them off to Java.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎09-22-2016 07:17 PM
I just read in the Helsinki Patch 5 release notes that this has been addressed for wizards, but it is possible it will show up elsewhere so I thought I would post how I got around it. It uses the exact method above, put it is just a more complex example.
Original redirect URL code:
var params = 'sysparm_customer=' + wizard.select_customer + '&sysparm_product=' + wizard.select_product + '&sysparm_dealer=' + wizard.dealer_code;
var url = 'com.glideapp.servicecatalog_cat_item_view.do?sysparm_id=' + gs.getProperty('name.of.property.goes.here');
answer = url + '&' + params;
With workaround applied:
var url = '';
url = 'com.glideapp.servicecatalog_cat_item_view.do?sysparm_id=' + gs.getProperty('name.of.property.goes.here');
url = url.toString();
var params = '';
params = 'sysparm_customer=' + wizard.select_customer + '&sysparm_product=' + wizard.select_product + '&sysparm_dealer=' + wizard.dealer_code;
params = params.toString();
var cons_answer = '';
cons_answer = url + '&' + params;
cons_answer = cons_answer.toString();
answer = cons_answer;
Unfortunately I had completed my fix for all eight of the different products linking from the wizard before reading the release notes for Patch 5. But there is a good chance this will show up elsewhere in the platform so it was educational at least.