Metro JAX-WS: SOAP based Web Service using Bottom-Up approach

In this article, we will learn and implement a JAX-WS based SOAP Web Service using Metro (JAX-WS + WSIT = Project Tango) Reference Implementation

 

Basically, we got two options while developing/implementation of SOAP Web Service

  • Top-Down approach or Contract first
  • Bottom-UP approach or Code first

We will concentrate on the second approach i.e.; Bottom-UP approach. Often developer community finds developing SOAP based Web Service using bottom-up lot easier comparing the former contract based approach

It’s considered a standard approach to design WSDL contract document involving architects, designer and governance team in a large enterprise application, where possibly there could be multiple interactions amongst exposed services. Anyways, it is purely design choice

In this approach, java implementation class is coded first and using maven jaxws plugin (wsgen/java2ws goals) we can generate required artifacts viz.; XML Schema & WSDL and their corresponding POJO

Note: For simple use case, we can use command line interface to generate java artifacts

Technology Used

  • Java 1.7
  • Eclipse Luna IDE
  • jaxws-rt-2.2.8
  • webservices-rt-2.3
  • Apache Maven 3.2.1
  • Apache Tomcat-8.0.15
  • Oracle Weblogic server 12c

Mavenize or download required jars

Add jaxws-rt & webservices-rt dependencies to pom.xml

<!-- properties -->
<properties>
	<sun.jaxws.version>2.2.8</sun.jaxws.version>
	<metro.jaxws.version>2.3</metro.jaxws.version>
	<jaxws.scope>compile</jaxws.scope> <!-- provided(weblogic) / compile(tomcat) -->
	<compileSource>1.7</compileSource>
	<maven.compiler.source>1.7</maven.compiler.source>
	<maven.compiler.target>1.7</maven.compiler.target>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>

	<!-- jax-ws runtime from sun -->
	<dependency>
		<groupId>com.sun.xml.ws</groupId>
		<artifactId>jaxws-rt</artifactId>
		<version>${sun.jaxws.version}</version>
		<scope>${jaxws.scope}</scope>
	</dependency>

	<!-- jax-ws runtime from glassfish Metro - project tango -->
	<dependency>
		<groupId>org.glassfish.metro</groupId>
		<artifactId>webservices-rt</artifactId>
		<version>${metro.jaxws.version}</version>
		<scope>${jaxws.scope}</scope>
	</dependency>

</dependencies>

Folks who aren’t familiar with Maven concepts or don’t require maven for their project, can download the below jars individually from the central repository or maven repository or maven2 and include them in the classpath

Steps to generate required artifacts from SEI (interface/class)

  • code java interface and class implementing this interface
  • configure maven plugins (wsgen/java2ws goal) in pom.xml with correct and complete path of SEI i.e.; service endpoint interface
  • Run maven command “mvn generate-sources” from project’s context-root

1_Metro-JAX-WS-Bottom-UP_java_interface_class

Let us understand above steps in more detail

Code Java Interface/Class

 

Book Service Interface

This interface defines a single method called “getBookNameBasedOnISBN” which inputs string argument and returns a single string response based on the request ISBN number

IBookService.java

package com.jaxws.series.bottom.up.approach.service;

import javax.jws.WebService;

@WebService(name="IBookService")
public interface IBookService {

	public String getBookNameBasedOnISBN(String isbnNumber);
}

Book Service Implementation

This service class implements above interface and provides concrete implementation, which on valid ISBN request number returns a book name otherwise returns a simple string with invalid message i.e.; “INVALID_ISBN_NUMBER

BookServiceImpl.java

package com.jaxws.series.bottom.up.approach.service;

import javax.jws.WebService;

@WebService(serviceName="BookService", endpointInterface="com.jaxws.series.bottom.up.approach.service.IBookService", portName="BookServicePort")
public class BookServiceImpl implements IBookService {

	public String getBookNameBasedOnISBN(String isbnNumber) {

		if(isbnNumber.equalsIgnoreCase("ISBN-2134")){
			return "Microbiology";
		}
		return "Invalid_ISBN_Number";
	}
}

Configure maven plugin in pom.xml (wsgen goal)

