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();

Logging

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>)”

Passing Parameters

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>

Creating Response Elements

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.

Creating 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();

Creating the Corresponding Business Rule

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.