/** * jqAxisClient * * jQuery Axis Client * A client written in JavaScript (using jQuery) intended * for the Apache SOAP implementation project named 'Axis'. * * Kurt Gubi * http://plugins.jquery.com/project/jqAxisClient */ var XMLNODE_ELEMENT = 1; var XMLNODE_TEXT = 3; var XMLNODE_COMMENT = 8; var XMLNODE_XMLDOCUMENT = 9; // String "endsWith" function String.prototype.endsWith = function(s) { return (this.lastIndexOf(s) == this.length-s.length); } /* * Constructor, takes WSDL url as the single parameter * 1. includes WSDL methods * 2. retrieves WSDL * 3. retrieves complex methods and methods from WSDL * 4. defines 'call' method (to hit webservice actions) */ var jqAxisClient = function(wsdl_url) { // retrieve WSDL as an XMLDocument this.wsdl = $.ajax({ type: 'GET', url: wsdl_url, async: false, processData: false, dataType: 'xml' }).responseXML; // set root node if(this.wsdl.nodeType == XMLNODE_XMLDOCUMENT) this.rootNode = this.wsdl.documentElement; else this.rootNode = this.wsdl; this.targetNamespace = $(this.rootNode).attr('targetNamespace'); this.serviceUrl = $(this.rootNode).find('service port address').attr('location'); this.typeList = jqAxisClient._generateTypeList(this.rootNode); this.methodList = jqAxisClient._generateMethodList(this.rootNode); this.call = function(methodName, parameters, callback) { jqAxisClient._runMethod(this, methodName, parameters, callback); } } // Webservice callback error handler jqAxisClient._callbackError = function(request, error, exception) { alert(error); } /* * Webservice method callback handler * Extracts and parses return value, then passes either text, an object, * or null (if nothing was returned) to the specified method */ jqAxisClient._callbackHandler = function(client, data, methodName, callback) { if(callback == null) { return; } if(data == null || data.nodeType != XMLNODE_XMLDOCUMENT) { throw new Exception('Invalid response data.'); } var returnList = $(data).find(methodName + 'Response > ' + methodName + 'Return'); if(returnList.length > 0) { if(returnList[0].childNodes.length == 1 && returnList[0].childNodes[0].nodeType == XMLNODE_TEXT) { callback($(returnList[0]).text()); return; } else { var retObjId = $(returnList[0]).attr('href'); if(retObjId == null) { callback(null); return; } var retObj = $(data).find(retObjId); callback( jqAxisClient._xmlToJSON(client, data, $(data).find(retObjId)[0] ) ); return; } } else { callback(null); return; } } /* * Method to post to the webservice action * Takes the requested webservice method name, webservice method parameters, * and the callback method */ jqAxisClient._runMethod = function(client, methodName, parameters, callback) { var soapMessage = jqAxisClient._createSoapMessage(client, methodName, parameters); $.ajax({ url: client.serviceUrl, type: 'POST', dataType: 'xml', data: soapMessage, processData: false, async: true, error: jqAxisClient._callError, success: function(data) { jqAxisClient._callbackHandler(client, data, methodName, callback); }, beforeSend: function(req) { req.setRequestHeader('Method', 'POST'); req.setRequestHeader('Content-Length', soapMessage.toString().length); req.setRequestHeader('Content-Type', 'text/xml; charset=utf-8'); req.setRequestHeader('SOAPAction', methodName); } }); } /* * Constructs a soap message */ jqAxisClient._createSoapMessage = function(client, methodName, parameters) { var msg = ''; var methodDesc = client.methodList.sendMethods[methodName]; // message header msg += ''; msg += '' + ''; if(parameters != null) { // iterate through parameters of method described in WSDL $(methodDesc).each( function(paramIndex, curParam) { // attempt to retrieve respective parameter from passed values if(parameters[curParam.name] != null) { //convert variable to XML msg += jqAxisClient._toXML(client, parameters[curParam.name], curParam.name, curParam.type); } }); } // message footer msg += ''; msg += ''; return msg; } /* * Converts an object to XML, conforming to the WSDL */ jqAxisClient._toXML = function(client, object, objName, objType) { var msg = ''; // if not described in WSDL, assume primitive if(client.typeList[objType] == null) { // don't bother setting an empty tag (or Axis starts throwing // unnecessary faults) if(object != null && object.toString() != '') { msg = '<'+objName+'>'; msg += object.toString(); msg += ''; } } // look up complex type from WSDL type list else { msg += '<'+objName+'>'; // iterate through each variable of complex type $(client.typeList[objType]._variables).each( function(varIndex, curVariable) { // if object defines this variable if(object[curVariable] != null) { // convert object variable to XML msg += jqAxisClient._toXML(client, object[curVariable], curVariable, client.typeList[objType][curVariable]); } }); msg += ''; } return msg; } /* * Converts an XML node to a JSON object. * The variables are listed in "_variables"; * -> myJSON._variables * -> myJSON[myJSON._variables[0]] * If the nodes are multiRef tags, it will end up being a classic array * -> myJSON[0]; myJSON[1]; myJSON[2]; */ jqAxisClient._xmlToJSON = function(client, xmlDoc, xmlNode) { if(xmlDoc == null || xmlNode == null) return null; var retObj = new Object(); retObj._variables = new Array(); // if this xml node has any child nodes, include them in the evaluation if($(xmlNode).children() != null && $(xmlNode).children().length > 0) { // iterate through the child nodes $($(xmlNode).children()).each( function(childIndex, curChild) { // if the current child node is a "multiRef" then use that as the current node instead if(curChild.nodeName == 'multiRef') { // multiRef id and corressponding multiRef var refId = $(curChild).attr('href'); var ref = $(xmlDoc).find(refId)[0]; if(ref != null) { // add variable and recursively evaluate child retObj._variables.push(childIndex); retObj[childIndex] = jqAxisClient._xmlToJSON(client, xmlDoc, ref); } } else { // add variable and recursively evaluate child retObj._variables.push(curChild.nodeName); retObj[curChild.nodeName] = jqAxisClient._xmlToJSON(client, xmlDoc, curChild); } }); } else { return $(xmlNode).text(); } return retObj; } /* * Retrieves the list of complex types for this service, given the document root node * Usage of return object ('typeList'); * * get list of type names in the list: * -> typeList._types * get list of variable names of 'someObject': * -> typeList.someObject._variables * * get type of 'someVariable' of 'someObject': * -> typeList.someObject.someVariable */ jqAxisClient._generateTypeList = function(rootNode) { // validate root node if(rootNode == null || rootNode.nodeType == null || rootNode.nodeType != XMLNODE_ELEMENT) return null; // get list of object types (as XML node) var xmlTypes = $(rootNode).find('types > schema > complexType > sequence'); var typeList = new Array(); typeList._types = new Array(); var curObjectName = ''; var curVariableName = ''; var curVariableType = ''; // iterate through each object type $(xmlTypes).each( function(objectIndex, curObject) { // iterate through each variable of the object $( $(curObject).children() ).each( function(variableIndex, curVariable) { // object name comes from parent node ('complexType') curObjectName = $(curObject.parentNode).attr('name'); curVariableName = $(curVariable).attr('name'); curVariableType = $(curVariable).attr('type'); // remove type namespace if(curVariableType.indexOf(':') >= 0) { curVariableType = curVariableType.substring(curVariableType.indexOf(':')+1); } // init object if this is the first variable set if(typeList[curObjectName] == null) { typeList[curObjectName] = new Array(); // add to list of object names typeList._types.push(curObjectName); } // init variable list for object if(typeList[curObjectName]._variables == null) typeList[curObjectName]._variables = new Array(); // add to list of object variable names typeList[curObjectName]._variables.push(curVariableName); // add variable/variable-type typeList[curObjectName][curVariableName] = curVariableType; }); }); return typeList; } /* * Retrieves the list of available methods for this service, given the document root node * Usage of return object ('methodList'); * * get list of send-methods * -> methodList.sendMethods._methods * get number of parameters for method 'someMethod' * -> methodList.sendMethods.someMethod.length * get the type of the first parameter for method 'someMethod' * -> methodList.sendMethods.someMethod[0] * * get list of receive-methods * -> methodList.recvMethods._methods * get the return type for the method 'someMethod' * -> methodList.recvMethods.someMethod */ jqAxisClient._generateMethodList = function(rootNode) { var SEND_IDENTIFIER = 'Request'; var RECEIVE_IDENTIFIER = 'Response'; // validate root node if(rootNode == null || rootNode.nodeType == null || rootNode.nodeType != XMLNODE_ELEMENT) return null; // get the list of object types (as an XML node) var xmlMessages = $(rootNode).find('message'); var messageList = new Object(); messageList.sendMethods = new Array(); messageList.sendMethods._methods = new Array(); messageList.recvMethods = new Array(); messageList.recvMethods._methods = new Array(); var curMethodName = ''; var curParamType = ''; var curReturnType = ''; // iterate through each method $(xmlMessages).each( function(msgIndex, curMsg) { curMethodName = $(curMsg).attr('name'); // send-methods if(curMethodName.endsWith(SEND_IDENTIFIER)) { // truncate method name ; init method ; add it to methods list ; add each parameter curMethodName = curMethodName.substring(0, curMethodName.length-SEND_IDENTIFIER.length); messageList.sendMethods[curMethodName] = new Array(); messageList.sendMethods._methods.push(curMethodName); // iterate through parameters $( $(curMsg).find('part') ).each( function(paramIndex, curParam) { messageList.sendMethods[curMethodName][paramIndex] = new Object(); messageList.sendMethods[curMethodName][paramIndex].name = $(curParam).attr('name'); curParamType = $(curParam).attr('type'); // remove type namespace if(curParamType.indexOf(':') >= 0) { curParamType = curParamType.substring(curParamType.indexOf(':')+1); } messageList.sendMethods[curMethodName][paramIndex].type = curParamType; }); } // receive-methods else if(curMethodName.endsWith(RECEIVE_IDENTIFIER)) { // ensure it has a return type defined var returnType = $(curMsg).find('part'); if(returnType.length == 1) { // truncate method name ; add it to methods list ; set return type curMethodName = curMethodName.substring(0, curMethodName.length-RECEIVE_IDENTIFIER.length); messageList.recvMethods._methods.push(curMethodName); curReturnType = $(returnType[0]).attr('type'); // remove type namespace if(curReturnType.indexOf(':') >= 0) { curReturnType = curReturnType.substring(curReturnType.indexOf(':')+1); } messageList.recvMethods[curMethodName] = curReturnType; } } }); return messageList; }