Using StAX with Spring WS

As I have posted before we are using XFire to implement our web service. Unfortunately, we are running into some serious issues with the framework, so we decided to look after another implementation. Since XFire is no longer supported by the community (or at least at a very low profile) and ‘replaced’ by CXF, we decided to have a look at Spring WS. We also have used the Spring Framework throughout our application elsewhere, so this would be a logical choice. After a quick look at the framework it convinced us that this would be the framework to use for our web services. It was easy to set up and well documented.
In this post I will show you how to use StAX to parse and write the SOAP request and response in combination with Spring WS, Maven2 and JBoss.

  1. Setup maven
  2. First thing to do is to setup Maven2. I assume this won’t be a problem, so I only show the settings.xml which I use for my Maven2 installation (you can also check out this post and this one):

    <settings>
      <localRepository>d:/maven2/repo/</localRepository>
      <profiles>
        <profile>
          <id>default-repositories</id>
          <repositories>
            <repository>
              <id>global</id>
              <name>External Mirror of Central Repository</name>
              <url>http://repo1.maven.org/maven2</url>
            </repository>
            <repository>
              <id>repo.maven</id>
              <url>http://repo1.maven.org/maven2-repoclean-java.net</url>
            </repository>
            <repository>
              <id>jboss.maven</id>
              <url>http://repository.jboss.com/maven2/</url>
            </repository>
          </repositories>
          <pluginRepositories>
            <pluginRepository>
              <id>repo1org</id>
              <name>External Plugin Repository</name>
              <url>http://repo1.maven.org/maven2/</url>
            </pluginRepository>
          </pluginRepositories>
        </profile>
        <profile>
            <id>dev-environment</id>
            <activation>
            <activeByDefault>true</activeByDefault>
              <property>
                <name>env</name>
                <value>dev</value>
              </property>
            </activation>
            <properties>
              <appserver.home>d:/java/jboss-4.0.5</appserver.home>
              <server.name>default</server.name>
            </properties>
        </profile>
      </profiles>
      <activeProfiles>
        <activeProfile>dev-environment</activeProfile>
        <activeProfile>default-repositories</activeProfile>
      </activeProfiles>
    </settings>
    
  3. Create a Maven2 web project
  4. You can do this with this command (as described in more detail here):
    mvn archetype:create -DgroupId=net.pascalalma -DartifactId=spring-ws-test
    -DarchetypeArtifactId=maven-archetype-webapp

  5. Create the XML Schema’s (XSD) for the request and response SOAP messages
  6. As an example I take the schema of the Spring WS reference documentation. The request is described there and I have added a response myself. Here is the xsd:

    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:hr="http://www.pascalalma.net/hr/schemas"
            elementFormDefault="qualified"
            targetNamespace="http://www.pascalalma.net/hr/schemas">
        <xs:element name="HolidayRequest">
            <xs:complexType>
                <xs:all>
                    <xs:element name="Holiday" type="hr:HolidayType"/>
                    <xs:element name="Employee" type="hr:EmployeeType"/>
                </xs:all>
            </xs:complexType>
        </xs:element>
        <xs:complexType name="HolidayType">
            <xs:sequence>
                <xs:element name="StartDate" type="xs:date"/>
                <xs:element name="EndDate" type="xs:date"/>
            </xs:sequence>
        </xs:complexType>
        <xs:complexType name="EmployeeType">
            <xs:sequence>
                <xs:element name="Number" type="xs:integer"/>
                <xs:element name="FirstName" type="xs:string"/>
                <xs:element name="LastName" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
        <xs:element name="HolidayResponse" type="hr:Status" />
        <xs:simpleType name="Status">
          <xs:restriction base="xs:string">
            <xs:enumeration value="DENIED"/>
            <xs:enumeration value="PENDING"/>
            <xs:enumeration value="APPROVED"/>
          </xs:restriction>
        </xs:simpleType>
    </xs:schema>
    

    I have stored this schema as ‘hr.xsd’ in the directory ‘$PROJECT_HOME$/WEB-INF/schema’.

  7. Configure the web application
  8. We have to reroot the incoming HTTP request to the Spring WS servlet. TO do this we define the Spring WS servlet in the web.xml, like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
      "http://java.sun.com/dtd/web-app_2_3.dtd">
    <web-app>
    	<!-- === Servlets === -->
    	<servlet>
    		<servlet-name>spring-ws</servlet-name>
    		<display-name>Spring-WS Servlet</display-name>
    		<servlet-class>
    			org.springframework.ws.transport.http.MessageDispatcherServlet
    		</servlet-class>
    		<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>/services/*</url-pattern>
    	</servlet-mapping>
    	<welcome-file-list id="WelcomeFileList">
    		<welcome-file>index.html</welcome-file>
    	</welcome-file-list>
    </web-app>
    
  9. Create model classes
  10. Now it’s time to start some Java coding. First I create the model classes that will represent the Objects as described by the xsd. We will have the following classes:
    The Employee.java
    The Holiday.java
    And the HolidayRequest.java

  11. Create the StAX parser and writer classes
  12. To get from the incoming XML to our Java model classes we make use of StAX parsing and writing. Here are the two classes that do the XML-Object translation and back:
    HolidayRequestReader.java
    HolidayResponseWriter.java

  13. Implement endpoint/webservice
  14. The last Java class we have to make is the Webservice implementation itself. Since we want to use StAX parsing, I simply extend the Spring class ‘AbstractStaxStreamPayloadEndpoint’:

    package net.pascalalma.ws;
    
    import javax.xml.stream.XMLStreamReader;
    import javax.xml.stream.XMLStreamWriter;
    
    import net.pascalalma.model.HolidayRequest;
    import net.pascalalma.xml.HolidayRequestReader;
    import net.pascalalma.xml.HolidayResponseWriter;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.ws.server.endpoint.AbstractStaxStreamPayloadEndpoint;
    
    public class HolidayRequestService extends AbstractStaxStreamPayloadEndpoint
    {
    
      private static final Log LOG = LogFactory.getLog(HolidayRequestService.class);
    
      @Override
      protected void invokeInternal(XMLStreamReader streamreader,
          XMLStreamWriter streamwriter) throws Exception
      {
        try
        {
          HolidayRequest hr = HolidayRequestReader.parseMessage(streamreader);
    
          LOG.info("Result = " + hr);
    
          HolidayResponseWriter.writeResponse(hr, streamwriter);
        } catch (Throwable t)
        {
          LOG.error("Catched exception", t);
        }
      }
    }
    
  15. Setup the SpringWS context
  16. Now that we have all the Java classes in place we have to configure the Spring context that is used by our webservice implementation. In our case it looks like the following XML file that is stored under the name ‘spring-ws-servlet’ in the ‘$PROJECT_HOME$/webapp/WEB-INF’ directory:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
    	<bean
    		class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
    		<property name="mappings">
    			<props>
    				<prop
    					key="{http://www.pascalalma.net/hr/schemas}HolidayRequest">
    					myEndPoint
    				</prop>
    			</props>
    		</property>
    	</bean>
    	<bean id="myEndPoint"
    		class="net.pascalalma.ws.HolidayRequestService" >
    	</bean>
    	<bean id="holiday"
    		class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
    		<property name="schema" ref="schema" />
    		<property name="portTypeName" value="HumanResource" />
    		<property name="locationUri"
    			value="http://localhost:8080/spring-ws-test/services/holidayService/" />
    		<property name="targetNamespace"
    			value="http://www.pascalalma.net/hr/definitions" />
    	</bean>
    	<bean id="schema"
    		class="org.springframework.xml.xsd.SimpleXsdSchema">
    		<property name="xsd" value="/WEB-INF/schemas/hr.xsd" />
    	</bean>
    </beans>
    
  17. Build and deploy to jboss
  18. With this file as the project’s POM file we can simply build, package and deploy our webservice to JBoss with the command in the command prompt:
    mvn clean package jboss:harddeploy

  19. Access wsdl in browser to test
  20. If everything went well and JBoss is started, you can access the dynamically generated WSDL with the following url:
    http://localhost:8080/spring-ws-test/services/holiday.wsdl
    This should give the following WSDL in your browser:

    <wsdl:definitions targetNamespace="http://www.pascalalma.net/hr/definitions">
      <wsdl:types>
        <xs:schema elementFormDefault="qualified" targetNamespace="http://www.pascalalma.net/hr/schemas">
          <xs:element name="HolidayRequest">
            <xs:complexType>
              <xs:all>
                <xs:element name="Holiday" type="hr:HolidayType"/>
                <xs:element name="Employee" type="hr:EmployeeType"/>
              </xs:all>
            </xs:complexType>
          </xs:element>
          <xs:complexType name="HolidayType">
            <xs:sequence>
              <xs:element name="StartDate" type="xs:date"/>
              <xs:element name="EndDate" type="xs:date"/>
            </xs:sequence>
          </xs:complexType>
          <xs:complexType name="EmployeeType">
            <xs:sequence>
              <xs:element name="Number" type="xs:integer"/>
              <xs:element name="FirstName" type="xs:string"/>
              <xs:element name="LastName" type="xs:string"/>
            </xs:sequence>
          </xs:complexType>
          <xs:element name="HolidayResponse" type="hr:Status"/>
          <xs:simpleType name="Status">
            <xs:restriction base="xs:string">
              <xs:enumeration value="DENIED"/>
              <xs:enumeration value="PENDING"/>
              <xs:enumeration value="APPROVED"/>
            </xs:restriction>
          </xs:simpleType>
        </xs:schema>
      </wsdl:types>
      <wsdl:message name="HolidayResponse">
        <wsdl:part element="sch:HolidayResponse" name="HolidayResponse">
        </wsdl:part>
      </wsdl:message>
      <wsdl:message name="HolidayRequest">
        <wsdl:part element="sch:HolidayRequest" name="HolidayRequest">
        </wsdl:part>
      </wsdl:message>
      <wsdl:portType name="HumanResource">
        <wsdl:operation name="Holiday">
          <wsdl:input message="tns:HolidayRequest" name="HolidayRequest">
          </wsdl:input>
          <wsdl:output message="tns:HolidayResponse" name="HolidayResponse">
          </wsdl:output>
        </wsdl:operation>
      </wsdl:portType>
      <wsdl:binding name="HumanResourceSoap11" type="tns:HumanResource">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="Holiday">
          <soap:operation soapAction=""/>
          <wsdl:input name="HolidayRequest">
            <soap:body use="literal"/>
          </wsdl:input>
          <wsdl:output name="HolidayResponse">
            <soap:body use="literal"/>
          </wsdl:output>
        </wsdl:operation>
      </wsdl:binding>
      <wsdl:service name="HumanResourceService">
        <wsdl:port binding="tns:HumanResourceSoap11" name="HumanResourceSoap11">
          <soap:address location="http://localhost:8080/spring-ws-test/services/holidayService/"/>
        </wsdl:port>
      </wsdl:service>
    </wsdl:definitions>
    

    That’s it. You can test your web service with a tool like SoapUI and create test cases based on the generated WSDL like I described here.

About Pascal Alma

Pascal is a senior IT consultant and has been working in IT since 1997. He is monitoring the latest development in new technologies (Mobile, Cloud, Big Data) closely and particularly interested in Java open source tool stacks, cloud related technologies like AWS and mobile development like building iOS apps with Swift. Specialties: Java/JEE/Spring Amazon AWS API/REST Big Data Continuous Delivery Swift/iOS
This entry was posted in Spring Framework, XML/ XSD/ XSLT and tagged , , , , . Bookmark the permalink.

4 Responses to Using StAX with Spring WS

  1. Vipin says:

    Hey,

    This is a wonderful post yar… Thanks a lot. You saved a lot of time.

    BTW i saw a small problem in port binding

    why the name is HumanResourceSoap11 ? how can i remove the 11 from name?

    regards,
    Vipin

  2. Pascal Alma says:

    You can just leave out the 11 in the wsdl:binding, but make sure you do the same in wsdl:port binding so the reffering name matches.

  3. Harish says:

    Hi,

    This is a wonderful post, it was very useful.

    Please can you give me an example to parse the entire soap envelope using StAX? I should access the soap headers included in the soap envelope using StAX for my requirement.

    I found that invokeInternal(XMLStreamReader streamreader,XMLStreamWriter streamwriter)method provides access only to payload (i.e content of the soap body) not to the entire soap envelope.

    Regards,
    K. Harish
    harish.gtec@gmail.com

  4. Pascal Alma says:

    Hi,

    I don’t have an example for the entire soap envelope. See the documentation here, which explains when you can access only the payload and when the complete message.
    I hope this helps.

Comments are closed.