Generating and deploying web service stubs using WSO2

In case you need a relatively simple stub that simulates a third-party web service or application during your testing process, you have several options at your disposal. In this article, I will introduce the WSO2 Enterprise Service Bus, which is one of these options, and show you how you can use it to quickly simulate an existing web service. This is particularly useful when the actual web service or application is not (always) available during testing, or when you want to simulate particular behavior in your testing process.

The WSO2 Enterprise Service Bus
The WSO2 Enterprise Service Bus is a lightweight, open source ESB implementation that has gained significant popularity in recent years. It is used by companies such as Ebay, British Airways and Volvo. Downloading and installation is simple: go to the product website and download a zip file from there. Unpack it and run wso2server.bat (Windows) or wso2server.sh (Linux) from the /bin directory. Note that you need a Java JDK to be installed for WSO2 ESB to run on your system.

Developing WSO2 stubs in Eclipse
As we are going to generate our own web service stub and deploy it using WSO2, it is a good idea to do all the work from within our IDE. I prefer to use Eclipse for this. Stop WSO2 using Ctrl+C as we are going to restart it from within Eclipse again later on. Start Eclipse and install the WSO2 Developer Studio using the Eclipse Marketplace.

stub_install_wso2_developer_studio

Generating a web service skeleton
For this example, I used a sample web service providing weather forecasts for cities in the US. Its WSDL is available here. In order to be able to generate a stub for this web service using WSO2, you should download it to your local machine.

In Eclipse, choose File > New > Project … > WSO2 > Service Hosting > Project Types > Axis2 Service Project. Next, select ‘Create New Axis2 Service Using WSDL File’ (this is why we need a copy of the WSDL on our system). Select the WSDL file and give your project a name:

stub_generate_skeleton_from_wsdl

Click finish to generate a web service skeleton for the service described in the WSDL.

Deploying the web service
To deploy our web service and interact with it, we need to carry out two steps. First, create a Carbon Application Project in Eclipse. This project bundles our web service project so it can be deployed. Select File > New … > Carbon Application Project, give the project a name, select our generated service as a dependency and click Finish.

stub_create_carbon_project

Next, we need to create a server in Eclipse on which to deploy the web service. Add a new server and choose WSO2 > WSO2 Carbon 4.0 based server as the server type. Click Next and select the installation directory of WSO2 as your CARBON_HOME folder. Click Next twice and add the Carbon Application project for our web service to the web server in the ‘Add and Remove’ window (this can be done later as well by right-clicking on the server and selecting ‘Add and Remove’). Finally, start the server.

stub_service_deployed

Testing the web service
Now that our web service skeleton has been deployed, let’s see whether we can communicate with it. The WSDL for the local web service implementation can be found using the WSO2 Management Console (user: admin, pass: admin) that is automatically opened when you start the server in Eclipse. You can then use a tool such as SmartBear SoapUI or Parasoft SOAtest to test the web service skeleton.

By default, none of the operations in the web service will be implemented yet, and sending a request message will result in a response containing a SOAP Fault:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
 <soapenv:Body>
  <soapenv:Fault>
   <faultcode>soapenv:Server</faultcode>
   <faultstring>Please implement com.cdyne.ws.weatherws.WeatherSkeleton#getCityForecastByZIP</faultstring>
   <detail/>
  </soapenv:Fault>
 </soapenv:Body>
</soapenv:Envelope>

The good news is that we have successfully created a web service skeleton from the WSDL web service description, that we deployed it on a local web server for testing purposes and that we are able to communicate with the web service, without having to write a single line of code.

Implementing the web service operations
In order for our web service simulation to return more intelligent responses, we will have to implement the methods that construct the actual responses. When you generate a web service skeleton using the method and example WSDL above, these methods will all be located in the WeatherSkeleton.java file. After generating the skeleton, the only action performed by each method is to throw the exception message we have seen when we first communicated with the web service:

