Monday, November 08, 2010 at 12:02 AM.
system.verbs.builtins.betty.rpc.client
on client (rpcServer="localhost", rpcPort=user.inetd.config.http.port, procedureName="", adrparamlist=nil, fldebug=false, ticksToTimeOut=nil, flShowMessages=true, rpcPath=nil, flAsynch=false, adrCallback=nil, extraInfo=nil, adrErrorCallback=nil, username="", password="") { <<Change <<8/20/02; 4:43:35 AM by JES <<Respect user.betty.prefs.flSaveDatabaseAfterAsynchRpcCall. <<1/4/02; 7:15:55 PM by DW <<If user.betty.prefs.flKeepClientCallTicks is true, keep track of call stats in system.temp.betty.clientCallTicks. <<Added a test code bundle at the end of the routine. <<8/20/00 by AR <<Added optional username and password parameters for HTTP Basic Authentication. <<10/8/99 by DW <<Added optional adrErrorCallback parameter, if specified, this script is called when there's an error sending an asynchronous XML-RPC call. <<3/6/99 by DW <<Added three optional parameters to allow asynchronous RPC-ing that try until they connect. <<flAsynch, defaults to false, if true it's an asynchronous call. <<adrCallback, defaults to nil, if non-nil, it's the routine we call when we connect. <<It takes a single parameter, the value returned by the RPC server. <<The routine pointed to by this address must not be local to the script calling betty.rpc.client because it won't be around when the actual RPC call is made. <<It must be in Frontier.root or in a guest database. <<extraInfo, defaults to nil, it's any kind of object, it's available to the callback to help it interpret the response. <<It will create a table at user.betty.queueOutgoing, which is watched by the agent. <<1/17/99 by DW <<Change default value of ticksToTimeOut to nil. If it's nil, set it to user.betty.prefs.rpcClientDefaultTimeout. <<Change default value of rpcPath to nil. If it's nil, set it to user.betty.prefs.rpcClientDefaultPath. <<1/15/99 by DW <<added optional parameter, rpcPath, to facilitate communication with non-Frontier XML-RPC servers. <<11/14/98 by DW <<In the 11/5 change, RPC error reporting lost a lot of its value due to a too-local declaration of adrtable. <<11/5/98 by DW <<At the end of betty.rpc.client, there was some real ancient code for producing examples <<It was copying the XML request and the compiled XML that was returned into the params list <<However, it's legal to call this routine without specifying the param list, it would fail if you did so. <<This code was only useful to people writing docs and testing this. <<Now it's deployed, things will run faster if we don't do this. <<And now you can leave out params if the procedure you're calling takes no parameters. <<11/1/98 by DW <<Added ticksToTimeOut optional parameter, it's passed on to tcp.httpClient <<Some operations take more than 30 seconds to complete, that's the default on tcp.httpClient <<I imagine that some take much less than 30 seconds too... <<Added flShowMessages optional parameter, it's also passed on to tcp.httpClient <<If true, it will display messages in Frontier's About window, if false it won't. <<Previously, there was no way for an RPC caller to turn on tcp.httpClient messages. <<This is important if you want to see what's going on at the HTTP level. <<7/17/98 by PBS <<XML header is now lowercase to conform to spec. <<Changed xml table from temp.rpcReturn to a local table for thread-safety. <<4/4/98 by DW <<A full-featured client for the RPC2 responder local (startticks = clock.ticks ()); betty.init (); //1/17/99 DW, make sure user.betty.prefs is set up bundle { //3/6/99 DW, handle asynchronous calls if flAsynch { local (adragent = @system.agents.asynchRPC); if not defined (adragent^) { new (scripttype, adragent); local (oldtarget = target.set (adragent)); op.setlinetext ("betty.rpc.agent ()"); target.set (oldtarget); script.compile (adragent)}; local (adrqueue = @user.betty.queueOutgoing); if not defined (adrqueue^) { new (tabletype, adrqueue)}; if not defined (adrqueue^.serialNum) { adrqueue^.serialNum = 1}; local (adrtable = @adrqueue^.table); if not defined (adrtable^) { new (tabletype, adrtable)}; local (adritem = @adrtable^.[string.padwithzeros (adrqueue^.serialNum++, 7)]); bundle { //populate it in a local table, don't want the agent catching it until we're ready local (localtable); new (tabletype, @localtable); localtable.rpcServer = rpcServer; localtable.rpcPort = rpcPort; localtable.procedureName = procedureName; localtable.paramlist = adrparamlist^; //have to copy the param list localtable.fldebug = fldebug; localtable.flShowMessages = flShowMessages; localtable.rpcPath = rpcPath; localtable.adrCallback = adrCallback; localtable.adrErrorCallback = adrErrorCallback; localtable.extraInfo = extraInfo; localtable.readyToRunAt = clock.now (); localtable.username = username; localtable.password = password; adritem^ = localtable}; if user.betty.prefs.flSaveDatabaseAfterAsynchRpcCall { fileMenu.save ()}; return (true)}}; bundle { //1/17/99 DW, check ticksToTimeOut, rpcPath if ticksToTimeOut == nil { ticksToTimeOut = user.betty.prefs.rpcClientDefaultTimeout}; if rpcPath == nil { rpcPath = user.betty.prefs.rpcClientDefaultPath}}; local (xmltext = ""); bundle { //build the XML request local (indentlevel = 0); on add (s) { xmltext = xmltext + string.filledString ("\t", indentlevel) + s + "\r\n"}; add ("<?xml version=\"1.0\"?>"); add ("<methodCall>"); indentlevel++; add ("<methodName>" + procedureName + "</methodName>"); add ("<params>"); indentlevel++; if adrparamlist != nil { local (item); for item in adrparamlist^ { add ("<param>"); indentlevel++; <<add ("<name>" + nameOf (adritem^) + "</name>") add ("<value>" + xml.coercions.frontierValueToTaggedText (@item, indentlevel) + "</value>"); add ("</param>"); indentlevel--; if typeOf (item) == tableType { delete (@item)}}}; add ("</params>"); indentlevel--; add ("</methodCall>"); indentlevel--}; local (xtable); bundle { //send the HTTP request, store result in xtable local (s); s = tcp.httpClient (method:"POST", server:rpcServer, port:rpcPort, path:rpcPath, data:xmltext, datatype:"text/xml", username:username, password:password, debug:fldebug, timeOutTicks:ticksToTimeOut, flMessages:flShowMessages); if fldebug { edit (@scratchpad.httpResult); //the result of setting debug to true in the call above edit (@scratchpad.httpCommand)}; xml.compile (string.delete (s, 1, string.patternMatch ("\r\n\r\n", s) + 3), @xtable)}; <<wp.newtextobject (s, @scratchpad.rpcresponse) //2/7/08; 12:32:34 PM by DW <<wp.newtextobject (xmltext, @scratchpad.rpccall) //2/7/08; 12:32:34 PM by DW local (returnedValue, adrtable); try { //walk the response structure, get returnedValue adrtable = @xtable; local (adrresponse = xml.getaddress (adrtable, "methodResponse")); local (adrparams = xml.getaddress (adrresponse, "params")); local (adrparam = xml.getaddress (adrparams, "param")); returnedValue = xml.getvalue (adrparam, "value"); if typeOf (returnedValue) == tableType { //4/16/98; 1:51:28 PM by DW local (newValue); xml.coercions.structToFrontierValue (@returnedValue [1], @newValue); table.assign (@returnedValue, newValue)}} else { //scriptError local (adrresponse = xml.getaddress (adrtable, "methodResponse")); local (adrfault = xml.getaddress (adrresponse, "fault")); local (adrvalue = xml.getaddress (adrfault, "value")); local (adrstruct = xml.getaddress (adrvalue, "struct")); local (memberlist = xml.getaddresslist (adrstruct, "member")); local (member, name, value, faultCode, faultString); for member in memberlist { name = xml.getvalue (member, "name"); value = xml.getvalue (member, "value"); case name { "faultCode" { faultCode = value}; "faultString" { faultString = value}}}; scriptError ("The server, " + rpcServer + ", returned error code " + faultCode + ": " + faultString)}; bundle { //track ticks, by procedure call in system.temp.betty.clientCallTicks if user.betty.prefs.flKeepClientCallTicks { local (adrtable = @system.temp.betty); if not defined (adrtable^) { new (tabletype, adrtable)}; adrtable = @adrtable^.clientCallTicks; if not defined (adrtable^) { new (tabletype, adrtable)}; local (adrcount = @adrtable^.[rpcServer + ":" + rpcPort + rpcPath + "/" + procedureName]); if not defined (adrcount^) { adrcount^ = 0}; adrcount^ = adrcount^ + (clock.ticks () - startticks)}}; return (returnedValue)} <<bundle //test code <<local (params = {"Dave's Handsome Radio Blog", "http://radio.weblogs.com/0001015/"}) <<scratchpad.response = betty.rpc.client ("rpc.weblogs.com", 80, "weblogUpdates.ping", @params)
This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.