前段时间,为了能提高用户操作的友好度写了大量的Js+XMLHTTP类型的应用。随着项目的扩大,细节的补充,这些代码越来越难维护,考虑到XML-RPC相对于SOAP的轻便,相对于私有协议的标准性,兼之以前曾在Asp框架下实现过一些RPC应用,所以考虑将所有的同类型应用都统一处理,换为XML-RPC应用。
首先,完成服务器端,在网上查资料,Apache XML-RPC可以完成此功能。找到的例程基于Servlet,麻烦...改写一下。rpc.jsp 主体部分如下:
***********************************************************
<%@ page contentType="text/xml; charset=gbk" import="org.apache.xmlrpc.*,java.io.*"%><%!
public class AllFunc {
public String sayHello(String str){
return "Hello," + str;
}
public String sayHello1(String str,String str1){
return escape("Hello," + unescape(str) + unescape(str1));
}
}
%><%
XmlRpcServer xmlrpc = new XmlRpcServer();
xmlrpc.addHandler("AllFunc", new AllFunc());
byte[] result = xmlrpc.execute(request.getInputStream());
response.setContentType("text/xml");
response.setContentLength(result.length);
OutputStream out1 = response.getOutputStream();
out1.write(result);
out1.flush();
%>
***********************************************************
sayHello方法是最简单的一个例子,可以用在英文参数,英文返回的情况
sayHello1方法是专门为处理中文参数,中文返回的情况
通过 unescape,escape方法处理中文,这个是自己写的函数,模拟js中这两个方法,代码如下(可以直接放到声明区..也可以放到公共函数文件Common.jsp
***********************************************************
<%!
private final static String[] hex = {
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A",
"0B", "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15",
"16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20",
"21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B",
"2C", "2D", "2E", "2F", "30", "31", "32", "33", "34", "35", "36",
"37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F", "40", "41",
"42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C",
"4D", "4E", "4F", "50", "51", "52", "53", "54", "55", "56", "57",
"58", "59", "5A", "5B", "5C", "5D", "5E", "5F", "60", "61", "62",
"63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D",
"6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77", "78",
"79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
"84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E",
"8F", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
"9A", "9B", "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4",
"A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
"B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA",
"BB", "BC", "BD", "BE", "BF", "C0", "C1", "C2", "C3", "C4", "C5",
"C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF", "D0",
"D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB",
"DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3", "E4", "E5", "E6",
"E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF", "F0", "F1",
"F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC",
"FD", "FE", "FF"
};
private final static byte[] val = {
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F
};
public static String escape(String s) {
StringBuffer sbuf = new StringBuffer();
int len = s.length();
for (int i = 0; i < len; i++) {
int ch = s.charAt(i);
if (ch == ' ') { // space : map to '+'
sbuf.append('%');
sbuf.append(hex[ch]);
} else if (('A' <= ch) && (ch <= 'Z')) { // 'A'..'Z' : as it was
sbuf.append((char) ch);
} else if (('a' <= ch) && (ch <= 'z')) { // 'a'..'z' : as it was
sbuf.append((char) ch);
} else if (('0' <= ch) && (ch <= '9')) { // '0'..'9' : as it was
sbuf.append((char) ch);
} else if ((ch == '-') || (ch == '_') // unreserved : as it was
||(ch == '.') || (ch == '!') || (ch == '~') ||
(ch == '*') || (ch == '/'') || (ch == '(') || (ch == ')')) {
sbuf.append((char) ch);
} else if (ch <= 0x007F) { // other ASCII : map to %XX
sbuf.append('%');
sbuf.append(hex[ch]);
} else { // unicode : map to %uXXXX
sbuf.append('%');
sbuf.append('u');
sbuf.append(hex[(ch >>> 8)]);
sbuf.append(hex[(0x00FF & ch)]);
}
}
return sbuf.toString();
}
public static String unescape(String s) {
StringBuffer sbuf = new StringBuffer();
int i = 0;
int len = s.length();
while (i < len) {
int ch = s.charAt(i);
if (ch == '+') { // + : map to ' '
sbuf.append(' ');
} else if (('A' <= ch) && (ch <= 'Z')) { // 'A'..'Z' : as it was
sbuf.append((char) ch);
} else if (('a' <= ch) && (ch <= 'z')) { // 'a'..'z' : as it was
sbuf.append((char) ch);
} else if (('0' <= ch) && (ch <= '9')) { // '0'..'9' : as it was
sbuf.append((char) ch);
} else if ((ch == '-') || (ch == '_') // unreserved : as it was
||(ch == '.') || (ch == '!') || (ch == '~') ||
(ch == '*') || (ch == '/'') || (ch == '(') || (ch == ')')) {
sbuf.append((char) ch);
} else if (ch == '%') {
int cint = 0;
if ('u' != s.charAt(i + 1)) { // %XX : map to ascii(XX)
cint = (cint << 4) | val[s.charAt(i + 1)];
cint = (cint << 4) | val[s.charAt(i + 2)];
i += 2;
} else { // %uXXXX : map to unicode(XXXX)
cint = (cint << 4) | val[s.charAt(i + 2)];
cint = (cint << 4) | val[s.charAt(i + 3)];
cint = (cint << 4) | val[s.charAt(i + 4)];
cint = (cint << 4) | val[s.charAt(i + 5)];
i += 5;
}
sbuf.append((char) cint);
}
i++;
}
return sbuf.toString();
}
%>
***********************************************************
如此,服务器端的代码就好了....
接着是客户端 Client.htm
***********************************************************
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Untitled</title>
<script src='vcXMLRPC.js'></script>
<script type="text/javascript">
//注册服务
var done=true;
XMLRPC.onerror = function(e){
alert("执行远程调用时,发生错误:"+e);
done=false;
return true;
}
AllFunc = XMLRPC.getService("http://127.0.0.1:8080/rpc/rpc.jsp");
AllFunc.add("AllFunc.sayHello", "sayHello");
AllFunc.add("AllFunc.sayHello1", "sayHello1");
//远程调用
function doTest(){
result = AllFunc.sayHello1(escape("T%测试"),escape("T%测试"));
if (done) alert(unescape(result));
}
</script>
</head>
<body>
<input type="button" value="Make RPC" οnclick="doTest();"></p>
</body>
</html>
***********************************************************
支持的vcXMLRPC.js如下
Object.prototype.toXMLRPC = function(){
var wo = this.valueOf();
if(wo.toXMLRPC == this.toXMLRPC){
retstr = "<struct>";
for(prop in this){
if(typeof wo[prop] != "function"){
retstr += "<member><name>" + prop + "</name><value>" + XMLRPC.getXML(wo[prop]) + "</value></member>";
}
}
retstr += "</struct>";
return retstr;
}
else{
return wo.toXMLRPC();
}
}
String.prototype.toXMLRPC = function(){
//<![CDATA[***your text here***]]>
return "<string><![CDATA[" + this.replace(//]/]/g, "] ]") + "]]></string>";//.replace(/</g, "<").replace(/&/g, "&")
}
Number.prototype.toXMLRPC = function(){
if(this == parseInt(this)){
return "<int>" + this + "</int>";
}
else if(this == parseFloat(this)){
return "<double>" + this + "</double>";
}
else{
return false.toXMLRPC();
}
}
Boolean.prototype.toXMLRPC = function(){
if(this) return "<boolean>1</boolean>";
else return "<boolean>0</boolean>";
}
Date.prototype.toXMLRPC = function(){
//Could build in possibilities to express dates
//in weeks or other iso8601 possibillities
//hmmmm ????
//19980717T14:08:55
return "<dateTime.iso8601>" + doYear(this.getUTCYear()) + doZero(this.getMonth()) + doZero(this.getUTCDate()) + "T" + doZero(this.getHours()) + ":" + doZero(this.getMinutes()) + ":" + doZero(this.getSeconds()) + "</dateTime.iso8601>";
function doZero(nr) {
nr = String("0" + nr);
return nr.substr(nr.length-2, 2);
}
function doYear(year) {
if(year > 9999 || year < 0)
XMLRPC.handleError(new Error("Unsupported year: " + year));
year = String("0000" + year)
return year.substr(year.length-4, 4);
}
}
Array.prototype.toXMLRPC = function(){
var retstr = "<array><data>";
for(var i=0;i<this.length;i++){
retstr += "<value>" + XMLRPC.getXML(this[i]) + "</value>";
}
return retstr + "</data></array>";
}
function VirtualService(servername, oRPC){
this.version = '0.91';
this.URL = servername;
this.multicall = false;
this.autoroute = true;
this.onerror = null;
this.rpc = oRPC;
this.receive = {};
this.purge = function(receive){
return this.rpc.purge(this, receive);
}
this.revert = function(){
this.rpc.revert(this);
}
this.add = function(name, alias, receive){
this.rpc.validateMethodName();if(this.rpc.stop){this.rpc.stop = false;return false}
if(receive) this.receive[name] = receive;
this[(alias || name)] = new Function('var args = new Array(), i;for(i=0;i<arguments.length;i++){args.push(arguments[i]);};return this.call("' + name + '", args);');
return true;
}
//internal function for sending data
this.call = function(name, args){
var info = this.rpc.send(this.URL, name, args, this.receive[name], this.multicall, this.autoroute);
if(info){
if(!this.multicall) this.autoroute = info[0];
return info[1];
}
else{
if(this.onerror) this.onerror(XMLRPC.lastError);
return false;
}
}
}
XMLRPC = {
routeServer : "http://www.vcdn.org/cgi-bin/rpcproxy.cgi",
autoroute : true,
multicall : false,
services : {},
stack : {},
queue : new Array(),
timers : new Array(),
timeout : 30000,
ontimeout : null,
getService : function(serviceName){
//serviceNames cannot contain / or .
if(/[///.]/.test(serviceName)){
return new VirtualService(serviceName, this);
}
else if(this.services[serviceName]){
return this.services[serviceName];
}
else{
try{
var ct = eval(serviceName);
this.services[serviceName] = new ct(this);
}
catch(e){
return false;
}
}
},
purge : function(modConst, receive){
if(this.stack[modConst.URL].length){
var info = this.send(modConst.URL, "system.multicall", [this.stack[modConst.URL]], receive, false, modConst.autoroute);
modConst.autoroute = info[0];
this.revert(modConst);
if(info){
modConst.autoroute = info[0];
return info[1];
}
else{
if(modConst.onerror) modConst.onerror(this.lastError);
return false;
}
}
},
revert : function(modConst){
this.stack[modConst.URL] = new Array();
},
call : function(){
//[optional info || receive, servername,] functionname, args......
var args = new Array(), i, a = arguments;
var servername, methodname, receive, service, info, autoroute, multicall;
if(typeof a[0] == "object"){
receive = a[0][0];
servername = a[0][1].URL;
methodname = a[1];
multicall = (a[0][1].supportsMulticall && a[0][1].multicall);
autoroute = a[0][1].autoroute;
service = a[0][1];
}
else if(typeof a[0] == "function"){
i = 3;
receive = a[0];
servername = a[1];
methodname = a[2];
}
else{
i = 2;
servername = a[0];
methodname = a[1];
}
for(i=i;i<a.length;i++){
args.push(a[i]);
}
info = this.send(servername, methodname, args, receive, multicall, autoroute);
if(info){
(service || this).autoroute = info[0];
return info[1];
}
else{
if(service && service.onerror) service.onerror(this.lastError);
return false;
}
},
/***
* Perform typematching on 'vDunno' and return a boolean value corresponding
* to the result of the evaluation-match of the mask-value stated in the 2nd argument.
* The 2nd argument is optional (none will be treated as a 0-mask) or a sum of
* several masks as follows:
* type/s -> mask/s
* --------------------
* undefined -> 0/1 [default]
* number -> 2
* boolean -> 4
* string -> 8
* function -> 16
* object -> 32
* --------------------
* Examples:
* Want [String] only: (eqv. (typeof(vDunno) == 'string') )
* Soya.Common.typematch(unknown, 8)
* Anything else than 'undefined' acceptable:
* Soya.Common.typematch(unknown)
* Want [Number], [Boolean] or [Function]:
* Soya.Common.typematch(unknown, 2 + 4 + 16)
* Want [Number] only:
* Soya.Common.typematch(unknown, 2)
**/
typematch : function (vDunno, nCase){
var nMask;
switch(typeof(vDunno)){
case 'number' : nMask = 2; break;
case 'boolean' : nMask = 4; break;
case 'string' : nMask = 8; break;
case 'function': nMask = 16; break;
case 'object' : nMask = 32; break;
default : nMask = 1; break;
}
return Boolean(nMask & (nCase || 62));
},
getNode : function(data, tree){
var nc = 0;//nodeCount
//node = 1
if(data != null){
for(i=0;i<data.childNodes.length;i++){
if(data.childNodes[i].nodeType == 1){
if(nc == tree[0]){
data = data.childNodes[i];
if(tree.length > 1){
tree.shift();
data = this.getNode(data, tree);
}
return data;
}
nc++
}
}
}
return false;
},
toObject : function(data){
var ret, i;
switch(data.tagName){
case "string":
return (data.firstChild) ? new String(data.firstChild.nodeValue) : "";
break;
case "int":
case "i4":
case "double":
return (data.firstChild) ? new Number(data.firstChild.nodeValue) : 0;
break;
case "dateTime.iso8601":
/*
Have to read the spec to be able to completely
parse all the possibilities in iso8601
07-17-1998 14:08:55
19980717T14:08:55
*/
var sn = (isIE) ? "-" : "/";
if(/^(/d{4})(/d{2})(/d{2})T(/d{2}):(/d{2}):(/d{2})/.test(data.firstChild.nodeValue)){;//data.text)){
return new Date(RegExp.$2 + sn + RegExp.$3 + sn +
RegExp.$1 + " " + RegExp.$4 + ":" +
RegExp.$5 + ":" + RegExp.$6);
}
else{
return new Date();
}
break;
case "array":
data = this.getNode(data, [0]);
if(data && data.tagName == "data"){
ret = new Array();
var i = 0;
while(child = this.getNode(data, [i++])){
ret.push(this.toObject(child));
}
return ret;
}
else{
this.handleError(new Error("Malformed XMLRPC Message1"));
return false;
}
break;
case "struct":
ret = {};
var i = 0;
while(child = this.getNode(data, [i++])){
if(child.tagName == "member"){
ret[this.getNode(child, [0]).firstChild.nodeValue] = this.toObject(this.getNode(child, [1]));
}
else{
this.handleError(new Error("Malformed XMLRPC Message2"));
return false;
}
}
return ret;
break;
case "boolean":
return Boolean(isNaN(parseInt(data.firstChild.nodeValue)) ? (data.firstChild.nodeValue == "true") : parseInt(data.firstChild.nodeValue))
break;
case "base64":
return this.decodeBase64(data.firstChild.nodeValue);
break;
case "value":
child = this.getNode(data, [0]);
return (!child) ? ((data.firstChild) ? new String(data.firstChild.nodeValue) : "") : this.toObject(child);
break;
default:
this.handleError(new Error("Malformed XMLRPC Message: " + data.tagName));
return false;
break;
}
},
/*** Decode Base64 ******
* Original Idea & Code by thomas@saltstorm.net
* from Soya.Encode.Base64 [http://soya.saltstorm.net]
**/
decodeBase64 : function(sEncoded){
// Input must be dividable with 4.
if(!sEncoded || (sEncoded.length % 4) > 0)
return sEncoded;
/* Use NN's built-in base64 decoder if available.
This procedure is horribly slow running under NN4,
so the NN built-in equivalent comes in very handy. :) */
else if(typeof(atob) != 'undefined')
return atob(sEncoded);
var nBits, i, sDecoded = '';
var base64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
sEncoded = sEncoded.replace(//W|=/g, '');
for(i=0; i < sEncoded.length; i += 4){
nBits =
(base64.indexOf(sEncoded.charAt(i)) & 0xff) << 18 |
(base64.indexOf(sEncoded.charAt(i+1)) & 0xff) << 12 |
(base64.indexOf(sEncoded.charAt(i+2)) & 0xff) << 6 |
base64.indexOf(sEncoded.charAt(i+3)) & 0xff;
sDecoded += String.fromCharCode(
(nBits & 0xff0000) >> 16, (nBits & 0xff00) >> 8, nBits & 0xff);
}
// not sure if the following statement behaves as supposed under
// all circumstances, but tests up til now says it does.
return sDecoded.substring(0, sDecoded.length -
((sEncoded.charCodeAt(i - 2) == 61) ? 2 :
(sEncoded.charCodeAt(i - 1) == 61 ? 1 : 0)));
},
getObject : function(type, message){
if(type == "HTTP"){
if(isIE)
obj = new ActiveXObject("microsoft.XMLHTTP");
else if(isNS)
obj = new XMLHttpRequest();
}
else if(type == "XMLDOM"){
if(isIE){
obj = new ActiveXObject("microsoft.XMLDOM");
obj.loadXML(message)
}else if(isNS){
obj = new DOMParser();
obj = obj.parseFromString(message, "text/xml");
}
}
else{
this.handleError(new Error("Unknown Object"));
}
return obj;
},
validateMethodName : function(name){
/*do Checking:
The string may only contain identifier characters,
upper and lower-case A-Z, the numeric characters, 0-9,
underscore, dot, colon and slash.
*/
if(/^[A-Za-z0-9/._//:]+$/.test(name))
return true
else
this.handleError(new Error("Incorrect method name"));
},
getXML : function(obj){
if(typeof obj == "function"){
this.handleError(new Error("Cannot Parse functions"));
}else if(obj == null || obj == undefined || (typeof obj == "number" && !isFinite(obj)))
return false.toXMLRPC();
else
return obj.toXMLRPC();
},
handleError : function(e){
if(!this.onerror || !this.onerror(e)){
//alert("An error has occured: " + e.message);
throw e;
}
this.stop = true;
this.lastError = e;
},
cancel : function(id){
//You can only cancel a request when it was executed async (I think)
if(!this.queue[id]) return false;
this.queue[id][0].abort();
return true;
},
send : function(serverAddress, functionName, args, receive, multicall, autoroute){
var id, http;
//default is sync
this.validateMethodName();
if(this.stop){this.stop = false; return false;}
//setting up multicall
multicall = (multicall != null) ? multicall : this.multicall;
if(multicall){
if(!this.stack[serverAddress]) this.stack[serverAddress] = new Array();
this.stack[serverAddress].push({methodName : functionName, params : args});
return true;
}
//creating http object
var http = this.getObject("HTTP");
//setting some things for async/sync transfers
if(!receive || isNS){;
async = false;
}
else{
async = true;
/* The timer functionality is implemented instead of
the onreadystatechange event because somehow
the calling of this event crashed IE5.x
*/
id = this.queue.push([http, receive, null, new Date()])-1;
this.queue[id][2] = new Function("var id='" + id + "';var dt = new Date(new Date().getTime() - XMLRPC.queue[id][3].getTime());diff = parseInt(dt.getSeconds()*1000 + dt.getMilliseconds());if(diff > XMLRPC.timeout){if(XMLRPC.ontimeout) XMLRPC.ontimeout(); clearInterval(XMLRPC.timers[id]);XMLRPC.cancel(id);return};if(XMLRPC.queue[id][0].readyState == 4){XMLRPC.queue[id][0].onreadystatechange = function(){};XMLRPC.receive(id);clearInterval(XMLRPC.timers[id])}");
this.timers[id] = setInterval("XMLRPC.queue[" + id + "][2]()", 20);
}
//setting up the routing
autoroute = (autoroute || this.autoroute);
//'active' is only set when direct sending the message has failed
var srv = (autoroute == "active") ? this.routeServer : serverAddress;
try{
http.open('POST', srv, async);
http.setRequestHeader("User-Agent", "vcXMLRPC v0.91 (" + navigator.userAgent + ")");
http.setRequestHeader("Host", srv.replace(/^https?://{2}([:/[/]/-/w/.]+)//?.*/, '$1'));
http.setRequestHeader("Content-type", "text/xml");
if(autoroute == "active"){
http.setRequestHeader("X-Proxy-Request", serverAddress);
http.setRequestHeader("X-Compress-Response", "gzip");
}
}
catch(e){
if(autoroute == true){
//Access has been denied, Routing call.
autoroute = "active";
if(id){
delete this.queue[id];
clearInterval(this.timers[id]);
}
return this.send(serverAddress, functionName, args, receive, multicall, autoroute);
}
//Routing didn't work either..Throwing error
this.handleError(new Error("Could not sent XMLRPC Message (Reason: Access Denied on client)"));
if(this.stop){this.stop = false;return false}
}
//Construct the message
var message = '<?xml version="1.0"?><methodCall><methodName>' + functionName + '</methodName><params>';
for(i=0;i<args.length;i++){
message += '<param><value>' + this.getXML(args[i]) + '</value></param>';
}
message += '</params></methodCall>';
var xmldom = this.getObject('XMLDOM', message);
if(self.DEBUG) alert(message);
try{
//send message
http.send(xmldom);
}
catch(e){
//Most likely the message timed out(what happend to your internet connection?)
this.handleError(new Error("XMLRPC Message not Sent(Reason: " + e.message + ")"));
if(this.stop){this.stop = false;return false}
}
if(!async && receive)
return [autoroute, receive(this.processResult(http))];
else if(receive)
return [autoroute, id];
else
return [autoroute, this.processResult(http)];
},
receive : function(id){
//Function for handling async transfers..
if(this.queue[id]){
var data = this.processResult(this.queue[id][0]);
this.queue[id][1](data);
delete this.queue[id];
}
else{
this.handleError(new Error("Error while processing queue"));
}
},
processResult : function(http){
if(self.DEBUG) alert(http.responseText);
if(http.status == 200){
//getIncoming message
dom = http.responseXML;
if(dom){
var rpcErr, main;
//Check for XMLRPC Errors
rpcErr = dom.getElementsByTagName("fault");
if(rpcErr.length > 0){
rpcErr = this.toObject(rpcErr[0].firstChild);
this.handleError(new Error(rpcErr.faultCode, rpcErr.faultString));
return false
}
//handle method result
main = dom.getElementsByTagName("param");
if(main.length == 0) this.handleError(new Error("Malformed XMLRPC Message"));
data = this.toObject(this.getNode(main[0], [0]));
//handle receiving
if(this.onreceive) this.onreceive(data);
return data;
}
else{
this.handleError(new Error("Malformed XMLRPC Message"));
}
}
else{
this.handleError(new Error("HTTP Exception: (" + http.status + ") " + http.statusText + "/n/n" + http.responseText));
}
}
}
//Smell something
ver = navigator.appVersion;
app = navigator.appName;
isNS = Boolean(navigator.productSub)
//moz_can_do_http = (parseInt(navigator.productSub) >= 20010308)
isIE = (ver.indexOf("MSIE 5") != -1 || ver.indexOf("MSIE 6") != -1) ? 1 : 0;
isIE55 = (ver.indexOf("MSIE 5.5") != -1) ? 1 : 0;
isOTHER = (!isNS && !isIE) ? 1 : 0;
if(isOTHER) alert("Sorry your browser doesn't support the features of vcXMLRPC");
***********************************************************
写这个东东,就是刚调试各个部件之间的组合时,比较费力,弄顺了,用起来还是很方便的,注册好函数后,在客户端和服务器端的方法调用形式可以一模一样。
配合一些JS应用技巧和eval 函数,可以完成许多比较实用的功能。如根据拼音码,无刷新模糊选出一系列的的名称等
支持环境...Apache Tomcat 5.5 + Apache XML 2.0 + Apache Commons Codec,几个jar放到web应用的lib目录中
本来想尝试 javascript 实现SOAP client的,可是查了一下,IE和FireFox的实现几乎完全不同,而且,不同的Soap还有细微差异,相比之下,XML-RPC 还是好一点...
上面的东东基本上都是利用网上的资料,Apache SOAP 的实例... java实现escape功能等文章。