Alternate to Handlerequest Methodology as Presented in the Hercules Example
© 2002 Tony McGuire
Download sample files demonstrating the methodology presented below.
The Hercules example/model is wonderful. It gives us incredible flexibility to handle incoming requests. And using server.db allows us to automatically open/reference the correct library to handle the incoming request, through the 'handlerequest' method. My only issue has been all those case... statements. Likely, though, if you have that many case statements you need to create a new library for handling additional methods. However, this just bugged me. So I went looking for an alternative. Thus the recent thread on the pnews news server on execMethod().
In a library, you cannot use execMethod() to execute an internal method, unless the construct that has the library open (i.e., a form or script) 'knows about' that method. This would normally be done in that script/form's Uses clause. However, Tom Krieg discovered that the library can open a copy of itself, and that copy can be used by the one opening it; at this point, execMethod() works just fine.
So, I went looking for a way to put this to use: the idea was to simplify the methodology presented in the Hercules example further. While initially complicating understanding of how it all works, perhaps, the below removes all necessity for a switch...case...endSwitch construct in your 'server' library.
In server.lsl, we set some global variables.
jsLib
is the variable to be used to open a copy of server.lsl so we can use execMethod.loLib
is the variable to use so we don't keep opening multiple copies of the library.loTrueFalse
is set by the handler method executed by execMethod() and specifies whether that method succeeded. It is later checked to determine whether we should return the results of said method or return an error page.dyIncoming
will contain the data in the incoming web form.stReturnValue
will hold our response.stPath
is used to locate the server.lsl so the library can open a copy of itself.arVersion
is here to demonstrate opening the correct version of a delivered library based on the version of Paradox being used.var jsLib library loLib logical loTrueFalse logical dyIncoming dynarray[] string stReturnValue string stPath string arVersion array[] string endVar method open(var eventInfo Event) var arPath array[] string endvar loLib=False ; break apart the version of Paradox ; so the correct version of the JSI Library ; will be opened. This way one copy of this ; client library can be created, and distributed ; to multiple client versions of Paradox without special ; recoding breakapart(version(),arVersion,".") ; get the path to this library, and use that path to ; reference a copy of server.lsl library breakapart(getfilename(),arPath,"\\") stPath=arPath[1] if arPath.size()>=3 then for x from 2 to arPath.size()-1 stPath=stPath+"\\"+arPath[x] endfor endif endMethod method jsParseRequest(var olRequest oleAuto, var olResponse oleAuto) logical var liOne, liTwo longint endVar ; retrieve the incoming field data into a Global dynarray ; so it is available to all methods/procs in this library ; and thus also available in the new copy of the library liTwo = olRequest.NFields - 1 if liTwo<0 then dyIncoming.empty() dyIncoming["ErrorMessage"]="Error: No data received" return false endif for liOne from 0 to liTwo dyIncoming[olRequest.GetFieldName(liOne)]=olRequest.GetFieldByIndex(liOne) endFor ; add some incoming request variables to the global array dyIncoming["IpAddress"]=olRequest.ipAddress dyIncoming["URI"]=olRequest.uri return True endMethod ; replacement for handlerequest; this version uses the new methodology method handlerequest(strHandler String, var Request OleAuto, var Response OleAuto) Logical ; these vars are for handling errors in the try...onFail...endTry block below var siErrors, liErrors longint arErrors array[30] string stClock string endvar ; create a global dynarray from the incoming data, ; which can be used by both the original copy of the library ; and the jsLib copy of the library ; There must be a connection established to new global ; variables, since there can be no parameters passed with execMethod() if not jsParseRequest(request,response) then response.resultstring=dyIncoming["ErrorMessage"] return True endif ; if a copy of server.lsl isn't open, open it with jsLib variable if not loLib then jsLib.open(stPath+"\\Server") loLib=True endif ; set default for 'errormessage' element ; It can then just be added to, rather than ; figure out later whether it has been initialized dyIncoming["ErrorMessage"]="" ; run execMethod based on the incoming 'handler' identified in server.db ; you *could* bypass server.db at this point. But it is just too handy and flexible to ; abandon. Especially with proxying, where you can watch for /paradox/ACTION and pass ; ACTION here as the execMethod parameter. try jsLib.execmethod(strHandler) if loTrueFalse then response.resultstring=stReturnValue else response.resultstring=dyIncoming["ErrorMessage"] endif onFail arErrors[1]=errormessage() liErrors=1 while errorpop() liErrors=liErrors+1 arErrors[liErrors]=errormessage() endwhile stClock=string(cpuclocktime()) for siErrors from 1 to liErrors if arErrors[siErrors].search(chr(10))=arErrors[siErrors].size() then arErrors[siErrors]=arErrors[siErrors].substr(1,arErrors[siErrors].size()-1) endif if arErrors[siErrors].search(chr(13))=arErrors[siErrors].size() then arErrors[siErrors]=arErrors[siErrors].substr(1,arErrors[siErrors].size()-1) endif ; you can build on 'ErrorMessage' element since you create default blank value near top ; of this method dyIncoming["ErrorMessage"]=dyIncoming["ErrorMessage"]+arErrors[siErrors]+"\n" writeprofilestring(stPath+"\\errors.ini", stClock, string(siErrors), arErrors[siErrors]) endfor errorclear() sleep(1) response.resultstring=dyIncoming["ErrorMessage"] endTry ; empty the incoming dynarray so any ; values set during this operation won't also be in the next request dyIncoming.empty() return True endMethod ; getresponse is the 'action' in the html form included in this sample. ; it is as simplistic as it gets, and is included solely so that you can see that ; execMethod(strHandler) works. strHandler is the incoming 'Action' (getresponse), ; and therefore is the method that automatically executes by calling execMethod(strHandler) ; in handlerequest, above. method getresponse() ; a VERY simplistic use ; but shows that the connection has been ; initialize between jsRequest/jsResponse and ; the incoming request from the outside stReturnValue=jsRequest.ipaddress ; all your normal processing of this request ; would take place here ; set a global variable that is a flag indicating whether this operation was successful loTrueFalse=True { ; if your operation fails, you could use dyIncoming["ErrorMessage"]="Message for user to see when operation fails" loTrueFalse=False } endMethod