|
|||||||
![]() |
|
|
Thread Tools |
|
#1
|
|||
|
|||
|
Hi guys,
this is my first post in this ext forum. i'm going to share with you a ext extension to connecting and retrieve data from a WebService. I attach 4 file: - SoapClient.js (a modified version of this script http://www.codeplex.com/JavaScriptSoapClient - thanks to author: Matteo Casati - http://www.guru4.net/) - Ext.data.SoapConnection.js (an extension of Ext.data.Connection that use Ext.data.SoapClient to create a connection to a WebService) - Ext.data.SoapProxy (an extension of Ext.data.DataProxy that implements a proxy for using SoapConnection) - Ext.data.SoapReader (en extension of Ext.data.DataReader that convert data in an object undestandable for Ext.data.Store) The code isn't commented but I think that is self-explained ![]() Server-sidely I use JEE and CXF framework for generating wrapped WebService from Java method. Comments, questions, critics and bug-fixes are accepted PS sorry for my english SoapClient function SOAPClientParameters()
{
var _pl = new Array();
this.add = function(name, value)
{
_pl[name] = value;
return this;
}
this.toXml = function(ns)
{
var xml = "";
for(var p in _pl)
{
switch(typeof(_pl[p]))
{
case "string":
case "number":
case "boolean":
case "object":
xml += "<ns1:" + p + ">" + SOAPClientParameters._serialize(_pl[p], ns) + "</ns1:" + p + ">";
break;
default:
break;
}
}
return xml;
}
}
SOAPClientParameters._serialize = function(o, ns)
{
var s = "";
switch(typeof(o))
{
case "string":
s += o.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); break;
case "number":
case "boolean":
s += o.toString(); break;
case "object":
// Date
if(o.constructor.toString().indexOf("function Date()") > -1)
{
var year = o.getFullYear().toString();
var month = (o.getMonth() + 1).toString(); month = (month.length == 1) ? "0" + month : month;
var date = o.getDate().toString(); date = (date.length == 1) ? "0" + date : date;
var hours = o.getHours().toString(); hours = (hours.length == 1) ? "0" + hours : hours;
var minutes = o.getMinutes().toString(); minutes = (minutes.length == 1) ? "0" + minutes : minutes;
var seconds = o.getSeconds().toString(); seconds = (seconds.length == 1) ? "0" + seconds : seconds;
var milliseconds = o.getMilliseconds().toString();
var tzminutes = Math.abs(o.getTimezoneOffset());
var tzhours = 0;
while(tzminutes >= 60)
{
tzhours++;
tzminutes -= 60;
}
tzminutes = (tzminutes.toString().length == 1) ? "0" + tzminutes.toString() : tzminutes.toString();
tzhours = (tzhours.toString().length == 1) ? "0" + tzhours.toString() : tzhours.toString();
var timezone = ((o.getTimezoneOffset() < 0) ? "+" : "-") + tzhours + ":" + tzminutes;
s += year + "-" + month + "-" + date + "T" + hours + ":" + minutes + ":" + seconds + "." + milliseconds + timezone;
}
// Array
else if(o.constructor.toString().indexOf("function Array()") > -1)
{
for(var p in o)
{
if(!isNaN(p)) // linear array
{
(/function\s+(\w*)\s*\(/ig).exec(o[p].constructor.toString());
var type = RegExp.$1;
switch(type)
{
case "":
type = typeof(o[p]);
case "String":
type = "string"; break;
case "Number":
type = "int"; break;
case "Boolean":
type = "bool"; break;
case "Date":
type = "DateTime"; break;
}
s += "<ns2:" + type + " xmlns:ns2=\""+ns+"\">" + SOAPClientParameters._serialize(o[p], ns) + "</ns2:" + type + ">"
}
else // associative array
s += "<ns2:" + p + " xmlns:ns2=\""+ns+"\">" + SOAPClientParameters._serialize(o[p], ns) + "</ns2:" + p + ">"
}
}
// Object or custom function
else
for(var p in o)
s += "<ns2:" + p + " xmlns:ns2=\""+ns+"\">" + SOAPClientParameters._serialize(o[p], ns) + "</ns2:" + p + ">";
break;
default:
break; // throw new Error(500, "SOAPClientParameters: type '" + typeof(o) + "' is not supported");
}
return s;
}
function SoapClient(url, method, parameters, async, callback, scope, arg) {
this.url = url;
this.method = method;
this.parameters = parameters;
this.async = async;
this.callback = callback;
this.scope = scope || this;
this.arg = arg || null;
}
SoapClient._cacheWsdl = new Array(); //static
SoapClient.prototype.invoke = function() {
if(this.async)
this._loadWsdl();
else
return this._loadWsdl();
}
SoapClient.prototype._loadWsdl = function() {
// load from cache?
var wsdl = SoapClient._cacheWsdl[this.url];
if(wsdl + "" != "" && wsdl + "" != "undefined") {
this.wsdl = wsdl;
return this._sendSoapRequest();
}
// get wsdl
var xmlHttp = SoapClient._getXmlHttp();
xmlHttp.open("GET", this.url + "?wsdl", this.async);
if(this.async) {
var that = this;
xmlHttp.onreadystatechange = function() {
if(xmlHttp.readyState == 4)
that._onLoadWsdl(xmlHttp);
}
}
xmlHttp.send(null);
if (!this.async)
return this._onLoadWsdl(xmlHttp);
}
SoapClient.prototype._onLoadWsdl = function(req) {
var wsdl = req.responseXML;
this.wsdl = wsdl;
SoapClient._cacheWsdl[this.url] = wsdl; // save a copy in cache
return this._sendSoapRequest();
}
SoapClient.prototype._sendSoapRequest = function() {
var wsdl = this.wsdl;
// get namespace
var ns = (wsdl.documentElement.attributes["targetNamespace"] + "" == "undefined") ? wsdl.documentElement.attributes.getNamedItem("targetNamespace").nodeValue : wsdl.documentElement.attributes["targetNamespace"].value;
var ns2 = wsdl.getElementsByTagName("schema")[0].getAttribute("targetNamespace");
// build SOAP request
var sr = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope " +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" " +
"xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
"<soap:Body>" +
"<ns1:" + this.method + " xmlns:ns1=\"" + ns + "\">" +
this.parameters.toXml(ns2) +
"</ns1:" + this.method + "></soap:Body></soap:Envelope>";
// send request
var xmlHttp = SoapClient._getXmlHttp();
/* not implemented */
if (SoapClient.userName && SoapClient.password){
xmlHttp.open("POST", url, async, SoapClient.userName, SoapClient.password);
// Some WS implementations (i.e. BEA WebLogic Server 10.0 JAX-WS) don't support Challenge/Response HTTP BASIC, so we send authorization headers in the first request
xmlHttp.setRequestHeader("Authorization", "Basic " + SoapClient_toBase64(SoapClientuserName + ":" + SoapClientpassword));
}
/* end of not implemented */
else
xmlHttp.open("POST", this.url, this.async);
var soapaction = ((ns.lastIndexOf("/") != ns.length - 1) ? ns + "/" : ns) + this.method;
xmlHttp.setRequestHeader("SOAPAction", soapaction);
xmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
if(this.async) {
var that = this;
xmlHttp.onreadystatechange = function() {
if(xmlHttp.readyState == 4)
that._onSendSoapRequest(xmlHttp);
}
}
xmlHttp.send(sr);
if (!this.async)
return this._onSendSoapRequest(xmlHttp);
}
SoapClient.prototype._onSendSoapRequest = function(req) {
var wsdl = this.wsdl;
var o = null;
var nd = SoapClient._getElementsByTagName(req.responseXML, this.method + "Response");
if(nd.length == 0)
nd = SoapClient._getElementsByTagName(req.responseXML, "return"); // PHP web Service?
if(nd.length == 0)
{
if(req.responseXML.getElementsByTagName("faultcode").length > 0)
{
if(this.async || this.callback)
o = new Error(500, req.responseXML.getElementsByTagName("faultstring")[0].childNodes[0].nodeValue);
else
throw new Error(500, req.responseXML.getElementsByTagName("faultstring")[0].childNodes[0].nodeValue);
}
}
else
o = this._soapresult2object(nd[0]);
if(this.callback)
Ext.callback(this.callback, this.scope, [this.arg, true, o]);
if(!this.async)
return o;
}
SoapClient.prototype._soapresult2object = function(node) {
var wsdl = this.wsdl;
this.wsdlTypes = this._getTypesFromWsdl();
return this._node2object(node);
}
SoapClient.prototype._node2object = function(node) {
wsdlTypes = this.wsdlTypes
// null node
if(node == null)
return null;
// text node
if(node.nodeType == 3 || node.nodeType == 4)
return this._extractValue(node);
// leaf node
if (node.childNodes.length == 1 && (node.childNodes[0].nodeType == 3 || node.childNodes[0].nodeType == 4))
return this._node2object(node.childNodes[0]);
var isarray = this._getTypeFromWsdl(node.nodeName.substring(node.nodeName.indexOf(":")+1)).toLowerCase().indexOf("arrayof") != -1;
// object node
if(!isarray)
{
var obj = null;
if(node.hasChildNodes())
obj = new Object();
for(var i = 0; i < node.childNodes.length; i++)
{
var p = this._node2object(node.childNodes[i]);
obj[node.childNodes[i].nodeName.substring(node.childNodes[i].nodeName.indexOf(":")+1)] = p;
}
return obj;
}
// list node
else
{
// create node ref
var l = new Array();
for(var i = 0; i < node.childNodes.length; i++)
l[l.length] = this._node2object(node.childNodes[i]);
return l;
}
return null;
}
SoapClient.prototype._extractValue = function(node) {
var wsdlTypes = this.wsdlTypes;
var value = node.nodeValue;
switch(this._getTypeFromWsdl(node.nodeName.substring(node.nodeName.indexOf(":")+1)).toLowerCase())
{
default:
case "s:string":
return (value != null) ? value + "" : "";
case "s:boolean":
return value + "" == "true";
case "s:int":
case "s:long":
return (value != null) ? parseInt(value + "", 10) : 0;
case "s:double":
return (value != null) ? parseFloat(value + "") : 0;
case "s:datetime":
if(value == null)
return null;
else
{
value = value + "";
value = value.substring(0, (value.lastIndexOf(".") == -1 ? value.length : value.lastIndexOf(".")));
value = value.replace(/T/gi," ");
value = value.replace(/-/gi,"/");
var d = new Date();
d.setTime(Date.parse(value));
return d;
}
}
}
SoapClient.prototype._getTypesFromWsdl = function() {
var wsdl = this.wsdl;
var wsdlTypes = new Array();
// IE
var ell = wsdl.getElementsByTagName("s:element");
var useNamedItem = true;
// MOZ
if(ell.length == 0)
{
ell = wsdl.getElementsByTagName("element");
useNamedItem = false;
}
for(var i = 0; i < ell.length; i++)
{
if(useNamedItem)
{
if(ell[i].attributes.getNamedItem("name") != null && ell[i].attributes.getNamedItem("type") != null)
wsdlTypes[ell[i].attributes.getNamedItem("name").nodeValue] = ell[i].attributes.getNamedItem("type").nodeValue;
}
else
{
if(ell[i].attributes["name"] != null && ell[i].attributes["type"] != null)
wsdlTypes[ell[i].attributes["name"].value] = ell[i].attributes["type"].value;
}
}
return wsdlTypes;
}
SoapClient.prototype._getTypeFromWsdl = function(elementname) {
var wsdlTypes = this.wsdlTypes;
var type = wsdlTypes[elementname] + "";
return (type == "undefined") ? "" : type;
}
/* Static method */
// private: utils
SoapClient._getElementsByTagName = function(document, tagName) {
try {
// trying to get node omitting any namespaces (latest versions of MSXML.XMLDocument)
return document.selectNodes(".//*[local-name()=\""+ tagName +"\"]");
}
catch (ex) {}
// old XML parser support
return document.getElementsByTagName(tagName);
}
// private: xmlhttp factory
SoapClient._getXmlHttp = function() {
try
{
if(window.XMLHttpRequest)
{
var req = new XMLHttpRequest();
// some versions of Moz do not support the readyState property and the onreadystate event so we patch it!
if(req.readyState == null)
{
req.readyState = 1;
req.addEventListener("load",
function()
{
req.readyState = 4;
if(typeof req.onreadystatechange == "function")
req.onreadystatechange();
},
false);
}
return req;
}
if(window.ActiveXObject)
return new ActiveXObject(SoapClient_getXmlHttpProgID());
}
catch (ex) {}
throw new Error("Your browser does not support XmlHttp objects");
}
SoapClient._getXmlHttpProgID = function()
{
if(SOAPClient._getXmlHttpProgID.progid)
return SOAPClient._getXmlHttpProgID.progid;
var progids = ["Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
var o;
for(var i = 0; i < progids.length; i++)
{
try
{
o = new ActiveXObject(progids[i]);
return SOAPClient._getXmlHttpProgID.progid = progids[i];
}
catch (ex) {};
}
throw new Error("Could not find an installed XML parser");
}
SoapClient._toBase64 = function(input)
{
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) +
keyStr.charAt(enc3) + keyStr.charAt(enc4);
} while (i < input.length);
return output;
}
Ext.data.SoapConnection Ext.data.SoapConnection = function(config){
Ext.apply(this, config);
Ext.data.SoapConnection.superclass.constructor.call(this);
}
Ext.extend(Ext.data.SoapConnection, Ext.data.Connection, {
request : function(o){
if(this.fireEvent("beforerequest", this, o) !== false){
var url = o.url || this.url;
var method = o.method || this.method;
var param = o.param || this.param;
var callback = o.callback || this.callback;
var scope = o.scope || this.scope;
var pl = new SOAPClientParameters();
if(param) for(i in param) pl.add(i, param[i]);
var client = new SoapClient(url, method, pl, true, callback, scope, o);
client.invoke();
}else{
Ext.callback(o.callback, this, [o, null, null]);
}
}
});
/* Costruttore */
Ext.data.SoapProxy = function(config){
Ext.data.SoapProxy.superclass.constructor.call(this);
this.conn = new Ext.data.SoapConnection(config);
};
Ext.extend(Ext.data.SoapProxy, Ext.data.DataProxy, {
getConnection : function(){
return this.conn;
},
load : function(params, reader, callback, scope, arg){
if(this.fireEvent("beforeload", this, params) !== false){
params.callback = this.loadResponse;
params.scope = this;
params.request = {};
params.request.callback = callback;
params.request.reader = reader;
params.request.scope = scope || this;
params.request.arg = arg || null;
this.conn.request(params);
}else{
callback.call(scope||this, null, arg, false);
}
},
// private
loadResponse : function(o, success, response){
delete this.activeRequest;
if(!success){
this.fireEvent("loadexception", this, o, response);
o.request.callback.call(o.request.scope, null, o.request.arg, false);
return;
}
var result;
try {
result = o.request.reader.read(response);
}catch(e){
this.fireEvent("loadexception", this, o, response, e);
o.request.callback.call(o.request.scope, null, o.request.arg, false);
return;
}
this.fireEvent("load", this, o, o.request.arg);
o.request.callback.call(o.request.scope, result, o.request.arg, true);
},
// private
update : function(dataSet){
},
// private
updateResponse : function(dataSet){
}
});
Ext.data.SoapReader Ext.data.SoapReader = function(meta, recordType){
meta = meta || {};
Ext.data.SoapReader.superclass.constructor.call(this, meta, recordType || meta.fields);
};
Ext.extend(Ext.data.SoapReader, Ext.data.DataReader, {
read: function(request) {
var recordType = this.recordType, fields = recordType.prototype.fields;
for(i in request) request = request[i];
for(i in request) {
var id = request[i][this.meta.id];
request[i] = new recordType(request[i], id);
}
return {
success : true,
records : request,
totalRecords : request.length
};
}
});
And now... a usage sample for creating a store using SOAP var o = {
url : "http://localhost:8080/SoapTest/ws/MyService",
method : "MyWebMethod"
}
var proxy = new Ext.data.SoapProxy(o);
var reader = new Ext.data.SoapReader({
id:'id'
}, ['name','title','birthdate']);
var store = new Ext.data.Store({
proxy: proxy,
reader: reader
});
store.load();
|
|
#2
|
|||
|
|||
|
nice work!
|
|
#3
|
|||
|
|||
|
i've upgraded my post...
the extension is complete (it's possible to download the code as an attachment) and there is a simple usage example have a good ext-time |
|
#4
|
|||
|
|||
|
Hey,
i have revied your soap client code. Why you havent updated the http-requests to Ext.Ajax.request? Is there any reason for let it be like it is? Michael |
|
#5
|
|||
|
|||
|
Hi, thanks to your code I wrote custom classes using Mozilla's wsdl API :
To use mozilla API you have to read security model
var KeywordSearchRequest = new Object();
KeywordSearchRequest.keyword="Hadrien"
KeywordSearchRequest.mode="books";
KeywordSearchRequest.tag="webservices-20";
KeywordSearchRequest.type="lite";
KeywordSearchRequest.devtag="D2Z2KU2NWTOHI";
KeywordSearchRequest.format="xml";
KeywordSearchRequest.version="1.0";
var o = {
url : "http://soap.amazon.com/schemas2/AmazonWebServices.wsdl",
port: "AmazonSearchPort",
method : "KeywordSearchRequest"
}
var proxy = new Ext.data.SoapProxy(o);
var reader = new Ext.data.SoapReader({
id:'Asin',
record: "Details",
totalRecords: "TotalPages"
}, [
{ name : 'productName', mapping : 'ProductName'},
{ name : 'url', mapping : 'Url'},
{ name : 'authors', mapping : 'Authors'},
{ name : 'availability', mapping : 'Availability'},
{ name : 'manufacturer', mapping : 'Manufacturer'},
{ name : 'imgUrl', mapping : 'ImageUrlSmall' },
{ name : 'price', mapping : 'ListPrice'}, // type : 'float'},
{ name : 'releaseDate', mapping : 'ReleaseDate', type : 'date'}]);
var store = new Ext.data.Store({
proxy: proxy,
reader: reader,
baseParams : KeywordSearchRequest
});
grid = new Ext.grid.GridPanel({
store: store,
columns: [
{id:'product',header: "Product Name", width: 160, sortable: true, dataIndex: 'productName'},
{header: "List price", width: 75, sortable: true, dataIndex: 'price'},
{header: "Manufacturer", width: 75, sortable: true, dataIndex: 'manufacturer'},
{header: "Release date", width: 85, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'releaseDate'}
],
viewConfig: {
forceFit:true
},
loadMask: true,
height:350,
width:600,
title:'Amazon',
renderTo: document.body
});
store.load();
|
|
#6
|
|||
|
|||
|
I've used the soap js library technic for creating connection but i'm actually working for a better version of this package, more Ext-able with hierarchy, static classes and implementation of Ext pseudo-interface...
I'm analysing source/data/Connection.js because i want to extends this class but i read this method invocation Ext.lib.Ajax.request (row 219). This is very different from Ext.Ajax but i can't find its source code. I think that Ext.lib.Ajax is a low-level object to open connection with XmlHttpRequest but without documentation and source code is too difficult to use ![]() can someone help me?? for newer version stay tuned ![]() |
|
#7
|
|||
|
|||
|
Hi,
i have extended source/data/Connection.js before. I have not had to change the ajax request for my purposes. Is it important for you to do so? If yes, why dont use Ext.Ajax.request for http-requests? What do you want to have changed? Maybe i can help. Michael |
|
#8
|
|||
|
|||
|
Your extension only works in FF, but not in IE7 (XP).
|
|
#9
|
|||
|
|||
|
Quote:
I would like to use the Ext-technique because if I use Ext.ajax I will use another connection and not the current one... It's not strange creating a connection object that uses another connection?? i hope my bad english will be understood... |
|
#10
|
|||
|
|||
|
Quote:
But for me its strange why to change the http requests byself. Why dont let it like it is and change only the content to send and the handling after recive? |
![]() |
| Thread Tools | |
|
|