Paradox Community
Search:

 Welcome |  What is Paradox |  Paradox Folk |  Paradox Solutions |
 Interactive Paradox |  Paradox Programming |  Internet/Intranet Development |
 Support Options |  Classified Ads |  Wish List |  Submissions 


Paradox OCX Internet/Intranet Articles  |  Non-OCX Internet/Intranet Articles  


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=dyIncoming["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

Inspect server.lsl for how the handlerequest method connects an internal dynArray to the incoming request data, then uses execMethod() to process the incoming request without using a switch..case..endSwitch construct. The execMethod() is based on 'strHandler', which is the ACTION set in the html file (or, rather, the handler identified in server.db).

Again, I am not suggesting that this alternate is better than the version that comes with Paradox. I use that version myself. However, I will be experimenting with this to determine any potential speed enhancements (or decreases). And it does simplify your coding of this. The ACTION in the form (or one you change it to in server.db) becomes the method to execute automatically, removing the need for those switch...case...endSwitch statements.


Discussion of this article


 Feedback |  Paradox Day |  Who Uses Paradox |  I Use Paradox |  Downloads 


 The information provided on this Web site is not in any way sponsored or endorsed by Corel Corporation.
 Paradox is a registered trademark of Corel Corporation.


 Modified: 15 May 2003
 Terms of Use / Legal Disclaimer


 Copyright © 2001- 2003 Paradox Community. All rights reserved. 
 Company and product names are trademarks or registered trademarks of their respective companies. 
 Authors hold the copyrights to their own works. Please contact the author of any article for details.