This plugin which defines wsgen goal from jaxws-maven-plugin generates xsd/wsdl and their corresponding POJO artifacts from SEI i.e.; Service Endpoint Interface under the folder location specified with attributes viz.; resourceDestDir, sourceDestDir

<!-- plugin 3-maven wsgen goal -->
<plugin>
	<groupId>org.jvnet.jax-ws-commons</groupId>
	<artifactId>jaxws-maven-plugin</artifactId>
	<version>2.3</version>
	<executions>
		<execution>
			<id>basic</id>
			<phase>generate-sources</phase>
			<goals>
				<goal>wsgen</goal>
			</goals>
		</execution>
	</executions>
	<configuration>
		<keep>true</keep>
		<sei>com.jaxws.series.bottom.up.approach.service.BookServiceImpl</sei>
		<genWsdl>true</genWsdl>
		<resourceDestDir>${basedir}\generated\resources\wsdl</resourceDestDir>
		<sourceDestDir>${basedir}\generated\java\source</sourceDestDir>
		<verbose>true</verbose>
		<target>2.1</target>
	</configuration>
</plugin>

Run “mvn generate-sources”

Look at the generated artifacts in the generated folder

After running above maven command, you will get to see below generated files

2_Metro-JAX-WS-Bottom-UP_generated_artifacts

  • BookService.wsdl
  • BookService_schema1.xsd
  • GetBookNameBasedOnISBN.java
  • GetBookNameBasedOnISBNResponse.java

BookService.wsdl

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is
	JAX-WS RI 2.2.8 svn-revision#13980. -->
<definitions targetNamespace="http://service.approach.up.bottom.series.jaxws.com/"
	name="BookService" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:tns="http://service.approach.up.bottom.series.jaxws.com/"
	xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns="http://schemas.xmlsoap.org/wsdl/"
	xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
	xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata">

	<types>
		<xsd:schema>
			<xsd:import namespace="http://service.approach.up.bottom.series.jaxws.com/"
				schemaLocation="BookService_schema1.xsd" />
		</xsd:schema>
	</types>

	<message name="getBookNameBasedOnISBN">
		<part name="parameters" element="tns:getBookNameBasedOnISBN" />
	</message>
	<message name="getBookNameBasedOnISBNResponse">
		<part name="parameters" element="tns:getBookNameBasedOnISBNResponse" />
	</message>

	<portType name="IBookService">
		<operation name="getBookNameBasedOnISBN">
			<input
				wsam:Action="http://service.approach.up.bottom.series.jaxws.com/IBookService/getBookNameBasedOnISBNRequest"
				message="tns:getBookNameBasedOnISBN" />
			<output
				wsam:Action="http://service.approach.up.bottom.series.jaxws.com/IBookService/getBookNameBasedOnISBNResponse"
				message="tns:getBookNameBasedOnISBNResponse" />
		</operation>
	</portType>

	<binding name="BookServicePortBinding" type="tns:IBookService">
		<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
			style="document" />
		<operation name="getBookNameBasedOnISBN">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
	</binding>

	<service name="BookService">
		<port name="BookServicePort" binding="tns:BookServicePortBinding">
			<soap:address location="REPLACE_WITH_ACTUAL_URL" />
		</port>
	</service>
</definitions>

BookService_schema1.xsd

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0"
	targetNamespace="http://service.approach.up.bottom.series.jaxws.com/"
	xmlns:tns="http://service.approach.up.bottom.series.jaxws.com/"
	xmlns:xs="http://www.w3.org/2001/XMLSchema">

	<xs:element name="getBookNameBasedOnISBN" type="tns:getBookNameBasedOnISBN" />

	<xs:element name="getBookNameBasedOnISBNResponse" type="tns:getBookNameBasedOnISBNResponse" />

	<xs:complexType name="getBookNameBasedOnISBN">
		<xs:sequence>
			<xs:element name="arg0" type="xs:string" minOccurs="0" />
		</xs:sequence>
	</xs:complexType>

	<xs:complexType name="getBookNameBasedOnISBNResponse">
		<xs:sequence>
			<xs:element name="return" type="xs:string" minOccurs="0" />
		</xs:sequence>
	</xs:complexType>
</xs:schema>

GetBookNameBasedOnISBN.java

