Thursday, 2 April 2015

Handling SOAP Faults

When we invoke a web service operation it may give proper response or throw an exception. So far we have seen web service giving a proper response. How to use exception in web service? How our addEmployee operation throws a user defined exception when it receives a NULL value? Lets see.

EmployeeService.java (service interface)

package com.sri.service;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

import com.sri.model.Employee;

@WebService
@SOAPBinding(style=Style.DOCUMENT)
public interface EmployeeService {

       @WebMethod
       public void addEmployee(Employee e) throws Exception;
       @WebMethod
       public Employee[] getEmployees();
}

EmployeeServiceImpl.java (Service Implementation)

package com.sri.service;

import java.util.ArrayList;

import javax.jws.WebService;

import com.sri.model.Employee;

@WebService(endpointInterface="com.sri.service.EmployeeService")
public class EmployeeServiceImpl implements EmployeeService{

       private ArrayList<Employee> employee;
       @Override
       public void addEmployee(Employee e) throws Exception {

              if(e == null){
                     throw new InvalidEmployeeException("Employee object is NULL");
              }
              if(employee == null)
                     employee = new ArrayList<Employee>();
              employee.add(e);
       }

       @Override
       public Employee[] getEmployees() {
              return (Employee[])employee.toArray();
       }

}

InvalidEmployeeException.java (User defined exception)

package com.sri.service;

public class InvalidEmployeeException extends Exception {

       public InvalidEmployeeException(String errorMsg){
              super(errorMsg);
       }
}


After deploy this service you can see WSDL updated with the exception details.
Along with input and output messages, the exception message also declared.


As we throw only one exception, one exception message has been defined. If we throw more than one exception then as many number of exception messages would be defined in the WSDL.



Types information also defined for the corresponding exception message in the types section.




Tuesday, 31 March 2015

WSDL Components

WSDL is an XML file which contains following sections.
  • service
  • binding
  • porttype
  • message
  • type

Service:

The service element in the WSDL defines the name of the service. Here I used the Employee service. Usually the service name is constructed from the implementation class + “service”. In our case implementation class name is EmployeeServiceImpl. The “service” is appended at the end of the implementation class and the name of our service becomes EmployeeServiceImplService. It also defines the end-point url or web service location which is http://localhost:8080/Employee/employee. The service tag also defines the port name EmployeeServiceImplPort and the binding tns:EmployeeServiceImplPortBinding. 


Binding:

This element defines how the web service accesses the values. The transport=http://schemas.xmlsoap.org/soap/http tells the web service is accessed via http. It also specifies we are using the Document style web service.


Porttype:

Porttype defines the service operations available for the web service. In the Employee service we have two operations.

  • addEmployee
  • getEmployees
Consider a normal java method which has one optional return type and zero or more input arguments.
Similarly our web service operation also has one input and one output message.



Operation
Messages
Complex Type
Parameter/Return type
addEmployee
tns:addEmployee
addEmployee
{empId,empName,{deptId,deptName}}

tns:addEmployeeResponse
addEmployeeResponse
Won’t return anything
getEmployees
tns:getEmployees
getEmployees
No Parameters

tns:getEmployeesResponse
getEmployeesResponse
Employee array







Message:

Is one input and one output message is mandatory? Because normal java method may not return any output and may not accept any input. Similarly it may accept more than one input as argument. So how can we define one input and one output message? 



Types:

Types section has the answer for the questions raised in the messages section. It has attribute schemaLocation which defines the messages in details. Paste this information in a separate url and you will find a page http://localhost:8080/Employee/employee?xsd=1.


Types referring a namespace http://service.sri.com which is reversal of the package structure com.sri.service. Each message was defined in terms of primary attribute. For instance addEmployee method accepts one input argument Employee. Employee has two primary datatype attributes and one user defined Department. All are defined in the complexType section in terms of primary attribute.



getEmployees operation may return zero or more employees. To denote it in the getEmployeeResponse, we have attributes minOccurs=0 and maxOccurs=unbound. 

For the addEmployee operation we need to pass one employee object. What if we pass the NULL to this method? Because of this, addEmployee operation defined minOccurs=0. We may pass employee object or we may not pass anything.
In case of RPC style web service the messages were defined in WSDL itself instead of a separate schema. Only the user defined data types were moved to another schema location.


Monday, 30 March 2015

