Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I call a web service from iSeries COBOL?

We need to be able to call an internal web service from COBOL code running on an iSeries LPAR (V6R1). I worked through the one complete example that I was able to find online here. So, my next step was to try to repeat the process and call one of our existing web services.

I used the WSDL2WS command from QSH to generate the C client stubs. I modified the example COBOL client program and attempted a call to my web service. The problem that I'm running into seems to be related to the fact that the example C client methods are returning pointers and the COBOL code is assigning them to pointers. I suspect that an error in my COBOL code is the root of my problem, because the C method that that WSDL2WS created for my web service method returns a simple xsdc_string not a pointer to a custom result type:

xsdc__string TestUnsuccessfulMessage(AXISCHANDLE stub)
{
    AXISCHANDLE call = axiscStubGetCall(stub);
    xsdc__string Ret = NULL;

    axiscCallSetSoapFaultNamespace(call, "http://myserver/PSItemMaintenance/ItemMaintenanceService.svc");


    // ======================================================================
    // Initialize client engine, set SOAP version, SOAPAction, operation, etc.
    // ======================================================================

    if (AXISC_SUCCESS != axiscCallInitialize(call, C_DOC_PROVIDER ))
        return Ret;

    if (NULL==axiscCallGetTransportProperty(call,"SOAPAction",0))
        axiscCallSetTransportProperty(call,AXISC_SOAPACTION_HEADER , "http://tempuri.org/IItemMaintenanceService/TestUnsuccessfulMessage");

    axiscCallSetSOAPVersion(call, SOAP_VER_1_1);
    axiscCallSetOperation(call, "TestUnsuccessfulMessage", "http://tempuri.org/");

    // ======================================================================
    // Apply SSL configuration properties and user-set SOAP headers.
    // ======================================================================

    axiscStubIncludeSecure(stub);
    axiscStubApplyUserPreferences(stub);


    // ======================================================================
    // Invoke web service, send/receive operation. Handle output parameters, if any.
    // ======================================================================

    if (AXISC_SUCCESS == axiscCallSendAndReceive(call))
    {
        if(AXISC_SUCCESS == axiscCallValidateMessage(call, "TestUnsuccessfulMessageResponse", "http://tempuri.org/", true_))
        {
            Ret = axiscCallGetElementAsString(call, "TestUnsuccessfulMessageResult", 0);
        }

        axiscStubCheckForExtraneousElements(stub);
    }
    axiscCallUnInitialize(call);
    return Ret;
}

I can setup the web service in debug mode and can see the call from the iSeries when I run the program that calls the COBOL and C modules. I can also see that I'm returning back a simple string value.

The problem comes when the COBOL program tries to take the return value and use it. I believe that the relevant bits of the COBOL code are:

LINKAGE PROCEDURE FOR "TestSuccessfulMessage"
                 USING ALL DESCRIBED    

LINKAGE SECTION.                     
 01 LookupResult         PIC X(1000).  

CALL PROCEDURE "TestSuccessfulMessage"
 USING BY VALUE STUB              
 RETURNING LookupResult.          

I get a MCH3601 when it's coded this way. If I return into a pointer and then set the address to LookupResult, I end up with a null value.

I hope that I'm just missing a small detail somewhere. I have very little COBOL experience. I'm just trying to create a reference app as a proof of concept for another team within our company. Any help, or suggestions of what to try would be appreciated. I can supply more code.

Update: I tried just moving the LookupResult declaration into working storage. While that eliminated the MCH3601 error, I just got a bunch of garbage data in my display. I can see pieces of info about my iSeries session (i.e. deviceID, etc...) within the data.

I also tried leaving the LookupResult in the linkage section and creating a pointer in working storage. Then I added a "SET Address of LookupResult TO ResultPointer". Again, the call ended without error, but when I display LookupResult I get garbage data. However, it's different than the data that comes back if I return straight into LookupResult. I can see pieces of the SOAP envelope in this data.

Final: All of the changes that I had to make were in the COBOL code. Here are the relevant pieces:

WORKING-STORAGE SECTION.              
01 Endpoint             PIC X(100).   
01 STUB                 USAGE POINTER.
01 ResultPointer        USAGE POINTER.

LINKAGE SECTION.                      
01 pszEndpoint          PIC X(100).   
01 LookupResult         PIC X(7).   

CALL PROCEDURE "TestSuccessfulMessage"       
     USING BY VALUE STUB                     
     RETURNING INTO ResultPointer.           

SET Address of LookupResult TO ResultPointer.
like image 959
Paul G Avatar asked Oct 09 '22 04:10

Paul G


1 Answers

If I return into a pointer and then set the address to LookupResult, I end up with a null value.

I am a COBOL person, but not on iSeries. Caveat emptor.

If xsdc__string resolves to a pointer, then your COBOL code should have a variable of type POINTER for the RETURNING portion of the CALL. Perhaps the C function is in fact returning NULL, the code certainly allows for it - maybe axiscCallInitialize isn't returning AXISC_SUCCESS.

At least on z/OS, you would want the COBOL pointer variable to be in either Working-Storage or Local Storage, and then you would SET ADDRESS OF LookupResult TO that pointer variable. I'm presuming that doesn't change just because you're on a different machine architecture.

...FWIW

like image 196
cschneid Avatar answered Oct 13 '22 11:10

cschneid