Using Public APIs and Wrapping RESTful API with SOAP

Introduction

Application Programming Interface (API) is a set of protocols, programming instructions and standards which makes it possible to interact with an external application. Within Service Oriented Architecture (SOA), the role of APIs is becoming more important. The technologies within the SOA help us design, implement, test and deploy the APIs. Nowadays a lot of companies such as Google, Amazon, Facebook and Twitter expose their platform's core functionalities as public APIs.

In this lab, we will change the address validation service which was developed as part of Onboarding Validation to use a public API to validate the address. The Address Location Web Service is a public API which can pinpoint a location within NSW. We will use this service to try to pinpoint an address. The idea is that if the operation fails to pinpoint the exact address, then we will consider the address invalid.

Address Location Web Service

One of the most important part of public APIs is that they must be well documented. In this case the Address Location Web Service page fully describes the input and output for this service. Please spend some time reading the documentation.

Try testing the service by giving it some input. You can use your browser, Postman or SoupUI to test this service. In this lab we use chrome browser and construct the HTTP request. An example is shown below (you can click the image to make it bigger).

As you can see, this API retrieves any address's pinpoint within NSW and returns it to the client.

Modifying Address Validation Service

In lab02, we developed an address validation service. Backup your existing lab02 solution somewhere else. In this section,we will directly modify the Lab02 solution so that it now uses the Address Location API to validate the address. The following instructions describe the steps to implement the new functionality.

  1. Add the following maven dependencies in the dependency section of the project pom file

     <dependency>
    	<groupId>org.apache.httpcomponents</groupId>
    	<artifactId>fluent-hc</artifactId>
    	<version>4.5.3</version>
     </dependency>
    

    Fluent facade API is an easy to use library which is intended for simple use cases which do not require full flexibility of a HTTP client.

     <dependency>
    	<groupId>com.google.code.gson</groupId>
    	<artifactId>gson</artifactId>
    	<version>2.8.0</version>
     </dependency>
    

    Gson is a Java library which developed by Google. This library can be used to convert Java objects to JSON representation and vice versa.

  2. In the next steps we will modify the validateAddress method in OnboadrdingValidationServiceImpl.java to use Address Location API. Because the Address Location API only works for addresses within NSW, first we need to check if state in the request is equals to "NSW".

    
    public ValidateAddressResponse validateAddress(ValidateAddressRequest request) 
    			throws ValidateAddressFaultMsg {
      if(request.getState() != null && !request.getState().equals("NSW")){
    	ServiceFaultType validateAddressFault = new ServiceFaultType();
    	validateAddressFault.errcode = "800";
    	validateAddressFault.errtext = "Invalid Address";
    	ValidateAddressFaultMsg validateAddressFaultMsg = new ValidateAddressFaultMsg( "Validation Error", validateAddressFault);
    	throw validateAddressFaultMsg;
      }
      ...
    
  3. Next step is to build a HTTP request and call Address Location API.

    
    ...
    Content responseContent = Request.Get("http://maps.six.nsw.gov.au/services/public/Address_Location?"+
    					"houseNumber="+ request.houseNumber +
    					"&roadName="+ request.roadName +
    					"&roadType=St"+ request.roadType +
    					"&suburb="+ request.suburb +
    					"&postCode="+ request.postCode +
    					"&projection=EPSG%3A4326")
    			.execute().returnContent();
    			
    responseStr = responseContent.asString();
    ...
    
  4. Last step is to validate if the address was found by Address Location API. If number of records which is returned from Address Location API is equal to zero then address is invalid.

    
    ...
    if (responseStr.contains("\"numRecs\":0")){
    	ServiceFaultType validateAddressFault = new ServiceFaultType();
    	validateAddressFault.errcode = "801";
    	validateAddressFault.errtext = "Invalid Address";
    	ValidateAddressFaultMsg validateAddressFaultMsg = new ValidateAddressFaultMsg( "Validation Error", validateAddressFault);
    	throw validateAddressFaultMsg;
    }
    ...
    

Data Transformation

So far in this lab we only focused on just calling the Address Location API and validating the address but our job is not done yet. validateAddress method returns some extra information(e.g., council name) to the client. All the extra information which we need exist in Address Location API's response. In this section we discuss the data transformation in validateAddress method.

  1. As mentioned earlier, Gson library is used to transform JSON to Java objects. Before implementing the transformation, Gson requires Java classes which represent the JSON object. Below is the sample response from Address Location API.

    
    {
    	"addressResult": {
    		"addresses": [{
    			"houseNumberFirst": 429,
    			"houseNumberFirstSuffix": null,
    			"houseNumberSecond": 481,
    			"houseNumberSecondSuffix": null,
    			"objectId": 3721773,
    			"propid": 2027783,
    			"postCode": 2000,
    			"roadName": "George",
    			"roadSuffix": null,
    			"roadType": "Street",
    			"shortAddressString": "133/429-481 GEORGE STREET SYDNEY",
    			"suburbName": "Sydney",
    			"addressType": "Assigned",
    			"council": "Sydney",
    			"addressString": "429null-481null George Street nullSydney 2000",
    			"houseNumberString": "429-481",
    			"addressPoint": {
    				"addressPointType": null,
    				"addressPointUncertainty": null,
    				"addressstringOid": 0,
    				"containment": null,
    				"createDate": null,
    				"gurasId": null,
    				"objectId": null,
    				"prowayOid": null,
    				"waypointOid": null,
    				"coordRefSys": "EPSG:4283",
    				"centreX": 151.2069968039468,
    				"centreY": -33.865815165676935
    			}
    		}],
    		"errMsg": null,
    		"numRecs": 1,
    		"searchMethod": {
    			"methodDescriptions": ["Input parameters matched",
    			"Addresses Returned"]
    		}
    	}
    }
    

    Create AddressLocationResult.java, AddressResult.java, Address.java and AddressPoint.java classes in your project.

  2. Add transformResponse method to your OnboadrdingValidationServiceImpl class. This method receives the response from Address Location API and transfors it to ValidateAddressResponse.

    
    public ValidateAddressResponse transformResponse(String responseStr){
    	Gson gson = new GsonBuilder().create();
    	AddressLocationResult addressResult = gson.fromJson(responseStr, AddressLocationResult.class);
    
    	ValidateAddressResponse response = new ValidateAddressResponse();
    	Address address = addressResult.addressResult.addresses.get(0);
    	response.addressType = address.addressType;
    	response.councilName = address.council;
    	response.centreX = address.addressPoint.centreX;
    	response.centreY = address.addressPoint.centreY;
    	response.houseNumberString = address.houseNumberString;
    	return response;
    }
    

Error Mapping/ Exception Handling

Exception handling is very important when we are using external APIs. If external APIs throw exceptions and they are not handled properly it could lead to unexcpected behaviour. In lab02 we defined SOAP faults which we used to throw address validation exception. After integrating with Address Location service, we are still throwing similar errors (i.e., errorCode 800 and 801) as we mentioned them in the previous steps. But there are some specific errors which might accure while we are invoking Address Location API. ClientProtocolException and IOException are two errors which might happen. The code below describes how these exceptions are caught and transformed to SOAP faults. Add the error mapping/exception handling part of the code into your solution.

OnboadrdingValidationServiceImpl.java contains the full implementation after integrating with Address Location API.


try {
	Content responseContent = Request.Get("http://maps.six.nsw.gov.au/services/public/Address_Location?"+
			"houseNumber="+ request.houseNumber +
			"&roadName="+ request.roadName +
			"&roadType=St"+ request.roadType +
			"&suburb="+ request.suburb +
			"&postCode="+ request.postCode +
			"&projection=EPSG%3A4326")
	.execute().returnContent();
	
	...
	
} catch (ClientProtocolException e) {
	ServiceFaultType validateAddressFault = new ServiceFaultType();
	validateAddressFault.errcode = "802";
	validateAddressFault.errtext = e.getMessage();
	ValidateAddressFaultMsg validateAddressFaultMsg = new ValidateAddressFaultMsg( "Something went wrong while contacting Address Location API", validateAddressFault);
	throw validateAddressFaultMsg;
} catch (IOException e) {
	ServiceFaultType validateAddressFault = new ServiceFaultType();
	validateAddressFault.errcode = "803";
	validateAddressFault.errtext = e.getMessage();
	ValidateAddressFaultMsg validateAddressFaultMsg = new ValidateAddressFaultMsg( "Address Location API is not available", validateAddressFault);
	throw validateAddressFaultMsg;
}

Build the project and deploy your application to tomcat container. Use the Soap UI project developed in lab02 to call address validation service

Saving/Loading Docker Containers

So far we used the standard tomcat container to run our applications. This means that every time we start the tomcat container (the vanilla version) we need to deploy our applications manually. It is also possible to create Docker containers which run our applications automatically as soon as they start. This way we can simply run the container and our application is ready to use. Another advantage of these containers is we can save them as a tar file and load them in other machines and run them. Below is the instructions for saving and loading Docker containers/images. Please note that application-name in these instructions is just a placeholder and you need to replace it with your application name.

Saving/Exporting your Docker Container into a Docker Image

  1. First, make sure your application is deployed successfully on to the container and running correctly. You could use Soap UI to test it before you move on to the next step.

  2. Run the following command to commit the state of your container (i.e., tomcat8). This command will create a Docker image which contains your application that runs on tomcat server.

    $ docker commit tomcat8 your-application-name
  3. Run the following command. This step saves the image which was created in the previous step to a tar file. The output tar file's size still can be reduced using a compression software (e.g., 7-Zip, winRAR, etc)

    $ docker save --output your-application-name.tar your-application-name:latest

Loading/Importing a Docker Image and Run as a Container

  1. The following instruction loads the Docker image into the image repository.

    $ docker load --input your-application-name.tar
  2. Final step is starting a new Docker container based on the new image.

    $ docker run -it --rm --name your-application-name -p 8888:8080 your-application-name:latest