public com.cdyne.ws.weatherws.GetCityForecastByZIPResponse getCityForecastByZIP (com.cdyne.ws.weatherws.GetCityForecastByZIP getCityForecastByZIP) {
            //TODO : fill this with the necessary business logic
            throw new  java.lang.UnsupportedOperationException("Please implement " + this.getClass().getName() + "#getCityForecastByZIP");
}

A simple implementation of this method, generating the same response no matter what is in the request message,is shown below:

public GetCityForecastByZIPResponse getCityForecastByZIP(GetCityForecastByZIP getCityForecastByZIP) {
	 
	 // generate new response for the web service
	 try {
		 GetCityForecastByZIPResponse cityForecastResponse = GetCityForecastByZIPResponse.class.newInstance();
		 
		 // create a new response object and the required objects to fill it
		 ForecastReturn fr = new ForecastReturn();
		 ArrayOfForecast aof = new ArrayOfForecast();
		 Forecast forecast = new Forecast();
		 Calendar cal = Calendar.getInstance();
		 Temp temp = new Temp();
		 POP pop = new POP();
		 
		 // set field values
		 fr.setCity("Boulder");
		 fr.setState("Colorado");
		 fr.setWeatherStationCity("Boulder");
		 fr.setResponseText("Forecast for Boulder, generated on " + new SimpleDateFormat("MM/dd/yyyy HH:mm:ss").format(cal.getTime()));
		 fr.setSuccess(true);
		 
		 // create weather forecast array and forecast entry
		 forecast.setDate(cal);
		 forecast.setDesciption("Sample forecast");
		 temp.setDaytimeHigh("90");
		 temp.setMorningLow("40");
		 forecast.setTemperatures(temp);
		 pop.setDaytime("12:00:00");
		 pop.setNighttime("00:00:00");
		 forecast.setProbabilityOfPrecipiation(pop);
		 
		 // add entry to forecast array
		 aof.addForecast(forecast);
		 
		 // add forecast to response and return it
		 fr.setForecastResult(aof);
		 cityForecastResponse.setGetCityForecastByZIPResult(fr);
		 return cityForecastResponse;
		 
	 } catch (Exception e) {
		 throw new UnsupportedOperationException("Unexpected error occurred during message processing");
	 }
 }

When we redeploy our web service (by right-clicking on the server in Eclipse and selecting ‘Redeploy’) and call the GetCityForecastByZIP operation again, we now get a sensible response from our web service:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope
   xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
 <soapenv:Body>
  <ns1:GetCityForecastByZIPResponse xmlns:ns1="http://ws.cdyne.com/WeatherWS/">
   <ns1:GetCityForecastByZIPResult>
    <ns1:Success>true</ns1:Success>
    <ns1:ResponseText>Forecast for Boulder, generated on 11/07/2013 09:02:54</ns1:ResponseText>
    <ns1:State>Colorado</ns1:State>
    <ns1:City>Boulder</ns1:City>
    <ns1:WeatherStationCity>Boulder</ns1:WeatherStationCity>
    <ns1:ForecastResult>
     <ns1:Forecast>
      <ns1:Date>2013-11-07T09:02:54.274+01:00</ns1:Date>
      <ns1:WeatherID>0</ns1:WeatherID>
      <ns1:Desciption>Sample forecast</ns1:Desciption>
      <ns1:Temperatures>
       <ns1:MorningLow>40</ns1:MorningLow>
       <ns1:DaytimeHigh>90</ns1:DaytimeHigh>
      </ns1:Temperatures>
      <ns1:ProbabilityOfPrecipiation>
       <ns1:Nighttime>00:00:00</ns1:Nighttime>
       <ns1:Daytime>12:00:00</ns1:Daytime>
      </ns1:ProbabilityOfPrecipiation>
     </ns1:Forecast>
    </ns1:ForecastResult>
   </ns1:GetCityForecastByZIPResult>
  </ns1:GetCityForecastByZIPResponse>
 </soapenv:Body>
</soapenv:Envelope>

In future posts, I will introduce ways to improve the functionality and flexibility of our simulated web service, and make it even more useful when used in our testing processes.

"