Newer Version Available
Making an Asynchronous Callout from an Imported WSDL
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 extra 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.
1public Continuation startRequest() {
2 Integer TIMEOUT_INT_SECS = 60;
3 Continuation cont = new Continuation(TIMEOUT_INT_SECS);
4 cont.continuationMethod = 'processResponse';
5
6 AsyncSOAPStockQuoteService.AsyncStockQuoteServiceSoap
7 stockQuoteService =
8 new AsyncSOAPStockQuoteService.AsyncStockQuoteServiceSoap();
9 stockQuoteFuture = stockQuoteService.beginStockQuote(cont,'CRM');
10
11 return cont;
12}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.
1public Object processResponse() {
2 result = stockQuoteFuture.getValue();
3 return null;
4}The following is the entire controller with the action and callback methods.
1public class ContinuationSOAPController {
2
3 AsyncSOAPStockQuoteService.GetStockQuoteResponse_elementFuture
4 stockQuoteFuture;
5 public String result {get;set;}
6
7 // Action method
8 public Continuation startRequest() {
9 Integer TIMEOUT_INT_SECS = 60;
10 Continuation cont = new Continuation(TIMEOUT_INT_SECS);
11 cont.continuationMethod = 'processResponse';
12
13 AsyncSOAPStockQuoteService.AsyncStockQuoteServiceSoap
14 stockQuoteService =
15 new AsyncSOAPStockQuoteService.AsyncStockQuoteServiceSoap();
16 stockQuoteFuture = stockQuoteService.beginGetStockQuote(cont,'CRM');
17 return cont;
18 }
19
20 // Callback method
21 public Object processResponse() {
22 result = stockQuoteFuture.getValue();
23 // Return null to re-render the original Visualforce page
24 return null;
25 }
26}This example shows the corresponding Visualforce page that invokes the startRequest method and displays the result field.
1<apex:page controller="ContinuationSOAPController" showChat="false" showHeader="false">
2 <apex:form >
3 <!-- Invokes the action method when the user clicks this button. -->
4 <apex:commandButton action="{!startRequest}"
5 value="Start Request" reRender="result"/>
6 </apex:form>
7
8 <!-- This output text component displays the callout response body. -->
9 <apex:outputText value="{!result}" />
10</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. Before you test ContinuationSOAPController.cls, create a class that implements WebServiceMock. This class enables safe testing for ContinuationTestForWSDL.cls, which we'll create in a moment, by enabling a mock continuation and making sure that the test has no real effect.
1public class AsyncSOAPStockQuoteServiceMockImpl implements WebServiceMock {
2 public void doInvoke(
3 Object stub,
4 Object request,
5 Map<String, Object> response,
6 String endpoint,
7 String soapAction,
8 String requestName,
9 String responseNS,
10 String responseName,
11 String responseType) {
12 // do nothing
13 }
14}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: Test.setContinuationResponse() and Test.invokeContinuationMethod().
1@isTest
2public class ContinuationTestingForWSDL {
3 public static testmethod void testWebService() {
4
5 ContinuationSOAPController demoWSDLClass =
6 new ContinuationSOAPController();
7
8 // Invoke the continuation by calling the action method
9 Continuation conti = demoWSDLClass.startRequest();
10
11 // Verify that the continuation has the proper requests
12 Map<String, HttpRequest> requests = conti.getRequests();
13 System.assertEquals(requests.size(), 1);
14
15 // Perform mock callout
16 // (i.e. skip the callout and call the callback method)
17 HttpResponse response = new HttpResponse();
18 response.setBody('<SOAP:Envelope'
19 + ' xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">'
20 + '<SOAP:Body>'
21 + '<m:getStockQuoteResponse '
22 + 'xmlns:m="http://soap.sforce.com/schemas/class/StockQuoteServiceSoap">'
23 + '<m:result>Mock response body</m:result>'
24 + '</m:getStockQuoteResponse>'
25 + '</SOAP:Body>'
26 + '</SOAP:Envelope>');
27
28 // Set the fake response for the continuation
29 String requestLabel = requests.keyset().iterator().next();
30 Test.setContinuationResponse(requestLabel, response);
31
32 // Invoke callback method
33 Object result = Test.invokeContinuationMethod(demoWSDLClass, conti);
34 System.debug(demoWSDLClass);
35
36 // result is the return value of the callback
37 System.assertEquals(null, result);
38
39 // Verify that the controller's result variable
40 // is set to the mock response.
41 System.assertEquals('Mock response body', demoWSDLClass.result);
42 }
43}