Skip to content

REST to SOAP

Ballerina is an open-source programming language that empowers developers to integrate their system easily with the support of connectors.

This tutorial demonstrates a scenario where Ballerina is used to call a SOAP backend. Here, a SOAP backend is fronted by a Ballerina service that accepts a REST request and converts it to a SOAP request. The SOAP connector is used to call the SOAP backend.

What you'll build

We create a service stockQuote that fronts a SOAP backend. The service has two resources getQuote and placeOrder, which calls respectively the relevant services from the SOAP backend. The response received from the backend is finally sent back to the client.

rest_to_soap

Before you begin

Get the code

Pull the module from Ballerina Central using the following command.

ballerina pull wso2/stockquote_service

Alternately, you can download the ZIP file and extract the contents to get the code.

Download ZIP

Implementation

  • Create a new Ballerina project named rest-to-soap-service.

    $ ballerina new rest-to-soap-service

  • Navigate to the rest-to-soap-service directory.

  • Add a new module named stockquote_service to the project.

    $ ballerina add stockquote_service
  • Open the project with VS Code. The project structure will be similar to the following.

    .
    ├── Ballerina.toml
    └── src
        └── stockquote_service
            ├── main.bal
            ├── Module.md
            ├── resources
            └── tests
                ├── main_test.bal
                └── resources

    We can remove the file main_test.bal for the moment, since we are not writing any tests for our service.

First let's create a mock SOAP service. Please note that Ballerina does not support writing SOAP services. Therefore, we're creating mock service that would mimic SOAP responses.

  • Create a file named mock_soap_service.bal under stockquote_service with the following content.

mock_soap_service.bal

import ballerina/http;
import ballerina/log;
import ballerina/stringutils;

@http:ServiceConfig {
    basePath: "/services"
}
service services on new http:Listener(9000) {
    @http:ResourceConfig {
        methods: ["GET", "POST"],
        path: "/SimpleStockQuoteService"
    }
    resource function SimpleStockQuoteService(http:Caller caller, http:Request request) {
        log:printInfo("Stock quote service invoked.");
        string soapAction = validateSoapAction(request.getHeader("SOAPAction"));
        xml requestPayload = checkpanic request.getXmlPayload();
        string company = requestPayload.Body.symbol.getTextValue();
        http:Response response = new; 
        match soapAction {
            "urn:getQuote" => {
                response.setXmlPayload(<@untainted> getQuote(company));
                log:printInfo("Stock quote generated.");                
            }
            "urn:placeOrder" => {
                response.setXmlPayload(<@untainted> placeOrder());
                log:printInfo("The order was placed.");
            }
            _ => {
                response.statusCode = http:STATUS_BAD_REQUEST;
                response.setTextPayload("Unsupported Action!!!");
            }
        }
        error? respond = caller->respond(response);
    }
}

function getQuote(string company) returns xml {
    xmlns "http://schemas.xmlsoap.org/soap/envelope/" as soapenv;
    xmlns "http://services.samples" as ns;
    xmlns "http://services.samples/xsd" as ax21;
    xml responsePayload = xml `<soapenv:Envelope>
                                    <soapenv:Body>
                                        <ns:getQuoteResponse>
                                                <ax21:change>-2.86843917118114</ax21:change>
                                                <ax21:earnings>-8.540305401672558</ax21:earnings>
                                                <ax21:high>-176.67958828498735</ax21:high>
                                                <ax21:last>177.66987465262923</ax21:last>
                                                <ax21:low>-176.30898912339075</ax21:low>
                                                <ax21:marketCap>5.649557998178506E7</ax21:marketCap>
                                                <ax21:name>${company} Company</ax21:name>
                                                <ax21:open>185.62740369461244</ax21:open>
                                                <ax21:peRatio>24.341353665128693</ax21:peRatio>
                                                <ax21:percentageChange>-1.4930577008849097</ax21:percentageChange>
                                                <ax21:prevClose>192.11844053187397</ax21:prevClose>
                                                <ax21:symbol>${company}</ax21:symbol>
                                                <ax21:volume>7791</ax21:volume>
                                        </ns:getQuoteResponse>
                                    </soapenv:Body>
                                </soapenv:Envelope>`;

    return responsePayload;
}

function placeOrder() returns xml {
    xmlns "http://schemas.xmlsoap.org/soap/envelope/" as soapenv;
    xmlns "http://services.samples" as ns;
    xmlns "http://services.samples/xsd" as ax21;
    xml responsePayload = xml `<soapenv:Envelope>
                                    <soapenv:Body>
                                        <ns:placeOrderResponse>
                                                <ax21:status>created</ax21:status>
                                        </ns:placeOrderResponse>
                                    </soapenv:Body>
                                </soapenv:Envelope>`;
    return responsePayload;
}