Using custom objects in Webservice

In this blog post I am going to discuss how to use the custom objects in the web service. This EmployeeService will have two operations namely addEmployee() and getEmployees(). Let’s start coding.

EmployeeService.java (service interface)
package com.sri.service;

import javax.jws.WebMethod;
import javax.jws.WebService;
import com.sri.model.Employee;

@WebService
public interface EmployeeService {

       @WebMethod
       public void addEmployee(Employee e);
       @WebMethod
       public Employee[] getEmployees();
}

EmployeeServiceImpl.java (service Implementation)
package com.sri.service;

import java.util.ArrayList;
import javax.jws.WebService;
import com.sri.model.Employee;

@WebService(endpointInterface="com.sri.service.EmployeeService")
public class EmployeeServiceImpl implements EmployeeService{

       private ArrayList<Employee> employee;
       @Override
       public void addEmployee(Employee e) {

              if(employee == null)
                     employee = new ArrayList<Employee>();
              employee.add(e);
       }

       @Override
       public Employee[] getEmployees() {
              return (Employee[])employee.toArray();
       }

}

Employee.java (pojo class)
package com.sri.model;

public class Employee {

       private int empId;
       private String empName;
       private Department deptId;
       public int getEmpId() {
              return empId;
       }
       public void setEmpId(int empId) {
              this.empId = empId;
       }
       public String getEmpName() {
              return empName;
       }
       public void setEmpName(String empName) {
              this.empName = empName;
       }
       public Department getDeptId() {
              return deptId;
       }
       public void setDeptId(Department deptId) {
              this.deptId = deptId;
       }
}

Department.java (Pojo class)

package com.sri.model;

public class Department {

       private int deprtmentId;
       private String departmentName;
       public int getDeprtmentId() {
              return deprtmentId;
       }
       public void setDeprtmentId(int deprtmentId) {
              this.deprtmentId = deprtmentId;
       }
       public String getDepartmentName() {
              return departmentName;
       }
       public void setDepartmentName(String departmentName) {
              this.departmentName = departmentName;
       }
      
}

sun-jaxws.xml

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
  <endpoint name="Employee"
      implementation="com.sri.service.EmployeeServiceImpl"
      url-pattern="/employee"/>
</endpoints>

For deploying this webservice in tomcat you can refer.
Now the webservice would be available in the following location.

http://localhost:8080/Employee/employee?wsdl


In the WSDL we used to define the input arguments and return types. If the input arguments are custom objects then how it get declared in WSDL? In this web service we are using two custom objects Employee and Department. These objects are defined in the another schema file associated with WSDL. See the line highlighted in the WSDL


Monday, 23 March 2015

WSDL - Bottom up and Top down

In Java, people used to write an interface first and they stick to the interface before writing the implementation class. The interface will obviously tells us what are all the input arguments, return type and exception if any. In web service the client won’t directly communicate with the implementation class.  The client used to talk with the interface. So we can do any modifications in the implementation class. 

                      

If you want to change anything in the interface like adding new service methods or adding new arguments to the existing service method leads to more problems.  All the clients using your web service would be notified to use the updated one - version 2.0. This might leads to the code change. Solution for this would be sticks to the service interface and do not alter it.

                        


But in the case web service we are writing the Implementation class first!!! Yes we are writing the HelloWorld interface and HelloWorldImpl class first. Then generated the WSDL out of it. (See the blog post How to generate the WSDL from implementation class). So which one should come first? Is it WSDL or implementation class? We can do this in either way. In our case first we have written the implementation class first and generated WSDL out of it. It is known as service first or implementation first approach or bottom up approach.

There are some tools available to generate the WSDL. So we can write a WSDL first and generate classes which are necessary for writing a web service out of it. This is known as contract first or WSDL first approach or Top down approach.

Thursday, 19 March 2015

Consuming Global Weather Web Service

In this blog post I am going to explain about consuming the Global Weather web service available at webservicex.net.

Global Weather:
Global Weather web service has two service operations
  • GetCitiesByCountry
  • GetWether


Initially I am going to populate a drop down list with some country names. Based on a country you select, drop down will trigger an AJAX call which in turn calls a service operation GetCitiesByCountry. This service operation would return cities of the country you have selected in a XML format. You can parse this XML and load another drop down with the city names. Then you can select a city name in the city drop down list and it will call the GetWeather service operation. This service operation would also returns an XML and I am going to parse this using JAXB.

 The WSDL location is.


Using the wsimport parse the WSDL and generate the web service client Java files. (See the blog post). Copy these files to net.webservicex package. See the diagram for more information.


index.jsp

This page will display country names and the drop down is linked with the AJAX call. 


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
<script type="text/javascript">
function getCities() {
var selectOb = document.getElementById("country");
var country = selectOb.options[selectOb.selectedIndex].value;
if (country == "-1") {
alert("Invalid selection");
return false;
}
var xmlHttp;
xmlHttp = new XMLHttpRequest();
var url = "GetCities.jsp";
url += "?country=" + country;
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 || xmlHttp.readyState == "complete") {
document.getElementById("cities").innerHTML = xmlHttp.responseText
}

};
xmlHttp.open("GET", url, true);
xmlHttp.send(null);

}
function getTemp(x) {

var selectOb = document.getElementById("country");
var country = selectOb.options[selectOb.selectedIndex].value;
if (country == "-1" || x == "select") {
alert("Invalid selection");
return false;
}
var xmlHttp;
xmlHttp = new XMLHttpRequest();
var url = "GetWeather.jsp";
url += "?country=" + country + "&city=" + x;
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 || xmlHttp.readyState == "complete") {
document.getElementById("weather").innerHTML = xmlHttp.responseText
}

};
xmlHttp.open("GET", url, true);
xmlHttp.send(null);

}
</script>
</head>
<body>
Country &nbsp;
<select onchange="getCities()" id="country">
<option value="-1">--select--</option>
<option value="India">India</option>
<option value="Ukraine">Ukraine</option>
<option value="US">US</option>
<option value="Nepal">Nepal</option>
<option value="Germany">Germany</option>
<option value="Japan">Japan</option>
</select>
<br>
<br>
<div id="cities">
Cities &nbsp;&nbsp;&nbsp;&nbsp; <select onChange="getTemp(this)"
id="city">
<option value="-1">--select--</option>
</select>
</div>
<div id="weather"></div>
</body>
</html>

GetCities.jsp

The AJAX call on a country drop down linked with GetCities.jsp. This GetCities.jsp in turn call GetCitiesByCountry service operation via GlobalWeatherDelegate.java


<%@page import="org.sri.bd.GlobalWeatherDelegate"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
import="java.util.*" pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<%
String cities = "Cities &nbsp;&nbsp;&nbsp;&nbsp;  <select name='cityselect'                                               onChange=getTemp(this.value);>  <option value='-1'>Select</option>";
GlobalWeatherDelegate globalWeatherbd = new GlobalWeatherDelegate();
ArrayList<String> cityList = (ArrayList<String>) globalWeatherbd
.getCities(request.getParameter("country"));
Iterator<String> cityIerator = cityList.iterator();
while (cityIerator.hasNext()) {
String city = cityIerator.next();
cities += "<option value=" + city + ">" + city + "</option>";
}
cities += "</select>";
System.out.println(cities);
out.println(cities);
%>
</body>
</html>

GetWeather.jsp

Once you select a city on the city drop down it will trigger an AJAX call through GetWeather.jsp. GetWeather.jsp will call the GlobalWeatherDelegate.java to get a weather information of a city


<%@page
import="org.sri.bd.GlobalWeatherDelegate,org.sri.bd.CurrentWeather"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<%
GlobalWeatherDelegate globalWeatherbd = new GlobalWeatherDelegate();
CurrentWeather currentWeather = globalWeatherbd.getWeather(
request.getParameter("city"),  request.getParameter("country"));
if (currentWeather != null) {
String responseXml = "<table border=\"0\" cellspacing=\"10\" cellpadding=\"10\"> <th> "
+ request.getParameter("city")
+ "</th> <tr> <td> Location </td> <td> "
+ currentWeather.getLocation()
+ "</td> </tr>"
+ "<tr> <td> Time </td> <td> "
+ currentWeather.getTime()
+ "</td> </tr>"
+ "<tr> <td> wind </td> <td> "
+ currentWeather.getWind()
+ "</td> </tr>"
+ "<tr> <td> Visibility </td> <td> "
+ currentWeather.getVisibility()
+ "</td> </tr>"
+ "<tr> <td> Temperature </td> <td> "
+ currentWeather.getTemperature()
+ "</td> </tr>"
+ "<tr> <td> Pressure </td> <td> "
+ currentWeather.getPressure()
+ "</td> </tr>"
+ "<tr> <td> Dewpoint </td> <td> "
+ currentWeather.getDewPoint()
+ "</td> </tr>"
+ "<tr> <td> Humidity </td> <td> "
+ currentWeather.getRelativeHumidity()
+ "</td> </tr>"
+ "</table>";

out.print(responseXml);
} else {
out.print("<h3> No Data Found Try another City </h3>");
}
%>

<table border="1" cellspacing="10" cellpadding="10">
</table>
</body>
</html>



CurrentWeather.java

package org.sri.bd;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="CurrentWeather")
public class CurrentWeather {

private String location;
private String time;
private String wind;
private String visibility;
private String temperature;
private String dewPoint;
private String relativeHumidity;
private String pressure;
private String status;
@XmlElement(name="Location")
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
@XmlElement(name="Time")
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
@XmlElement(name="Wind")
public String getWind() {
return wind;
}
public void setWind(String wind) {
this.wind = wind;
}
@XmlElement(name="Visibility")
public String getVisibility() {
return visibility;
}
public void setVisibility(String visibility) {
this.visibility = visibility;
}
@XmlElement(name="Temperature")
public String getTemperature() {
return temperature;
}
public void setTemperature(String temperature) {
this.temperature = temperature;
}
@XmlElement(name="DewPoint")
public String getDewPoint() {
return dewPoint;
}
public void setDewPoint(String dewPoint) {
this.dewPoint = dewPoint;
}
@XmlElement(name="RelativeHumidity")
public String getRelativeHumidity() {
return relativeHumidity;
}
public void setRelativeHumidity(String relativeHumidity) {
this.relativeHumidity = relativeHumidity;
}
@XmlElement(name="Pressure")
public String getPressure() {
return pressure;
}
public void setPressure(String pressure) {
this.pressure = pressure;
}
@XmlElement(name="Status")
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String toString() {
return "CurrentWeather [location=" + location + ", time=" + time
+ ", wind=" + wind + ", visibility=" + visibility
+ ", temperature=" + temperature + ", dewPoint=" + dewPoint
+ ", relativeHumidity=" + relativeHumidity + ", pressure="
+ pressure + ", status=" + status + "]";
}
}

GlobalWeatherDelegate.java

This Java class is responsible for calling the web service and fetching the details from it. This will parse the XML returned from the web service using the JAXB and give it back to the JSP.

package org.sri.bd;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import net.webservicex.GlobalWeather;
import n et.webservicex.GlobalWeatherSoap;

public class GlobalWeatherDelegate {

GlobalWeather weather = new GlobalWeather();
GlobalWeatherSoap soap = weather.getGlobalWeatherSoap();

public List<String> getCities(String countryName) {
ArrayList<String> cityList = new ArrayList<String>();
GlobalWeather weather = new GlobalWeather();
GlobalWeatherSoap soap = weather.getGlobalWeatherSoap();
String cities = soap.getCitiesByCountry(countryName);

try {
DocumentBuilder documentBuilder = DocumentBuilderFactory
.newInstance().newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(cities));
Document doc = documentBuilder.parse(is);
NodeList nodes = doc.getElementsByTagName("Table");
for (int i = 0; i < nodes.getLength(); i++) {
Element element = (Element) nodes.item(i);
NodeList subnode = element.getElementsByTagName("City");
Element subelement = (Element) subnode.item(0);
cityList.add(subelement.getTextContent());
System.out.println(subelement.getTextContent());
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return cityList;
}

public CurrentWeather getWeather(String city, String country) {

try {
GlobalWeather weather = new GlobalWeather();
GlobalWeatherSoap soapWeather = weather.getGlobalWeatherSoap();
String response = soapWeather.getWeather(city, country);
if (response.equalsIgnoreCase("Data Not Found"))
return null;
 
         else {

JAXBContext jAXBContext = JAXBContext
.newInstance(CurrentWeather.class);
Unmarshaller unmarshaller = jAXBContext.createUnmarshaller();
CurrentWeather currentWeather = (CurrentWeather) unmarshaller
.unmarshal(new StringReader(response));
return currentWeather;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

}