Spring WS

I had a small requirement at work to expose some data from database as web service. Spring provides awesome supports for contract-first web service. I just delved into it and created a very simple web service and exposed it.First I will show you how to develop a simple web service in the first part.In the second part I will show how to test it.

PART I

So, here goes the simple project.

Step 1. The project structure.

The project structure is simple as I used in all the projects. Its a maven project with the webapp archtype. Here is a overview of the structure.

PS_1_WS

Step 2. Lets update our pom.xml.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.anupam</groupId>
   <artifactId>SVMSReporter</artifactId>
   <packaging>war</packaging>
   <version>0.0.1-SNAPSHOT</version>
   <name>SVMSReporter Maven Webapp</name>
   <url>http://maven.apache.org</url>
   <dependencies>
      <!-- Spring -->
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-core</artifactId>
         <version>4.2.2.RELEASE</version>
      </dependency>
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-beans</artifactId>
         <version>4.2.2.RELEASE</version>
      </dependency>
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <version>4.2.2.RELEASE</version>
      </dependency>
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
         <version>4.2.2.RELEASE</version>
      </dependency>
      <dependency>
         <groupId>org.springframework.ws</groupId>
         <artifactId>spring-ws-core</artifactId>
         <version>2.2.3.RELEASE</version>
      </dependency>
      <!-- Connector -->
      <dependency>
         <groupId>net.sourceforge.jtds</groupId>
         <artifactId>jtds</artifactId>
         <version>1.3.1</version>
      </dependency>
      <dependency>
         <groupId>commons-dbcp</groupId>
         <artifactId>commons-dbcp</artifactId>
         <version>1.2.2</version>
      </dependency>
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>5.1.37</version>
      </dependency>
      <!-- ServletContext -->
      <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>javax.servlet-api</artifactId>
         <version>4.0.0-b01</version>
      </dependency>
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context-support</artifactId>
         <version>4.2.2.RELEASE</version>
      </dependency>
      <!-- Mail -->
      <dependency>
         <groupId>javax.mail</groupId>
         <artifactId>mail</artifactId>
         <version>1.5.0-b01</version>
      </dependency>
      <!-- Logging -->
      <dependency>
         <groupId>log4j</groupId>
         <artifactId>log4j</artifactId>
         <version>1.2.17</version>
      </dependency>
      <dependency>
         <groupId>wsdl4j</groupId>
         <artifactId>wsdl4j</artifactId>
         <version>1.6.3</version>
      </dependency>
      <!-- Schema -->
      <dependency>
         <groupId>org.apache.ws.xmlschema</groupId>
         <artifactId>xmlschema-core</artifactId>
         <version>2.0.1</version>
      </dependency>
      <!-- POI -->
      <dependency>
         <groupId>org.apache.poi</groupId>
         <artifactId>poi</artifactId>
         <version>3.11</version>
      </dependency>
      <!-- Jackson -->
      <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-core</artifactId>
         <version>2.6.3</version>
      </dependency>
      <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-databind</artifactId>
         <version>2.6.3</version>
      </dependency>
      <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-annotations</artifactId>
         <version>2.6.3</version>
      </dependency>
   </dependencies>
   <build>
      <finalName>webutility</finalName>
      <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
            <configuration>
               <source>1.8</source>
               <target>1.8</target>
            </configuration>
         </plugin>
         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxb2-maven-plugin</artifactId>
            <version>1.5</version>
            <executions>
               <execution>
                  <id>xjc</id>
                  <goals>
                     <goal>xjc</goal>
                  </goals>
               </execution>
            </executions>
            <configuration>
               <schemaDirectory>${project.basedir}/src/main/webapp/xsd/</schemaDirectory>
               <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
               <clearOutputDir>false</clearOutputDir>
               <packageName>com.anupam.inventory</packageName>
            </configuration>
         </plugin>
      </plugins>
   </build>
</project>

 Step 3. Defining contract.

Okay lets define our contract i.e create our xsd and expose it as wsdl.My simple schema is located at Reporter/src/main/webapp/xsd/InventoryReport.xsd

<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://www.gocodeblog.wordpress.com"
   elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://www.gocodeblog.wordpress.com">
   <element name="getRequest">
      <complexType>
         <sequence>
            <element name="locationID" type="int"></element>
         </sequence>
      </complexType>
   </element>
   <element name="getResponse">
      <complexType>
         <sequence>
            <element name="model" maxOccurs="unbounded" minOccurs="0">
               <complexType>
                  <sequence>
                     <element name="count" type="int"></element>
                     <element name="brand" type="string"></element>
                     <element name="model" type="string"></element>
                     <element name="barcode" type="string"></element>
                     <element name="location" type="string"></element>
                  </sequence>
               </complexType>
            </element>
         </sequence>
      </complexType>
   </element>
</schema>