package com.jaxws.series.bottom.up.approach.service.jaxws;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name = "getBookNameBasedOnISBN", namespace = "http://service.approach.up.bottom.series.jaxws.com/")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getBookNameBasedOnISBN", namespace = "http://service.approach.up.bottom.series.jaxws.com/")
public class GetBookNameBasedOnISBN {

    @XmlElement(name = "arg0", namespace = "")
    private String arg0;

    /**
     *
     * @return
     *     returns String
     */
    public String getArg0() {
        return this.arg0;
    }

    /**
     *
     * @param arg0
     *     the value for the arg0 property
     */
    public void setArg0(String arg0) {
        this.arg0 = arg0;
    }

}

GetBookNameBasedOnISBNResponse.java

package com.jaxws.series.bottom.up.approach.service.jaxws;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name = "getBookNameBasedOnISBNResponse", namespace = "http://service.approach.up.bottom.series.jaxws.com/")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getBookNameBasedOnISBNResponse", namespace = "http://service.approach.up.bottom.series.jaxws.com/")
public class GetBookNameBasedOnISBNResponse {

    @XmlElement(name = "return", namespace = "")
    private String _return;

    /**
     *
     * @return
     *     returns String
     */
    public String getReturn() {
        return this._return;
    }

    /**
     *
     * @param _return
     *     the value for the _return property
     */
    public void setReturn(String _return) {
        this._return = _return;
    }

}

 

Directory Structure

Before moving on, let us understand the directory/package structure once you create project and/plus after generating java artifacts in Eclipse IDE

Maven has to follow certain directory structure

  • src/test/java –> test related files, mostly JUnit test cases
  • src/main/java –> create java source files under this folder
  • generated/java/source –> generated java source files are placed here
  • generated/resources/wsdl –> generated resources like *.wsdl, *.xsd, etc
  • src/main/resources –> all configuration files placed here
  • src/test/resources –> all test related configuration files placed here
  • Maven Dependencies or Referenced Libraries –> includes jars in the classpath
  • WEB-INF under webapp –> stores web.xml & other configuration files related to web application

Project Structure (Package Explorer view in Eclipse)

3_Metro-JAX-WS-Bottom-UP_project_structure_in_eclipse

Jar Libraries Used in the Project (Maven Dependencies)

4_Metro-JAX-WS-Bottom-UP_jars_in_classpath

Web application

For any web application, entry point is web.xml which describes how the incoming http requests are served / processed. Further, it describes about the global-context and local-context param (i.e.; <context-param> & <init-param>) for loading files particular to project requirements & contains respective listener

With this introduction, we will understand how we configured web.xml & sun-jaxws.xml for Metro JAX-WS SOAP based Web Service

web.xml (the entry point –> under WEB-INF)

