petercawdron
Kilo Guru

For the most part, all the integration you need to manage within the Service Portal can be accomplished using REST web services, but occasionally you run into a dinosaur like an MS SQL database acting as a data warehouse. When the DBAs won't run up a web service, you can always use JDBC by calling a Script Includes from the server component of your portal widget. 

(function() {
  /* Widget server code */
 	
    if(input.hasOwnProperty('searchingFor')){
	
	var driver    = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
	var dbServer  = "xxxxx01";
	var dbPort    = "1433";
	var database  = "XXXX";
	var midServer = "xxxxx03";
	var sqlQuery  = "SELECT * FROM XYZ WHERE employee_number='"+ input.searchingFor;
		
	var dw = new dataWarehouse()
		
	data.raw = dw.executeRemoteQuery(driver, dbServer, dbPort, database, midServer, sqlQuery)
	}
})();

This will return an XML document you'll need to parse or scrap (either server side or on the client) in c.data.raw.

You will need to tweak the connection string if you're querying a different type of database, but this approach produces sub-second response times. Not quite as smooth as a web service, but still workable.

Special thanks to John James Anderson for the original code for this global script includes code.

var dataWarehouse = Class.create();
dataWarehouse.prototype = Object.extendsObject(AbstractAjaxProcessor, {
	//see http://www.john-james-andersen.com/blog/service-now/using-the-servicenow-jdbcprobe.html
	
	executeRemoteQuery:function(driver, dbServer, dbPort, database, midServer, sqlQuery){
		var timeout = 30, queryResults='';

		var connectionString = "jdbc:sqlserver://"+dbServer+":"+dbPort+";databaseName="+database+";integratedSecurity=true;";

		var xml = this._getQueryXML(driver, connectionString, sqlQuery);

		var eccQueueId = this._postQueryToEccQueue(midServer, xml);

		var eccResponse = this._waitForQueryResponse(eccQueueId, timeout);

		if(!eccResponse.sys_id){
			eccQueueId = 0;
		} else {
			var eccRes = new GlideRecord("ecc_queue");
			if(eccRes.get('sys_id',eccResponse.sys_id)){
				queryResults = eccRes.payload.toString();
			}
		}
		return queryResults;
	},
	_getQueryXML:function(driver, connectionString, sqlQuery){
		var xml = '<?xml version="1.0" encoding="UTF-8"?>';
		xml = '<parameters>';
		xml += '<parameter name="jdbc_driver" value="'+driver+'"/>';
		xml += '<parameter name="connection_string" value="'+connectionString+'"/>';
		xml += '<parameter name="query" value="Specific SQL"/>';
		xml += '<parameter name="sql_statement" value="'+sqlQuery+'"/>';
		xml += '</parameters>';
		return xml;
	},
	_postQueryToEccQueue:function(midServer, xml){
		var ecc = new GlideRecord("ecc_queue");
		ecc.initialize();
		ecc.agent="mid.server." + midServer;
		ecc.topic="JDBCProbe";
		ecc.payload=xml;
		ecc.queue="output";
		ecc.state="ready";
		ecc.insert();

		return ecc.sys_id;
	},
	_waitForQueryResponse:function(eccQueueId, timeout){
		var resp = new GlideRecord("ecc_queue");
		resp.addQuery("response_to", eccQueueId);
		resp.addQuery("agent","JDBCProbeResult");
		resp.addQuery("queue", "input");

		var counter = 0;
		do{
			resp.query();
			resp.next();
			gs.sleep(200); //wait before trying again
			counter ++;
		} while(!resp.sys_id && counter < timeout);
		return resp;
	},
	
	type: 'dataWarehouse'
});

When debugging results, it is extremely useful to look at the ECC Queue

find_real_file.png

Comments
sharepointau
Giga Guru

This is a great article. I have been trying to do this exact thing for months.... off and on, but still. 

Thanks to your article I'm able to query SQL and get the result to the ecc queue. However, I am unable to return "queryResults." Could you take a look at this script include and help me figure out why it's not returning a result? queryResults are always returned to my client script as " ". 