Step 4. Lets configure our web.xml
Remember that we are using MessageDispatcherServlet to direct the web service requests. The DispatcherServlet can also be used to direct the web service requests. Just need some small configurations. Will post it sometimes later.

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
   <!-- Dispatcher -->
   <servlet>
      <servlet-name>myspring</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet
      </servlet-class>
      <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>/WEB-INF/spring/app-config.xml,/WEB-INF/spring/hibernate-config.xml
         </param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
      <servlet-name>myspring</servlet-name>
      <url-pattern>/report/*</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
      <servlet-name>myspring</servlet-name>
      <url-pattern>/result/*</url-pattern>
   </servlet-mapping>
   <!-- Web Service -->
   <servlet>
      <servlet-name>spring-ws</servlet-name>
      <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet
      </servlet-class>
      <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>/WEB-INF/spring/app-config.xml,/WEB-INF/spring/hibernate-config.xml</param-value>
      </init-param>
      <init-param>
         <param-name>transformWsdlLocations</param-name>
         <param-value>true</param-value>
      </init-param>
   </servlet>
   <servlet-mapping>
      <servlet-name>spring-ws</servlet-name>
      <url-pattern>/endpoints/*</url-pattern>
   </servlet-mapping>
   <!-- Welcome files -->
   <welcome-file-list>
      <welcome-file>index.html</welcome-file>
   </welcome-file-list>
</web-app>

Step 5. The spring app configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
   xmlns:task="http://www.springframework.org/schema/task" xmlns:sws="http://www.springframework.org/schema/web-services"
   xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
   http://www.springframework.org/schema/context 
   http://www.springframework.org/schema/context/spring-context-4.2.xsd
   http://www.springframework.org/schema/task
   http://www.springframework.org/schema/task/spring-task-4.2.xsd
   http://www.springframework.org/schema/web-services
   http://www.springframework.org/schema/web-services/web-services-2.0.xsd">
   <context:annotation-config />
   <context:component-scan base-package="com.anupam.*" />
   <!-- Task -->
   <task:scheduled-tasks scheduler="myScheduler">
      <task:scheduled ref="reporterService" method="sendReport"
         cron="0 0 15 * * ?" />
   </task:scheduled-tasks>
   <task:scheduler id="myScheduler" />
   <!-- Email -->
   <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
      <property name="host">
         <value>abc.xyz</value>
      </property>
      <property name="protocol">
         <value>smtp</value>
      </property>
      <property name="javaMailProperties">
         <props>
            <prop key="mail.smtp.auth">false</prop>
         </props>
      </property>
   </bean>
   <!-- Web Service -->
   <sws:annotation-driven />
   <sws:dynamic-wsdl id="inventory" portTypeName="inventoryService"
      locationUri="/endpoints">
      <sws:xsd location="/xsd/InventoryReport.xsd" />
   </sws:dynamic-wsdl>
</beans>

Remember that the scans for the @Endpoint stereotypes of Spring.And the id=”inventory” is used for wsdl browsing.In , we are providing the location of our schema. This location is relative to WEB-INF.

Step 6. The Endpoint class
com.anupam.report.InventoryService

package com.anupam.endpoint;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

import com.anupam.dao.InventoryDAO;
import com.anupam.inventory.GetRequest;
import com.anupam.inventory.GetResponse;

/*
 * Web Service Endpoint.
 */
@Endpoint
public class InventoryWS
{

    private static final String NS_INVENTORY_SERVICE = "http://www.gocodeblog.wordpress.com";

    @Autowired
    private InventoryDAO inventoryDAO;

    @PayloadRoot(namespace = NS_INVENTORY_SERVICE, localPart = "getRequest")
    @ResponsePayload
    public GetResponse getReport(
	    @RequestPayload GetRequest req) throws Exception
    {
	System.out.println("Request received...");
	List<GetResponse.Model> list = inventoryDAO.getInventoryWS(req.getLocationID());
	GetResponse res = new GetResponse();
	res.getModel().addAll(list);
	return res;
    }
}

Now in this class the namespace NS_INVENTORY_SERVICE must match with the target namespace we declared in our schema. It can be anything you want.The classes GetRequest and GetResponse are generated at the time of maven build. In our pom.xml, there is a xjc plugin configured. The plugin will create the xml binding classes based on the schemas provided for them.I am using a dummy method to generate some data.

Step 7. Generating wsdl.
After successfully publishing the webservice(I am using Tomcat), just browse the link,http://localhost:8080/Reporter/endpoints/inventory.wsdl and you should see the wsdl generated. The key “inventory” is what we configured in the app-config.xml

<sws:annotation-driven />
<sws:dynamic-wsdl id="inventory" portTypeName="inventoryService"
   locationUri="/endpoints">
   <sws:xsd location="/xsd/InventoryReport.xsd" />
</sws:dynamic-wsdl>

PART II
Now you are done.Lets test the web service.Java already has built in tools like wsimport to create the JAX-WS client classes from a running web service. So,

Step 1.
Lets create a simple java class and nothing else inside.

Step 2. Open a command prompt and navigate to the src of your currently created java project.Execute the following command,

wsimport -keep -verbose http://localhost:8080/Reporter/endpoints/inventory.wsdl

After successful execution, it will create all necessary classes. In my case it created the following classes,
client

Step 3. Create the client.
Now, just create the client as follows,

package runner;

import com.wordpress.gocodeblog.GetRequest;
import com.wordpress.gocodeblog.GetResponse;
import com.wordpress.gocodeblog.InventoryService;
import com.wordpress.gocodeblog.InventoryServiceService;

public class Run {

public static void main(String[] args) {
InventoryServiceService service=new InventoryServiceService();
InventoryService s=service.getInventoryServiceSoap11();

GetRequest req=new GetRequest();
req.setLocationID(11);
GetResponse res= s.get(req);
for(GetResponse.Model model:res.getModel())
{
System.out.println(model.getBarcode()+&amp;quot;-&amp;quot;+model.getBrand()+&amp;quot;-&amp;quot;+model.getCount()+&amp;quot;-&amp;quot;+model.getLocation());
}
}

}

After execution, you should see the proper results.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s