This web.xml file describes,

  • Like any JEE web framework register com.sun.xml.ws.transport.http.servlet.WSServlet with servlet container
  • http requests with URL pattern “/services/*” will be sent to the registered servlet called “metro-jaxws-servlet” i.e.; (com.sun.xml.ws.transport.http.servlet.WSServlet)
  • configure web service listener “com.sun.xml.ws.transport.http.servlet.WSServletContextListener
  • configure session timeout in secs using <session-config> tag
  • <welcome-file-list> files under this tag is the start-up page

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">

	<display-name>Metro-JAX-WS-Bottom-UP</display-name>

	<!-- listener for metro jax-ws -->
	<listener>
		<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
	</listener>

	<!-- metro jax-ws servlet -->
	<servlet>
		<servlet-name>metro-jaxws-servlet</servlet-name>
		<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>metro-jaxws-servlet</servlet-name>
		<url-pattern>/services/*</url-pattern>
	</servlet-mapping>

	<!-- session timeout -->
	<session-config>
		<session-timeout>60</session-timeout>
	</session-config>

	<!-- welcome file -->
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>

</web-app>

sun-jaxws.xml (under WEB-INF)

This file basically defines endpoint url-pattern stating/mentioning qualified package name of the implementation (java class)

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="IBookService"
		implementation="com.jaxws.series.bottom.up.approach.service.BookServiceImpl"
		url-pattern="/services/BookService" />
</endpoints>

URL Pattern

Http url for any common web application is http://<server>:<port>/<root-context>/<from_here_application_specific_path>

In our example, we are going to deploy the war into Tomcat 8.0 server so our server and port are localhost and 8080 respectively. Context root is the project name i.e.; Metro-JAX-WS-Bottom-UP. Initial path for this application is http://localhost:8080/Metro-JAX-WS-Bottom-UP

We have configured “/services/*” as url-pattern for the “metro-jaxws-servlet” servlet in web.xml and our business implementation class which is annotated with @WebService annotation at class-level got a attribute serviceName with value BookService defining endpoint URL as follows

http://localhost:8080/Metro-JAX-WS-Bottom-UP/services/BookService?wsdl

That’s all with coding and configuration part, now let us move on to deployment and testing

 

Deployment: We can either deploy on it Apache Tomcat server or Oracle weblogic application server. Steps for deployment on both these servers are detailed below

Server 1: Apache Tomcat-8.0.15 Deployment

  • Run maven command to build the war: mvn clean install (use command prompt or integrated maven in eclipse IDE)
  • Copy(ctrl+c) the war file from the target folder
  • Paste(ctrl+v) it into apache tomcat (webapps folder)
  • Start the tomcat server (Tomcat_Home\bin\startup.bat)

Server 2: Oracle Weblogic server 12.1.1.0 Deployment

  • Change the scope of jax-ws in pom.xml
    <jaxws.scope>provided</jaxws.scope><!-- provided(weblogic)/compile(tomcat)-->
    
  • Run maven command to build the war: mvn clean install (use command prompt or integrated maven in eclipse IDE)
  • Once you see “BUILD SUCCESS” after running maven command, keep the war file ready to be deployed
  • Start weblogic 12c application server and hit the URL http://localhost:7001/console in any of the latest web browser and enter username/password you configured while setting up weblogic 12c server
  • Go to Deployments –> click install button –> browse through the location where you have kept your war file –> say Next –> say Next –> Finish

For Oracle WebLogic 12c Installation steps see here

Test the service !!

Testing

There are many ways to do testing

  • SOAP UI Client
  • Java Client using JDK’s in-built classes like HttpURLConnection
  • Java Client using SOAP API
  • Eclipse’s Web Services Explorer
  • Write your own client for example, Java client using httpcomponents from Apache

We will cover first 2 ways of testing above JAX-WS deployed services

1. SOAP UI Client

Load the endpoint URL in SOAP UI Client, which will pre-populate the request XML based on the operation deployed/exposed using WSDL

For example, http://localhost:8080/Metro-JAX-WS-Bottom-UP/services/BookService?wsdl

Request XML

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
	xmlns:ser="http://service.approach.up.bottom.series.jaxws.com/">
	<soapenv:Header />
	<soapenv:Body>
		<ser:getBookNameBasedOnISBN>
			<!--Optional: -->
			<arg0>ISBN-2134</arg0>
		</ser:getBookNameBasedOnISBN>
	</soapenv:Body>
</soapenv:Envelope>

 

Response.XML

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Body>
		<ns2:getBookNameBasedOnISBNResponse
			xmlns:ns2="http://service.approach.up.bottom.series.jaxws.com/">
			<return>Microbiology</return>
		</ns2:getBookNameBasedOnISBNResponse>
	</S:Body>
</S:Envelope>

5_Metro-JAX-WS-Bottom-UP_Soap_ui_testing

 

2. Java client

For this java client to work/execute, we don’t need to add any extra jars or any new dependency in pom.xml as these classes comes shipped along with JDK. Observe, import statements closely for this client

Note: Request XML pattern formed taking help from pre-populated request XML from SOAP UI client as explained above

TestBookService.java

package test.jaxws.series.bottom.up.approach.service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;

public class TestBookService {

	/**
	 * wsimport - JAX-WS top-down web service approach
	 * main() method to test/start soap web service
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {

		String httpRequestURL = "http://localhost:8080/Metro-JAX-WS-Bottom-UP/services/BookService?WSDL";
		String requestXmlParam = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ser=\"http://service.approach.up.bottom.series.jaxws.com/\">"
				+ 					" <soapenv:Header/>"
				+ 					"<soapenv:Body>"
				+						"<ser:getBookNameBasedOnISBN>"
				+    						"<arg0>ISBN-2134</arg0>"
				+  						" </ser:getBookNameBasedOnISBN>"
				+					"</soapenv:Body>"
				+  					"</soapenv:Envelope>";
		String responseString = testBookService(httpRequestURL, requestXmlParam);
		System.out.println("Response String : " + responseString);
	}

	/**
	 * This method uses HttpURLConnection to invoke exposed SOAP web service and returns the response string to the calling client
	 *
	 * @param httpRequestURL
	 * @param requestXmlParam
	 * @return responseXML
	 * @throws IOException
	 */
	public static String testBookService(String httpRequestURL, String requestXmlParam) throws IOException {

		// local variables
		URL url = null;
		HttpURLConnection httpURLConnection = null;
		OutputStreamWriter outputStreamWriter = null;
		String responseMessageFromServer = null;
		String responseXML = null; 

		try {
			// set basic request parameters
			url = new URL(httpRequestURL);
			httpURLConnection = (HttpURLConnection) url.openConnection();
			httpURLConnection.setDoOutput(true);
			httpURLConnection.setRequestMethod("POST");
			//			httpURLConnection.setRequestProperty("SOAPAction", "");
			httpURLConnection.setRequestProperty("Content-Type", "text/xml");
			httpURLConnection.setRequestProperty("Accept", "application/xml");

			// write request XML to the HTTP request
			outputStreamWriter = new OutputStreamWriter(httpURLConnection.getOutputStream());
			outputStreamWriter.write(requestXmlParam);
			outputStreamWriter.flush();

			System.out.println("Response code: " + httpURLConnection.getResponseCode());

			if (httpURLConnection.getResponseCode() == 200) {

				responseMessageFromServer = httpURLConnection.getResponseMessage();
				System.out.println("ResponseMessageFromServer: " + responseMessageFromServer);
				responseXML = getResponseXML(httpURLConnection);
			}
		}
		catch (IOException ioex) {
			ioex.printStackTrace();
			throw ioex;
		}
		finally{
			// finally close all operations
			outputStreamWriter.close();
			httpURLConnection.disconnect();
		}
		return responseXML;
	}

	/**
	 * This method is used to get response XML from the HTTP POST request
	 *
	 * @param httpURLConnection
	 * @return stringBuffer.toString()
	 * @throws IOException
	 */
	private static String getResponseXML(HttpURLConnection httpURLConnection) throws IOException {

		// local variables
		StringBuffer stringBuffer = new StringBuffer();
		BufferedReader bufferedReader = null;
		InputStreamReader inputStreamReader = null;
		String readSingleLine = null;

		try{
			// read the response stream AND buffer the result into a StringBuffer
			inputStreamReader = new InputStreamReader(httpURLConnection.getInputStream());
			bufferedReader = new BufferedReader(inputStreamReader);

			// reading the XML response content line BY line
			while ((readSingleLine = bufferedReader.readLine()) != null) {
				stringBuffer.append(readSingleLine);
			}
		}
		catch (IOException ioex) {
			ioex.printStackTrace();
			throw ioex;
		}
		finally{
			// finally close all operations
			bufferedReader.close();
			httpURLConnection.disconnect();
		}
		return stringBuffer.toString();
	}
}

Output in console

Response code: 200
ResponseMessageFromServer: OK
Response String : 
<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Body>
		<ns2:getBookNameBasedOnISBNResponse
			xmlns:ns2="http://service.approach.up.bottom.series.jaxws.com/">
			<return>Microbiology</return>
		</ns2:getBookNameBasedOnISBNResponse>
	</S:Body>
</S:Envelope>

Conclusion: Thus, we have implemented & understood SOAP based Web Service implementation using bottom-up approach

In the next article, we will integrate spring framework with JAX-WS based SOAP Web Service either using top-down or bottom-up approach

Download project

Metro-JAX-WS-Bottom-UP (10kB)

 

Happy Coding !!
Happy Learning !!

Metro JAX-WS: SOAP based Web Service using Top-Down approach + Integrating with Spring framework
Metro JAX-WS - SOAP based Web Service using Top-Down approach