Asynchronous Call

kirsch59

Member
I have a CGI program that calls a web service. I would like to call the CGI program asynchronously or at a minimum run the web service asynchronously. Is this possible? I'm running Webspeed 10.1.C on Windows Server 2003.
 

RealHeavyDude

Well-Known Member
Do you want to call the CGI program from the WebSpeed agent? Wouldn't it be easier to call the web service directly from the ABL thus bypassing the CGI script and eliminating the asynchronous issue?

Heavy Regards, RealHeavyDude.
 

kirsch59

Member
The CGI program processes an end-user's input from a web form. The web service is called using data from the web form. I would like to display the new web page (&out) before making the web service transaction. The response from the web service is not needed to display the new web page to the end-user.
 

rstanciu

Member
here is an sample code ... who call direct the web-service asyncron

Code:
/* client_validateCustomerAsync.p */
/*******************************************************************/
DEFINE VARIABLE hWebService AS HANDLE    NO-UNDO.
DEFINE VARIABLE hPortType   AS HANDLE    NO-UNDO.
DEFINE VARIABLE cResponse   AS CHARACTER NO-UNDO.
DEFINE VARIABLE li          AS INTEGER   NO-UNDO.
DEFINE VARIABLE retValue    AS CHARACTER INITIAL "OK" NO-UNDO.
DEFINE VARIABLE err         AS LOGICAL   NO-UNDO.
DEFINE VARIABLE result      AS CHARACTER NO-UNDO.
DEFINE VARIABLE har1        AS HANDLE     EXTENT 100 NO-UNDO.
DEFINE VARIABLE iCustNums   AS INTEGER    EXTENT 100 NO-UNDO.
DEFINE VARIABLE cCustNames  AS CHARACTER  EXTENT 100 NO-UNDO.
DEFINE VARIABLE j AS INTEGER NO-UNDO.
DEFINE VARIABLE iMinCust    AS INTEGER INITIAL 80 NO-UNDO.
DEFINE VARIABLE iMaxCust    AS INTEGER INITIAL 90 NO-UNDO.
/*******************************************************************/
CREATE SERVER hWebService.
ETIME(true).
li = MTIME.
hWebService:CONNECT("-WSDL 'http://rs:25011/wsa/wsa1/wsdl?targetURI=urn:prgs:DocLiteral'
  -Service WSTKDocLiteralService
  -Port WSTKDocLiteralObj").

IF  NOT hWebService:CONNECTED() THEN DO:
    MESSAGE "SERVER NOT CONNECTED" VIEW-AS ALERT-BOX.
    retValue = "ERROR".
END.


RUN WSTKDocLiteralObj SET hPortType ON SERVER hWebService NO-ERROR.

IF ERROR-STATUS:ERROR THEN DO:
    MESSAGE "Failed to create hPortType" VIEW-AS ALERT-BOX.
    retValue = "ERROR".
END.


DO li = iMinCust TO iMaxCust:
  /*the result parameter is mapped on RETURN-VALUE*/
  RUN getCustomerName IN hPortType ASYNCHRONOUS SET har1[li]
  EVENT-PROCEDURE "ValidateCustomerResponseHandler" (INPUT li,
  OUTPUT result, OUTPUT cResponse) NO-ERROR.
  RUN ErrorInfo (OUTPUT err).
  iCustNums[li] = li.
  PROCESS EVENTS.
END.


/*memory cleanup*/
DO WHILE (hWebService:ASYNC-REQUEST-COUNT > 0):
  PROCESS EVENTS.
  PAUSE 5.
  MESSAGE
    "ASYNC-REQUEST-COUNT: "  hWebService:ASYNC-REQUEST-COUNT
   VIEW-AS ALERT-BOX.
END.

/*disp result*/
DO j = iMinCust TO iMaxCust:
    MESSAGE iCustNums[j] " = "  cCustNames[j]
      VIEW-AS ALERT-BOX.
END.

DELETE PROCEDURE hPortType.
hWebService:DISCONNECT().
DELETE OBJECT hWebService.

RETURN retValue.
/*******************************************************************/
PROCEDURE ValidateCustomerResponseHandler:
DEFINE INPUT PARAMETER result    AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER cCustName AS CHARACTER NO-UNDO.

DO j = iMinCust TO iMaxCust:
  IF SELF = har1[j] THEN DO:
    IF cCustName = ? THEN cCustNames[j] = "not found".
    ELSE cCustNames[j] = cCustName.
    LEAVE.
  END.
END.
END.
/*******************************************************************/
PROCEDURE ErrorInfo: /*1*/
  DEFINE OUTPUT PARAMETER errorfound AS LOGICAL INITIAL FALSE.
  DEFINE VARIABLE i                  AS INTEGER NO-UNDO.
  DEFINE VARIABLE hSOAPFault         AS HANDLE NO-UNDO.
  DEFINE VARIABLE hSOAPFaultDetail   AS HANDLE NO-UNDO.
  DEFINE VARIABLE HeaderXML AS LONGCHAR VIEW-AS EDITOR SIZE 70 BY 15 LARGE.
  IF ERROR-STATUS:NUM-MESSAGES > 0 THEN DO:
     errorfound = TRUE.
     DO i = 1 TO ERROR-STATUS:NUM-MESSAGES:
        MESSAGE ERROR-STATUS:GET-MESSAGE(i) VIEW-AS ALERT-BOX.
     END.
/*2*/
     IF VALID-HANDLE(ERROR-STATUS:ERROR-OBJECT-DETAIL) THEN DO:
       hSOAPFault = ERROR-STATUS:ERROR-OBJECT-DETAIL.
       MESSAGE
       "Fault Code: "   hSOAPFault:SOAP-FAULT-CODE        SKIP
       "Fault String: " hSOAPFault:SOAP-FAULT-STRING      SKIP
       "Fault Actor: "  hSOAPFault:SOAP-FAULT-ACTOR       SKIP
       "Error Type: "   hSOAPFault:TYPE  VIEW-AS ALERT-BOX.
/*3*/
       IF VALID-HANDLE(hSOAPFault:SOAP-FAULT-DETAIL) THEN  DO:
            hSOAPFaultDetail = hSOAPFault:SOAP-FAULT-DETAIL.
            MESSAGE  "Error Type: " hSOAPFaultDetail:TYPE
                     VIEW-AS ALERT-BOX.
            HeaderXML = hSOAPFaultDetail:GET-SERIALIZED().
            DISPLAY HeaderXML LABEL "Serialized SOAP fault detail"
                    WITH FRAME a.
        END.
     END.
  END.
END PROCEDURE.
/*******************************************************************/
 

kirsch59

Member
Great. Thanks for the sample code for calling a web service asynch.

Is there any way to call the program that calls the web service asynch? I would like to return the web page before I start formatting the XML.
 

RealHeavyDude

Well-Known Member
What should be the response to the web service call?

The WebSpeed agent is a single-threaded process which means as soon as you return the web page the processing of the request on the agent is completed. AFAIK there is no way that you process any logic after that on the original request's behalf. I think you need call the web service independently from the processing of the web request from the user. You could, for example, write something into the database that is picked up later by another background process which check a database table periodically for new entries. But you need to be aware, if the web service call fails for any reason, the original transaction has been rolled back or committed as soon as you've returned the web page to the user. Therefore you would need to implement your own rollback logic if something like that is needed.

Heavy Regards, RealHeavyDude.
 

lee.bourne

Member
What about using an AppServer to do the async bit? Your webspeed can run the code below, the appserver can run webservice call.

DEF VAR hAppServer AS HANDLE NO-UNDO.
DEF VAR hProc AS HANDLE NO-UNDO.


CREATE SERVER hAppServer.
hAppServer:CONNECT("-H localhost") NO-ERROR. /* Assumes an appserver supporting the default service */
RUN yourprocedure.p ON SERVER hAppServer ASYNCHRONOUS SET hProc (INPUT yourparameters).
 

Cecil

19+ years progress programming and still learning.
What about using an AppServer to do the async bit? Your webspeed can run the code below, the appserver can run webservice call.

DEF VAR hAppServer AS HANDLE NO-UNDO.
DEF VAR hProc AS HANDLE NO-UNDO.


CREATE SERVER hAppServer.
hAppServer:CONNECT("-H localhost") NO-ERROR. /* Assumes an appserver supporting the default service */
RUN yourprocedure.p ON SERVER hAppServer ASYNCHRONOUS SET hProc (INPUT yourparameters).

Does this actual work or is it just theory? I keep thinking it will cause resource issues over time as none of the handle(s) will get not get tidy up (unless they were stored somewhere and clean up later)???
 

lee.bourne

Member
Does this actual work or is it just theory? I keep thinking it will cause resource issues over time as none of the handle(s) will get not get tidy up (unless they were stored somewhere and clean up later)???

It definitely works. If you use the QUIT command at the end of the main block in your appserver procedure you should be fine. However, if you want the belt and braces approach then you can add this into your webspeed code which should help clean things up too.

Code:
DEFINE VARIABLE vhCurServer    AS HANDLE NO-UNDO.      
    vhCurServer = SESSION:FIRST-SERVER.
    
    /*Clean up any async procedures running on the app server 
      NB: only if not open server sockets*/
    IF NOT VALID-HANDLE(SESSION:FIRST-SERVER-SOCKET) THEN
    DO WHILE VALID-HANDLE( vhCurServer ) :
         IF vhCurServer:ASYNC-REQUEST-COUNT = 0 THEN
        DO:
            IF vhCurServer:CONNECTED() THEN
                vhCurServer:DISCONNECT().
             
            DELETE OBJECT vhCurServer.
            vhCurServer = SESSION:FIRST-SERVER.
         END.
        ELSE
            vhCurServer = vhCurServer:NEXT-SIBLING.
    END.
 
Top