There is a magical and powerful feature that is largely undescribed in the ServiceNow platform. The mysterious “JavascriptProbe” and its companion, the “MID Server Script Include” feature is the gateway to endless possibilities for someone trying to do the impossible integration to ServiceNow with another system within their own network.
Overview
The ten second overview of a JavascriptProbe is that you can ask a MID Server to execute server-side javascript. The JavascriptProbe has the same functionality of making “Packages” calls into standard Java libraries. The nice thing about a MID Server is that you have more control over the Java libraries on the MID Server. You can even load your own JAR files on the MID Server and have the JavascriptProbe make Package calls into those java files as well. Very Powerful!
MID Server Script Includes are really just an integral component of the JavascriptProbe that is available on MID Servers. They are the MID Server’s version of a Script Include. They are created and managed within the ServiceNow instance. There are a few basics around the JavascriptProbe and MID Server Script Includes that we will want to cover, and then we will go through an example.
JavscriptProbe Basics
The JavascriptProbe can be invoked by creating an Output record on the ECCQueue with a Topic of “JavascriptProbe”. The Payload of the JavascriptProbe will contain a parameter with a name of “script”. The value of the element is the actual Javascript.
Here is a sample ECC Queue Record:
This particular script will write a log entry to the MID Server log file.
There is also a JavascriptProbe Script Include on your instance that will facilitate the creation of these probe records. At the time of this article, there is no formal documentation on the JavascriptProbe Script Include library, but if you check it out, it should be pretty self explanatory.
The following Script could generate the ECC Queue record shown above:
1 2 3 4 | var jspr = new JavascriptProbe('EC2MID2'); jspr.setName('My Test Message'); //Any descriptive name will do jspr.setJavascript('ms.log("TEST MESSAGE");'); jspr.create(); |
With the JavascriptProbe, you can’t use the typical Glide System Logging function that logs to your instance system log. Remember, the MID Server is running actively and independent of the instance. Therefore, you do not log to your instance system log, but rather to the MID Server log file that is contained in the MID Server path under: logs/agent0.log.0
In order to write to that log, you make the call: “ms.log(<Your message goes here>)”
You can pass parameters through the JavascriptProbe that are accessible by the script itself, or also accessible by the MID Server Script Include.
In order to set a parameter, simply add a “parameter” element to the payload on your Javascript ECC Queue record. Give that element a name and value.
In order for the script or MID Server Script Include to get the parameter value, you use the “probe.getParameter( paramName )” function.
Here is a sample Payload that sets a parameter and prints the parameter to the log:
1 2 3 4 | <parameters> <parameter name="script" value="ms.log(probe.getParameter('destFTPServer'));"/> <parameter name="destFTPServer" value="myFTP.myDomain.com"/> </parameters> |
By default, the last variable that is set in the script will have it’s value returned in the ECC Queue Record. For example, if my output payload were:
1 2 3 | <parameters> <parameter name="script" value="john = true;"/> </parameters> |
Then the input response payload would contain:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <results probe_time="508"> <result> <output>true</output> </result> <parameters> <parameter name="topic" value="JavascriptProbe"/> <parameter name="queue" value="output"/> <parameter name="destFTPServer" value="myFTP.myDomain.com"/> <parameter name="error" value=""/> <parameter name="from_sys_id" value=""/> <parameter name="sys_id" value="9ee9fc13c37e2000fb47df384aba8f48"/> <parameter name="state" value="ready"/> <parameter name="from_host" value=""/> <parameter name="script" value="john= true;"/> <parameter name="agent" value="mid.server.EC2MID2"/> <parameter name="processed" value=""/> <parameter name="ecc_queue" value="9ee9fc13c37e2000fb47df384aba8f48"/> <parameter name="response_to" value=""/> <parameter name="source" value=""/> <parameter name="sequence" value="13833c74e1a0000001"/> <parameter name="name" value="My Test Message"/> <parameter name="table_name" value="ecc_queue"/> <parameter name="agent_correlator" value=""/> </parameters> </results> |
Please note that the /results/result/output element contains the “john” variable value since that was the last thing we set.
Your MID Server Script Includes can set XML elements for the resulting ECC Queue entry after the JavascriptProbe executes that get included in addition to the “output” element discussed above.
probe.createElement( elementName, text );
The resulting “input” record in the ECC Queue will have an element set with the text as the inner content for that element.
probe.createCDATAElement( elementName, cDataText );
This will do the same as the createElement function, but it will surround the text with a CDATA tag.
Example Scenario
Now, lets take the above information and apply it to a possible scenario. Let’s say that when we have an incident created, we want the MID Server to generate a PDF record of that incident and store it on its file system for some paper-less PDF auditing.
First, we will create a MID Server Script Include that handles all of the logic. Later we’ll set up the business rule to setup the JavascriptProbe that references the MID Server Script Include.
When creating a MID Server Script Include, you set it up the same way that you set up a regular Script Include. However, you go through the MID Server application to do so.
I am going to name my MID Server Script Include: GeneratePDFRecord
I then name my class the same name as the MID Server Script Include record and built out the template for the class:
1 2 3 4 5 6 7 8 9 10 | var GeneratePDFRecord = Class.create(); GeneratePDFRecord.prototype = { initialize: function () { //---This will be the constructor }, type: GeneratePDFRecord }; |
I like to set up my constructor with all of the Package calls that I will be using. I also like to set up the parameters from the JavascriptProbe as well.
Notice how I am making Packages calls into standard Java libraries that are included on the JDK that comes with the MID Server. These will then be available to the functions in the MID Server Script Include.
Also, take note on how I am grabbing the parameters using the “probe.getParameter” call. This grabs specific parameter elements from the ECC Queue’s Javascript Probe request in the payload.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | initialize: function () { // // Set up the Packages references // this.File = Packages.java.io.File; this.FileOutputStream = Packages.java.io.FileOutputStream; this.HttpClient = Packages.org.apache.commons.httpclient.HttpClient; this.UsernamePasswordCredentials = Packages.org.apache.commons.httpclient.UsernamePasswordCredentials; this.AuthScope = Packages.org.apache.commons.httpclient.auth.AuthScope; this.GetMethod = Packages.org.apache.commons.httpclient.methods.GetMethod; // //Set up the parameters // this.domain = probe.getParameter("httpDomain"); this.port = probe.getParameter("httpDomainPort"); this.protocol = probe.getParameter("httpProtocol"); //http/https this.relativeUrl = probe.getParameter("relativeUrl"); this.verbose = probe.getParameter("verbose"); this.filename = probe.getParameter("filename"); }, |
I’m going to also create a debug logging helper function that will log to the MID Server log file if there is a “verbose” parameter set to “true” with the probe.
1 2 3 4 5 | debug: function (m) { if (this.verbose == "true") { ms.log(m); } }, |
Let’s finish the MID Server Script Include with the execute function – the function that is going to do all of the hard work.
We are going to use the PDF web service provided by ServiceNow. Essentially, if we may an http call to our instance and point to an instance record with the PDF processor parameter, ServiceNow will return the PDF binary in its response.
Here is a sample URL: https://demo17.service-now.com/incident.do?sys_id=4f13760cc3322000fb47df384aba8f34&PDF
We will then take that HTTP GET Response Stream, and save it to a file on the MID Server using standard Java IO classes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | execute: function () { // // Set up the HTTP Connection. We will use the PDF web service built into SN // var client = new this.HttpClient(); var authScope = new this.AuthScope(this.domain, this.port, null); var credentials = new this.UsernamePasswordCredentials("admin", "admin"); client.getState().setCredentials(authScope, credentials); var url = this.protocol + "://" + this.domain + ":" + this.port + "/" + this.relativeUrl; this.debug("URL: "+url); var get = new this.GetMethod(url); get.setDoAuthentication(true); // // Now that we have the HTTP client set up, we are going to submit the request // to ServiceNow // var status = client.executeMethod(get); this.debug("GET Status: " + status); if( status == "200" ){ // // We are going to write the HTTP Response to a file // this.debug("Attempted to save the file to filname: " + this.filename); var f = new this.File(this.filename); var inputStream = get.getResponseBodyAsStream(); var out = new this.FileOutputStream(f); var buf = Packages.java.lang.reflect.Array.newInstance(Packages.java.lang.Byte.TYPE, 1024); while ((len = inputStream.read(buf)) > 0) { this.debug("Writing to the file…with buffer size of "+len); out.write(buf, 0, len); } out.close(); inputStream.close(); var result = "Done creating "+this.filename; this.debug(result); } else { var result = "Invalid HTTP Response. Exiting Early."; this.debug(result); } return result; }, |
All together, this is what our MID Server Script Include will look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | var GeneratePDFRecord = Class.create(); GeneratePDFRecord.prototype = { initialize: function () { // // Set up the Packages references // this.File = Packages.java.io.File; this.FileOutputStream = Packages.java.io.FileOutputStream; this.HttpClient = Packages.org.apache.commons.httpclient.HttpClient; this.UsernamePasswordCredentials = Packages.org.apache.commons.httpclient.UsernamePasswordCredentials; this.AuthScope = Packages.org.apache.commons.httpclient.auth.AuthScope; this.GetMethod = Packages.org.apache.commons.httpclient.methods.GetMethod; // //Set up the parameters // this.domain = probe.getParameter("httpDomain"); this.port = probe.getParameter("httpDomainPort"); this.protocol = probe.getParameter("httpProtocol"); //http/https this.relativeUrl = probe.getParameter("relativeUrl"); this.verbose = probe.getParameter("verbose"); this.filename = probe.getParameter("filename"); // // run it automatically // //return this.execute(); }, execute: function () { // // Set up the HTTP Connection. We will use the PDF web service built into SN // var client = new this.HttpClient(); var authScope = new this.AuthScope(this.domain, this.port, null); var credentials = new this.UsernamePasswordCredentials("admin", "admin"); client.getState().setCredentials(authScope, credentials); var url = this.protocol + "://" + this.domain + ":" + this.port + "/" + this.relativeUrl; this.debug("URL: "+url); var get = new this.GetMethod(url); get.setDoAuthentication(true); // // Now that we have the HTTP client set up, we are going to submit the request // to ServiceNow // var status = client.executeMethod(get); this.debug("GET Status: " + status); if( status == "200" ){ // // We are going to write the HTTP Response to a file // this.debug("Attempted to save the file to filname: " + this.filename); var f = new this.File(this.filename); var inputStream = get.getResponseBodyAsStream(); var out = new this.FileOutputStream(f); var buf = Packages.java.lang.reflect.Array.newInstance(Packages.java.lang.Byte.TYPE, 1024); while ((len = inputStream.read(buf)) > 0) { this.debug("Writing to the file…with buffer size of "+len); out.write(buf, 0, len); } out.close(); inputStream.close(); var result = "Done creating "+this.filename; this.debug(result); } else { var result = "Invalid HTTP Response. Exiting Early."; this.debug(result); } return result; }, debug: function (m) { if (this.verbose == "true") { ms.log(m); } }, type: GeneratePDFRecord }; |
I can test this by running the following javascript in my Scripts Background utility:
1 2 3 4 5 6 7 8 9 10 | var jspr = new JavascriptProbe('EC2MID2'); jspr.setName('GeneratePDFRecord'); //Any descriptive name will do jspr.setJavascript('var pdf = new GeneratePDFRecord(); res = pdf.execute();'); jspr.addParameter("httpDomain", "demo10.service-now.com"); jspr.addParameter("httpDomainPort", "443"); jspr.addParameter("httpProtocol", "https"); jspr.addParameter("relativeUrl", "incident.do?sys_id=e8caedcbc0a80164017df472f39eaed1&PDF"); jspr.addParameter("verbose", "true"); jspr.addParameter("filename", "e8caedcbc0a80164017df472f39eaed1.pdf"); jspr.create(); |
Now that we can generate PDF files on our MID server given a domain, port, URL, and filename, we can easily tie this into the Business Rule.
Since I am not going to modify the incident ticket after the PDF creation, I am going to use an onAfter business rule.
Here is my business rule information:
Name: “Generate PDF of Incident Record”
Table: “incident”
Order: 600
When: After
Insert: Checked
Condition: none
Script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var jspr = new JavascriptProbe('EC2MID2'); //hard coding this for the example jspr.setName('GeneratePDFRecord'); //This is where we are calling our MID Server Script Include jspr.setJavascript('var pdf = new GeneratePDFRecord(); res = pdf.execute();'); //Set up the parameters for the MID Server Script Include to use jspr.addParameter("httpDomain", "demo10.service-now.com"); jspr.addParameter("httpDomainPort", "443"); jspr.addParameter("httpProtocol", "https"); jspr.addParameter("relativeUrl", "incident.do?sys_id="+current.sys_id+"&PDF"); jspr.addParameter("verbose", "true"); jspr.addParameter("filename", current.sys_id+".pdf"); jspr.create(); |
Conclusion
Using a JavascriptProbe, and it related MID Server Script Includes, you can generate powerful integrations that take place on the MID Server. With these amazing capabilities almost any integration or automation actions within your network are within your reach.
Awesome!!… I was searching for a similar thing…
that is pretty cool and extremely powerful, thanks !
Hi John,
thanks for this great tutorial. Put my thoughts in the right direction 🙂
In the meantime you should probably use / extend the class AProbe, this way there is no need to hava javascript in the payload body.
Cheers,
Daniel
Hello, Do we have a similar stuff for SSHCommand? I mean executing ssh commands using scrips
Olaf, there sure is! See: Executing Command Line Applications in ServiceNow.
John,
Do you have any examples/tips using “org.apache.commons.httpclient.methods.multipart”
I am specifically having an issue with the “org.apache.commons.httpclient.methods.multipart.Part”
Thanks!
reply to Jonathan, about Multipart Http Requests
I struggled with that myself for a while and finally had the most success with creating a custom .jar file and just calling my java code from the probe , without the use of the mid-server script include.I could not get the Packages and Class correct in the script include. Thats why I went that route. Here are the Packages I used to post a file and a string to the web service.
org.apache.commons.httpclient.HttpClient;
org.apache.commons.httpclient.methods.PostMethod; org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
org.apache.commons.httpclient.methods.multipart.FilePart;
org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
org.apache.commons.httpclient.methods.multipart.Part;
org.apache.commons.httpclient.methods.multipart.StringPart;
Other tips, don’t forget to add the “boundary” to your header. Let me know if you have any other specific questions I can maybe help. Also I did discover you can post a “string” Multipart request” using the REST app in SN, but it does not seem to support outbound bytes. So if you are not posting a file or binary data you should be fine with the REST app in SN.
Hi John,
Thanks for this great tutorial. Is it possible to create javascript probe in the scoped application? I have a requirement where I have decode CSR. I am thinking to use javascript probe as it allows the use of Java packages.
Any thoughts?
Thanks
KS
Hi John,while running the script include i am getting run time exception ,may i know how to add httpclient external jars to the class path
Got it,i have added the jar files in JRE–Lib folder…issue resolved…guys u can also follow this, rather than writing java code and calling it using probes.
I modify the original script include to download an audio file coming from internal domain web server to the Mid-server and it worked.
Now I wanted to attached it to an incident instead of saving the file, I looked at what was done in “ATTACH REMOTE HTTP RESOURCES TO A SERVICENOW RECORD” . and tried it in a Mid server script include. I have the following error for the GlideSys Attachment:
11/30/17 20:23:11 (030) Worker-Standard:JavascriptProbe WARNING *** WARNING *** org.mozilla.javascript.EcmaError: [JavaPackage com.glide.ui.GlideSysAttachment] is not a function.
Caused by error in MID Server script include ‘DownloadCiscoAudioRecord’ at line 79
76: this.debug(“*** Script Cisco: Call# ” + rec.number);
77: }
78: var SysAttachment = Packages.com.glide.ui.GlideSysAttachment;
==> 79: var sa1 = new SysAttachment();
80:
81: sa.write(rec, this.filename, this.contentType, is);
82:
I’m on Jackarta Patch 3.
Is it possible to control which folder the file gets posted to, on the mid server?
Hi Martin L/John,
Could you please help me out in the script to download audio file from Mid Server? Basically my requirement is to download the pdf files from a shared location accessible through Mid Server.
Thanks,
Poornima
Hi John,
I am getting a below error while executing GeneratePDFRecord script include through Javascript Probe as stated in the post. Please help me out here.
Evaluation error: Caused by error in JavaScript probe ‘GeneratePDFRecord’ at line 1
==> 1: var pdf = new GeneratePDFRecord(); res = pdf.execute();
How we can block additional entries in ecc queue -> input = error with message: No sensors defined
Any ideas?
@KKK this is a message you may get when you have Discovery installed. We have found that if you add the following parameter, “skip_sensor”, in the ECC Queue Output payload, and give it a value of “true”, it will prevent these messages from occurring.
@ Poornima Did you find how to fixe this?
I have the same error : Evaluation error: Caused by error in JavaScript probe
Did we got the solution on below error.
Evaluation error: Caused by error in JavaScript probe ‘GeneratePDFRecord’ at line 1
==> 1: var pdf = new GeneratePDFRecord(); res = pdf.execute();
Getting the same issue as several people above – error at line 1 where I call my script include. Seems like the problem is that the MID isn’t recognizing the class but I have no idea why. The script include is there in MID Script Includes and I’ve restarted each of the MIDs to make sure they have it downloaded.
Did anyone get solution for the below??
Evaluation error: Caused by error in JavaScript probe ‘GeneratePDFRecord’ at line 1
==> 1: var pdf = new GeneratePDFRecord(); res = pdf.execute();