Making an Asynchronous Callout from an Imported WSDL

In addition to HttpRequest-based callouts, asynchronous callouts are supported in Web service calls that are made from WSDL-generated classes. The process of making asynchronous callouts from a WSDL-generated class is similar to the process for using the HttpRequest class.

When you import a WSDL in Salesforce, Salesforce autogenerates two Apex classes for each namespace in the imported WSDL. One class is the service class for the synchronous service, and the other is a modified version for the asynchronous service. The autogenerated asynchronous class name starts with the Async prefix and has the format AsyncServiceName. ServiceName is the name of the original unmodified service class. The asynchronous class differs from the standard class in the following ways.

  • The public service methods contain an additional Continuation parameter as the first parameter.
  • The Web service operations are invoked asynchronously and their responses are obtained with the getValue method of the response element.
  • The WebServiceCallout.beginInvoke and WebServiceCallout.endInvoke are used to invoke the service and get the response respectively.

You can generate Apex classes from a WSDL in the Salesforce user interface. From Setup, enter Apex Classes in the Quick Find box, then select Apex Classes.

To make asynchronous Web service callouts, call the methods on the autogenerated asynchronous class by passing your Continuation instance to these methods. The following example is based on a hypothetical stock-quote service. This example assumes that the organization has a class, called AsyncSOAPStockQuoteService, that was autogenerated via a WSDL import. The example shows how to make an asynchronous callout to the service by using the autogenerated AsyncSOAPStockQuoteService class. First, this example creates a continuation with a 60-second timeout and sets the callback method. Next, the code example invokes the beginStockQuote method by passing it the Continuation instance. The beginStockQuote method call corresponds to an asynchronous callout execution.

public Continuation startRequest() {
   Integer TIMEOUT_INT_SECS = 60;  
   Continuation cont = new Continuation(TIMEOUT_INT_SECS);
   cont.continuationMethod = 'processResponse';
   
   AsyncSOAPStockQuoteService.AsyncStockQuoteServiceSoap 
      stockQuoteService = 
        new AsyncSOAPStockQuoteService.AsyncStockQuoteServiceSoap();
   stockQuoteFuture = stockQuoteService.beginStockQuote(cont,'CRM');    

   return cont;   
}

When the external service returns the response of the asynchronous callout (the beginStockQuote method), this callback method is executed. It gets the response by calling the getValue method on the response object.

public Object processResponse() {
   result = stockQuoteFuture.getValue();
   return null; 
}

The following is the entire controller with the action and callback methods.

public class ContinuationSOAPController {
 
    AsyncSOAPStockQuoteService.GetStockQuoteResponse_elementFuture
           stockQuoteFuture;
    public String result {get;set;}

    // Action method
    public Continuation startRequest() {    
       Integer TIMEOUT_INT_SECS = 60;  
       Continuation cont = new Continuation(TIMEOUT_INT_SECS);
       cont.continuationMethod = 'processResponse';
       
       AsyncSOAPStockQuoteService.AsyncStockQuoteServiceSoap 
          stockQuoteService = 
            new AsyncSOAPStockQuoteService.AsyncStockQuoteServiceSoap();
           stockQuoteFuture = stockQuoteService.beginGetStockQuote(cont,'CRM');     
       return cont;   
    }    
    
    // Callback method
    public Object processResponse() {    
       result = stockQuoteFuture.getValue();
       // Return null to re-render the original Visualforce page
       return null; 
    }
}

This example shows the corresponding Visualforce page that invokes the startRequest method and displays the result field.

<apex:page controller="ContinuationSOAPController" showChat="false" showHeader="false">
   <apex:form >      
      <!-- Invokes the action method when the user clicks this button. -->
      <apex:commandButton action="{!startRequest}" 
              value="Start Request" reRender="result"/> 
   </apex:form>

   <!-- This output text component displays the callout response body. -->
   <apex:outputText value="{!result}" />
</apex:page>

Testing WSDL-Based Asynchronous Callouts

Testing asynchronous callouts that are based on Apex classes from a WSDL is similar to the process that’s used with callouts that are based on the HttpRequest class.

This example is the test class that corresponds to the ContinuationSOAPController controller. The test method in the class sets a fake response and invokes a mock continuation. The callout isn’t sent to the external service. To perform a mock callout, the test calls these methods of the Test class: setContinuationResponse(requestLabel, mockResponse) and invokeContinuationMethod(controller, request).

@isTest
public class ContinuationTestingForWSDL {
    global static testmethod void testWebService() {
        ContinuationSOAPController demoWSDLClass = new ContinuationSOAPController();
        // Invoke the continuation by calling the action method
        Continuation conti = demoWSDLClass.startRequest();
        
        // Verify that the continuation has the proper requests
        Map<String, HttpRequest> requests = conti.getRequests();
        system.assert(requests.size() == 1);
        
        // Perform mock callout 
        // (i.e. skip the callout and call the callback method)
        HttpResponse response = new HttpResponse();     
        response.setBody('Mock response body');
        // Set the fake response for the continuation  
        String requestLabel = requests.keyset().iterator().next();
        Test.setContinuationResponse(requestLabel, response);
        // Invoke callback method
        Object result = Test.invokeContinuationMethod(demoWSDLClass, conti);
        
        // result is the return value of the callback
        System.assertEquals(null, result);
        // Verify that the controller's result variable
        //   is set to the mock response.
        System.assertEquals('Mock response body', demoWSDLClass.result);
    }
}
Previous
Next