var SI2 = Class.create();
SI2.prototype = Object.extendsObject(AbstractAjaxProcessor, {
	
	executeRemoteQuery:function(){
		var timeout = 30, queryResults='';
		var driver = this.getParameter('sysparm_driver');
		var midServer = this.getParameter('sysparm_midserver');
		var sqlQuery = this.getParameter('sysparm_sqlquery');
		var dbServer  = "xxx";
		var dbPort    = "xxx";
		var dbInstance = "xxx";
		var dbUser = "xxx";
		var dbPassword = "xxx";
		var database  = "xxx";

var connectionString = "jdbc:sqlserver://"+dbServer+":"+dbPort+";instanceName="+dbInstance+";databaseName="+database+";user="+dbUser+";password="+dbPassword;
			
		var xml = this._getQueryXML(driver, connectionString, sqlQuery);
		var eccQueueId = this._postQueryToEccQueue(midServer, xml);
		var eccResponse = this._waitForQueryResponse(eccQueueId, timeout);		


		/****DEBUGGING*****/
		gs.log("Script Include - driver: " + driver);
		gs.log("Script Include - sqlQuery: " + sqlQuery);		
		gs.log("Script Include - eccResponse: " + eccResponse.toString());
		gs.log("TEST eccRes.get('sys_id',eccResponse.sys_id): " + eccRes.get('sys_id',eccResponse.sys_id));
		gs.log("TEST eccRes.get('sys_id'): " + eccRes.get('sys_id'));
		gs.log("TEST eccRes.getValue('sys_id'): " + eccRes.getValue('sys_id'));
		gs.log("Script Include - eccResponse.sys_id: " + eccResponse.sys_id);
		/****DEBUGGING*****/
		
            var test = new GlideRecord("ecc_queue");
		gs.log("TEST 12345" + test.sys_id);

		if(!eccResponse.sys_id){
			eccQueueId = 0;
		} else {
			var eccRes = new GlideRecord("ecc_queue");
		gs.log("Script Include - beforeIF eccRes.get('sys_id',eccResponse.sys_id): " + eccRes.getValue('sys_id'));
			if(eccRes.get('sys_id',eccResponse.sys_id)){
		gs.log("Script Include - afterIF eccRes.get('sys_id',eccResponse.sys_id): " + eccRes.get('sys_id',eccResponse.sys_id));
				queryResults = eccRes.payload.toString();
			}
		}
		gs.log("Script Include - queryResults: " + queryResults);
		return queryResults;
	},
	_getQueryXML:function(driver, connectionString, sqlQuery){
		var xml = '<?xml version="1.0" encoding="UTF-8"?>';
		xml = '<parameters>';
		xml += '<parameter name="jdbc_driver" value="'+driver+'"/>';
		xml += '<parameter name="connection_string" value="'+connectionString+'"/>';
		xml += '<parameter name="query" value="Specific SQL"/>';
		xml += '<parameter name="sql_statement" value="'+sqlQuery+'"/>';
		xml += '</parameters>';
		gs.log("Script Include - xml: " + xml);
		return xml;
	},
	_postQueryToEccQueue:function(midServer, xml){
		var ecc = new GlideRecord("ecc_queue");
		ecc.initialize();
		ecc.agent="mid.server." + midServer;
		ecc.topic="JDBCProbe";
		ecc.payload=xml;
		ecc.queue="output";
		ecc.state="ready";
		ecc.insert();

		return ecc.sys_id;
	},
	_waitForQueryResponse:function(eccQueueId, timeout){
		var resp = new GlideRecord("ecc_queue");
		
		gs.log("Script Include - eccQueueId: " + eccQueueId);
		
		resp.addQuery("response_to", eccQueueId);
		resp.addQuery("agent","JDBCProbeResult");
		resp.addQuery("queue", "input");

		var counter = 0;
		do{
			resp.query();
			resp.next();
			gs.sleep(200); //wait before trying again 
			counter ++;
		} while(!resp.sys_id && counter < timeout);
		return resp;
	},
	
	type: 'SI2'
});

  

Peter Cawdron
Kilo Explorer

Look at ecc_queue.list immediately after attempting your integration call. You should see several entries. The initial outbound call and a bunch of subsequent entries in the queue. If there's lots of noise and it's difficult to determine what's happening, filter based on the response to sys_id matching the sys_id of your original request. But your results should be in a record that looks something like this... 

find_real_file.png

Also, double check that your query works when run independently AND that your MID Server has the appropriate credentials (trust) to allow it to run that query against your database. You may need to pass credentials in the connection string, but these records should allow you to see what the MID server is getting as a response from the database and hopefully steer you in the right direction.

Kind regards,

Peter

Peter Cawdron
Kilo Explorer

This is what your ECC Queue should look like

find_real_file.png

Version history
Last update:
‎06-02-2019 09:16 PM
Updated by: