How to Write RESTful Web Services With JAX-WS

Recommended by 121 users

Jun 11, 2015
How to Write RESTful Web Services With JAX-WS
Add to Bookmark Email this Post 4.2K    1

A Web Service is an application deployed on a machine (server). Web services accept incoming requests from client and send response to client in either plain text, JSON or XML format.

The beautiful thing about web services is they are language independent. So a web service written in Python can be requested from a client written in Java, C#, Ruby etc.

Web services are classified into two categories:

  • SOAP – In SOAP-based web services both client and service transfer SOAP messages to communicate.
  • REST – While in REST-style services both client and service usually transfer raw XML to communicate.

Java provides API for creating both SOAP and REST-style web services

JAX-WS – JAX-WS (Java API for XML Web Services) is a Java API for creating both SOAP and REST-style web services. There is a common misconception that JAX-WS is only for SOAP-based services, which is not true. Using JAX-WS you can create both SOAP and REST-style services

JAX-RS – JAX-RS (Java API for RESTful Web Services) is a Java API to write RESTful web services easily

Note : Although JAX-WS can be used to write RESTful web services, it’s not the best way to create REST-style services. In a production mode you should probably use JAX-RS, Restlet framework etc.

Now, let’s see how to create a RESTful weather service using JAX-WS API.

We will write a weather service which takes a city name as parameter and returns the weather detail as an XML document. We will use weather service of Open Weather Map.

Now, let’s dive into the code.

Here is the Project structure in Eclipse IDE

restful-webservice-projectstructure

RESTfulWeather class implements the weather service which will be published by WeatherPublisher class

@WebServiceProvider
@ServiceMode(value = javax.xml.ws.Service.Mode.MESSAGE)
@BindingType(value = HTTPBinding.HTTP_BINDING)

public class RESTfulWeather implements Provider {

	@Resource
	protected WebServiceContext wsContext;

	@Override
	public Source invoke(Source request) {		
		MessageContext msg_cxt = wsContext.getMessageContext();
		String httpMethod = (String) msg_cxt
				.get(MessageContext.HTTP_REQUEST_METHOD);
		//System.out.println("Http Method : " + httpMethod);
		if (httpMethod.equalsIgnoreCase("GET")) {
			 return doGet(msg_cxt);
		}
		return null;
	}

	private Source doGet(MessageContext msg_cxt) {
		String query_string = (String) msg_cxt.get(MessageContext.QUERY_STRING);
		StringBuffer text=new StringBuffer("");		
		String cityName=query_string.split("=")[1];
		try {
			URL url = new URL(
					"http://api.openweathermap.org/data/2.5/weather?q="+cityName+"&mode=xml");
			HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
			urlConnection.setRequestMethod("GET");		
			urlConnection.connect();

			BufferedReader bReader = new BufferedReader(new InputStreamReader(
					urlConnection.getInputStream()));
			String line = null;			

			while ((line = bReader.readLine()) != null) {							
				text=text.append(line);			
			}		

		} catch (Exception e) {
			e.printStackTrace(); 
		}
		return new StreamSource( new StringReader(text.toString()) );

	}	

}

Let’s drill down into the code line by line.

We have annotated the class with @WebServiceProvider, which means exchanged messages will be XML document. Weather service implements the generic provider interface rather than a customized Service Endpoint Interface (SEI).

The annotation @ServiceMode with value MESSAGE indicates that the service wants access to the entire message (For example,  HTTP headers and body). BindingType annotation declares that this service deals with raw XML over HTTP instead of SOAP over HTTP.

In the invoke method, we first retrieve the MessageContext from WebServiceContext. We then check the request method for the incoming request and if it is GET, we call it the doGet method passing the MessageContext. Then we extract the value of the city parameter from the query string and call it the OpenWeatherMap service which returns an XML document.

Note: We store the returned XML document in a StringBuffer.

How to get a source type from a StringBuffer

return new StreamSource( new StringReader(text.toString()) );

The above line will return a StreamSource from a StringBuffer instance text.

We are all done with the weather service. Now let’s publish the service.

WeatherPublisher.java

import javax.xml.ws.Endpoint;

public class WeatherPublisher {

	public static void main(String[] args) {
		System.out.println("Publishing Weather Service");
		Endpoint.publish("http://127.0.0.1:8700/weather",new RESTfulWeather());

	}
}

Accessing the Web Service

Let’s access the weather service giving it a city name. We can check the weather for Seoul

access-restful-webservice

We get an XML document as the response with information on latitude, longitude, temperature, humidity , wind, clouds etc.

As our weather service is up and running, it’s time to write a client which will use this service.

Writing a client against weather service

Since weather service returns an XML document as a response, any client that uses the web service will have to parse that XML document. Below is the Java client written against the weather service

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class WeatherClient {

	public static void main(String[] args) throws IOException, SAXException, ParserConfigurationException {
		// GET requests
		URL url=new URL("http://127.0.0.1:8700/weather?city=seoul");		
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("GET");
		conn.connect();		

		String xml = "";
		BufferedReader reader =	new BufferedReader(new InputStreamReader(conn.getInputStream()));
		String next = null;
		while ((next = reader.readLine()) != null)
		xml += next;		

		SAXParser parser =SAXParserFactory.newInstance().newSAXParser();
		SaxParserHandler handler=new SaxParserHandler();
		parser.parse(new ByteArrayInputStream(xml.getBytes()),handler);
		System.out.println("-------------------------------------------------------------------");
		System.out.println(handler.city+" weather update on "+handler.lastUpdate);
		System.out.println("-------------------------------------------------------------------");
		System.out.println("City : "+handler.city);
		System.out.println("Latitude : "+handler.latitude);
		System.out.println("Longitude : "+handler.longitude);
		System.out.println("Mininum Temperature (Celsius) : "+handler.minTemperature);
		System.out.println("Maximum Temperature (Celsius) : "+handler.maxTemperature);
		System.out.println("Wind : "+handler.wind);
		System.out.println("Clouds : "+handler.clouds);
	}
}	

    class SaxParserHandler extends DefaultHandler{
		 String city;
		 String latitude;
		 String longitude;
		 float minTemperature;
		 float maxTemperature;
		 String wind;
		 String clouds;
		 String lastUpdate;

		 public void startElement(String namespaceURI,String localName,String qname,Attributes attributes){		

				if(qname.equals("city")){
					city=attributes.getValue("name");					
				}else if(qname.equals("coord")){
					latitude=attributes.getValue("lat");					
					longitude=attributes.getValue("lon");
				}
				else if(qname.equals("temperature")){
					String minKelvin=attributes.getValue("min");
					minTemperature=Math.round(Float.parseFloat(minKelvin)) - 272;
					String maxKelvin=attributes.getValue("max");					
					maxTemperature=Math.round(Float.parseFloat(maxKelvin)) - 272;
				}
				else if(qname.equals("speed")){
					wind=attributes.getValue("name");										
				}
				else if(qname.equals("clouds")){
					clouds=attributes.getValue("name");									
				}	
				else if(qname.equals("lastupdate")){
					lastUpdate=attributes.getValue("value").split("T")[0];									
				}
			}		

      }

Note : WeatherClient uses SAX parser to parse the response from the service. If you are not very familiar with parsing XML files in Java, here is an article that explains how to parse XML files using SAX parser.

You can also use a DOM or StAX parser for parsing the XML document.

Output

Below is the output that you will get on executing the WeatherClient class

rest-output

A Word of Caution :

Let’s iterate through what we said earlier, JAX-WS is not the right choice for implementing real-world RESTful services. For writing real-world RESTful web services JAX-RS, Restlet or Spring framework is the way to go.

Curious to try the code yourself? Download the code.

Getting java.net.BindException: If you are trying to publish the web service on a port that is already in use you will get java.net.BindException

Caused by: java.net.BindException: Address already in use: bind

To get rid of the exception just use some other port for publishing the web service.

Got a question for us? Please mention it in the comments section and we will get back to you.

Related Posts:

Get Started with Java/J2EE

Creating an Online Quiz Application using JSP Servlet

Parsing XML files using SAX Parser

Share on
Comments
1 Comment
  • Chanakya Volam

    Hi can you explain server connections

24 X 7 Customer Support X

  • us flag 1-800-275-9730 (Toll Free)
  • india flag +91 88808 62004