function validateSoapAction(string soapAction) returns string {
    if (soapAction.startsWith("urn:")) {
        return soapAction;
    } else {
        // Remove `\"` from soap action.
        return stringutils:replace(soapAction, "\\\"", "");
    }
}

This service checks the SOAPAction header in the request, calls the relevant method, and responds with a SOAP envelope.

  • Now let's open the main.bal file and add the following content. This is going to be our integration logic.

main.bal

import ballerina/http;
import ballerina/jsonutils;
import wso2/soap;

soap:Soap11Client soapEP = new("http://localhost:9000/services/SimpleStockQuoteService");

@http:ServiceConfig {
    basePath: "/stockQuote"
}
service stockQuote on new http:Listener(9090) {
    @http:ResourceConfig {
        methods: ["GET"],
        path: "/quote/{symbol}"
    }
    resource function getQuote(http:Caller caller, http:Request request, string symbol) {
        xmlns "http://services.samples" as m0; 
        xml payload = xml `<m0:symbol>${symbol}</m0:symbol>`; 
        soap:SoapResponse soapResponse = checkpanic soapEP->sendReceive("urn:getQuote", payload); 
        xml responsePayload = checkpanic soapResponse.httpResponse.getXmlPayload(); 
        http:Response response = new;
        response.setJsonPayload(<@untainted>checkpanic jsonutils:fromXML(responsePayload.Body.getQuoteResponse,
            {preserveNamespaces: false}));        
        error? respond = caller->respond(response);
    }

    @http:ResourceConfig {
        methods: ["POST"],
        path: "/order"
    }
    resource function placeOrder(http:Caller caller, http:Request request) {
        json orderReq = checkpanic request.getJsonPayload();
        string price = orderReq.price != null ? orderReq.price.toString() : "";
        string quantity = orderReq.quantity != null ? orderReq.quantity.toString() : "";
        string symbol = orderReq.symbol != null ? orderReq.symbol.toString() : "";
        xmlns "http://services.samples" as m;
        xml payload = xml `<m:placeOrder>
                                <m:order>
                                    <m:price>${price}</m:price>
                                    <m:quantity>${quantity}</m:quantity>
                                    <m:symbol>${symbol}</m:symbol>
                                </m:order>
                            </m:placeOrder>`;
        soap:SoapResponse soapResponse = checkpanic soapEP->sendReceive("urn:placeOrder", <@untainted> payload); 
        xml responsePayload = checkpanic soapResponse.httpResponse.getXmlPayload(); 
        http:Response response = new;
        response.setJsonPayload(<@untainted>checkpanic jsonutils:fromXML(responsePayload.Body.placeOrderResponse,
            {preserveNamespaces: false}));
        error? respond = caller->respond(<@untainted> response);
    }
}

Here we create a SOAP client with the mock SOAP service we created earlier. There are two resources in the service - getQuote and placeOrder. In the getQuote resource, we construct an XML payload with the path parameter company and pass the payload to the SOAP client. Then we receive the response, convert it to JSON and respond back to the client. The resource placeOrder also has a similar logic except the XML payload is constructed from the values in the request payload.

Testing

  • First let’s build the module. While being in the exposing-soap-service directory, execute the following command.

    $ ballerina build stockquote_service

This creates the executables.

  • Now run the .jar file created in the above step.

    $ java -jar target/bin/stockquote_service.jar

Now we can see that two services have started on ports 9000 and 9090.

  • Let’s access the stockQuote service's getQuote resource by executing the following curl command.

    $ curl http://localhost:9090/stockQuote/quote/xyz

    We receive a JSON payload similar to the following.

    {  
    "getQuoteResponse":{  
        "change":"-2.86843917118114",
        "earnings":"-8.540305401672558",
        "high":"-176.67958828498735",
        "last":"177.66987465262923",
        "low":"-176.30898912339075",
        "marketCap":"5.649557998178506E7",
        "name":"xyz Company",
        "open":"185.62740369461244",
        "peRatio":"24.341353665128693",
        "percentageChange":"-1.4930577008849097",
        "prevClose":"192.11844053187397",
        "symbol":"xyz",
        "volume":"7791"
    }
    }
  • Now let's access the placeOrder resource by executing the following curl command.

    $ curl http://localhost:9090/stockQuote/order -H 'Content-Type:application/json' --data '{"price":"1000.00", "quantity":"2", "symbol":"abc"}'

    We receive a JSON payload similar to the following.

    {
        "placeOrderResponse":{
            "status":"created"
        }
    }
Top