Spring WS

  • Authors : null
5 votes
Dependency :
compile ":springws:0.5.0"

Documentation Source Issues

Summary

Installation

Installation

To install the Spring WS plugin, enter the following on the command line while inside your Grails application's root directory:

$> grails install-plugin springws

Description

Inspired by the Spring Web Services project (see Spring WS), the Spring Web Services (WS) plugin support the provision and consumption of best practice, contract-first web services in your Grails applications. For more on why contract-first web services are considered a best practice, see in why contract first .

Unfortunately, contract first web services have a, largely undeserved, reputation for being a touch tricky to implement, however this plugin intends to make it as easy as possible to employ these best practices.

On the service provision side, a first-class endpoint artefact is introduced including functional tests for endpoints. When consuming services, a WebServiceTemplate is provided, in much the same vein as the Spring WebServiceTemplate, that integrates more naturally with the rest of your Groovy code.

Main contributor: Russ Miles (russ At russmiles Dot com)

Contributor: Ivo Houbrechts (ivo At houbrechts-it Dot be)

Contributor: Okke Tijhuis (o.tijhuis At gmail Dot com)

Contributor: Tareq Abedrabbo (tareq.abedrabbo AT gmail DOT com)

Latest plugin version is 0.5.0.

Dependencies

The plugin contains all of the Spring WS libraries it requires. In addition, the Spring WS plugin also has a dependency on the 'functional-test' plugin when conducting web service functional tests, and the Spring Security Grails plugin if you want to use that option for securing your endpoints.

Getting Started

First of all you have to define a contract and second you have to define an endpoint.

Create the contract first

How to create a contract could be seen in Spring WS tutorial .

The resulting XML Schema (named for example Holiday.xsd) should be stored within your grails project in directory web-app/WEB-INF .

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:hr="http://www.myveryimportantcompany.com/hr/schemas"
    targetNamespace="http://www.myveryimportantcompany.com/hr/schemas" 
    elementFormDefault="qualified" 
    attributeFormDefault="unqualified">
	<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">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="status" type="xs:string"/>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
</xs:schema>

Create an endpoint

The second step, after you've actually defined your contract in XML, is to create an endpoint that will respond to service invocations as defined in that contract. With the Spring WS plugin, this is as simple as entering the following command:

$> grails create-endpoint com.mycompany.hr.Holiday

This command will create a 'HolidayEndpoint' class in the new grails-app/endpoints directory containing the following template code:

package com.mycompany.hr

class HolidayEndpoint {

def static namespace = "http://mycompany.com/schemas"

def invoke = { request, response -> // TODO Deal with the incoming XML document here. null } }

There are three important things to point out about the endpoint implementation. First, the name of the endpoint is important when it comes to which XML document payloads will be routed to this endpoint. The Holiday endpoint will, by default, be invoked for incoming service invocations that contain an element called 'HolidayRequest' as the root element in the payload.

Secondly, the static namespace property is used to classify the root payload element, so this endpoint will respond to 'HolidayRequest' elements in the root of the incoming document payload, as long as it is in the specified namespace as indicated by the URI in the namespace property.

Finally, if an incoming document contains the 'HolidayRequest' element in the root of the payload, in the specified namespace, then the invoke closure is invoked.

The used package is not part of the convention.

The complete HolidayEndpoint might look like the following:

package com.mycompany.hr

class HolidayEndpoint {

def static namespace = "http://www.myveryimportantcompany.com/hr/schemas"

def invoke = { request, response ->

// Using the incoming document println "Holiday Request Received!" println "Start Date: ${request.Holiday.StartDate}" println "End Date: ${request.Holiday.EndDate}"

// Typically you'd invoke some internal business services here

// Preparing the response document response.HolidayResponse(xmlns: namespace) { status('complete') } } }

That's it! Now it's time to test that endpoint...

Creating a functional test for your endpoints

NOTE: As of 0.2.3 the Spring WS plugin does not come packaged with the Functional Test plugin. This was due to problems with packaging the plugin into a war when it depended on plugins that only had 'test' scope. In order to create web service functional tests you'll need to install the Functional Test plugin explicitly by entering:

> grails install-plugin functional-test

Endpoints, like Controllers, typically are thin components that delegate any business logic to Services. This means that although you can unit test an Endpoint, like any other Groovy class, the real value for testing is in functional tests where the endpoint is tested within a full web service stack. To this end, the Spring WS plugin provides an Endpoint Functional Test artefact to make creating this functional tests as easily as possible.

To create a functional test for your endpoint, enter the following command:

$> grails create-endpoint-functional-test com.mycompany.hr.Holiday
This command will create a new Groovy class in the test/functional directory containing the following template:
package com.mycompany.hr
import org.codehaus.groovy.grails.plugins.spring.ws.EndpointFunctionalTestCase

class HolidayEndpointFunctionalTests extends EndpointFunctionalTestCase {

def serviceURL = "http://localhost:8080/your-app/services"

def namespace = "http://mycompany.com/your-org/schemas"

void setUp(){ super.setUp() webServiceTemplate.setDefaultUri(serviceURL) }

void testSOAPDocumentService() {

} }

A functional endpoint test provides an instance of the WebServiceTemplate that you can use to invoke a web service endpoint. The complete functional test, including using MarkupBuilder to create the outgoing 'HolidayRequest' document and XMLSlurper to the examine the response document, is shown below:

The serviceURL has the convention http://localhost:8080/myGrailApp/services. The grails application has been created with grails create-app springws .
package com.mycompany.hr
import org.codehaus.groovy.grails.plugins.spring.ws.EndpointFunctionalTestCase

class HolidayEndpointFunctionalTests extends EndpointFunctionalTestCase {

def serviceURL = "http://localhost:8080/springws/services" def namespace = "http://www.myveryimportantcompany.com/hr/schemas"

void testSOAPDocumentService() {

def response = withEndpointRequest(serviceURL) { HolidayRequest(xmlns: namespace) { Holiday { StartDate("2006-07-03") EndDate("2006-07-07") } Employee { Number("42") FirstName("Russ") LastName("Miles") } } }

def status = response.status assert status == "complete" }

New Feature for 0.2! We've introduced the neater withEndpointRequest feature in 0.2 of the plugin that means even less code in your endpoint functional tests (so now there really is no reason not to implement them).

You will need to change the serviceURL in this example to point to your own Grails application's url. The 'services' URL path is a convention and is the location where all your endpoints reside.

Finally to run the functional test with a full web service stack, enter the following command:

$> grails test-app

Or alternatively, to just run the functional tests enter the following:

$> grails test-app functional

Note: For Grails version 1.2.1 use colon (:) as the end of the command option:

$> grails test-app functional:

Exposing your endpoint's WSDL

New in version 0.2, To expose the HolidayEndpoint's wsdl we can now take advantage of the new configuration support for this. A simple exposure of the WSDL would involve placing Holiday.xsd in 'web-app/WEB-INF' and adding the following code to the application's Config.groovy:

springws.wsdl.Holiday.export

If you wanted more control over the resulting WSDL, then you can take advantage of the deeper configuration options as shown below:

springws {
    wsdl {
        Holiday {
            // In this case the wsdl will be available at <grails.serverURL>/services/hr/v2/Holiday/Holiday-v2.wsdl
            wsdlName= 'Holiday-v2'
            xsds= '/WEB-INF/Holiday.xsd'
            portTypeName = 'HolidayPort'
            serviceName = 'HolidayService'
            locationUri = grails.serverURL + '/services/hr/v2/Holiday'
            targetNamespace = 'http://www.myveryimportantcompany.com/hr/v2/definitions'
        }
    }
}

The targetNamespace is the namespace of the generated WSDL document.

Adding an Endpoint Interceptor

New in version 0.2, Endpoint interceptors apply common functionality across one or more endpoints, similar to filters but for specific XML messaging endpoints. Since there was so much similarity to filters we implemented a similar approach. To add a single endpoint interceptor that outputs incoming and outgoing message information to the standard output stream you simply add an EndpointInterceptors.groovy file into the grails-app/endpoints directory with the following content:

class EndpointInterceptors {
  def interceptors = {
       stdout(endpoint:'*') {
           handleRequest = {messageContext, endpoint ->
              println "stdout:handleRequest"
              return true
           }

handleResponse = {messageContext, endpoint -> println "stdout:handleResponse" return true }

handleFault = {messageContext, endpoint -> println "stdout:handleFault" return true } } } }

Using predefined interceptors is also easy. First define the interceptor in 'resources.groovy':
holidayValidator(org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor){
    schema= '/WEB-INF/Holiday.xsd'
    validateRequest= true
    validateResponse= true
}
Then use it in your interceptors definition:
class HolidayInterceptors {
  def holidayValidator

def interceptors = { validation(endpoint:'holiday', interceptors:[holidayValidator]) { //optional handleRequest, handleResponse and handleFault } } }

Customizing the expected payload root element

New in version 0.2.2. By convention, an endpoint is routed all documents that have a root payload element that marches the endpoint's name, without the Endpoint bit, and in the indicated namespace. As of version 0.2.2 you can now override this default behaviour an indicate a specific element name for the incoming request document. This is especially useful when you are working with a schema that has been in existence prior to your endpoint implementation, or perhaps is a public shared schema that you simply cannot change.

To override the default endpoint payload mapping strategy, all you have to do is provide the requestElement static property that contains the name of the element you expect in the root of the documents that you want to route to your endpoint:

class CustomRequestElementEndpoint {

def static namespace = "http://www.myveryimportantcompany.com/hr/schemas"

// Rather than using the default strategy, which // would map incoming documents with a root payload of // 'CustomRequestElement' elements according to the endpoint name, // we explicitly specify that we're interested in the payload being // a 'Vacation' element, in the above indicated namespace. def static requestElement= "Vacation"

def invoke = { request, response ->

// Using the incoming document println "Vacation Request Received!" println "Start Date: ${request.Holiday.StartDate}" println "End Date: ${request.Holiday.EndDate}"

// Typically you'd invoke some internal business services here

// Preparing the response document response.VacationResponse(xmlns: namespace) { status('complete') { } } } }

Securing your endpoints

WS-Security is the standard mechanism for providing secure web services. New in release 0.5.0 is the first level of support for WS-Security, which provides a working implementation that can rely on simple keystores or Spring Security for authorization and authentication.

Rather than repeat the description of how to use these features, they are described in depth by the key contributor of this feature, Tareq Abedrabbo, on his blog.

A massive thanks again to Tareq for this contribution! Tareq and Russ are also planning to do a talk on secure SOAPful web services at the upcoming Groovy and Grails eXchange in London on the 9th and 10th of December.

Version History

v. 0.5.0
  • Added support for creating and testing secure web services that conform to the WS-Security standard
Note: Special thanks to Tareq Abedrabbo who has been an absolutely rockstar for implementing the new WS-Security support in this release.

v. 0.2.3

  • Fixed bug with Functional Test plugin dependency corrupting war generation (see GRAILSPLUGINS-1225)
v. 0.2.2
  • Added configuration option to override default Endpoint-name-based strategy for mapping incoming XML payloads to endpoints
Note: Special thanks to Ivo Houbrechts for the implementation of the requestElement endpoint configuration parameter

v. 0.2.1

  • Fixed concurrency issue in DefaultEndpointAdapter (see GRAILSPLUGINS-1204)
  • Upgraded to Spring Web Services 1.5.7 (see GRAILSPLUGINS-1208)
Note: Special thanks to Okke Tijhuis for all the contributions to this release

v. 0.2

  • added WSDL generation
  • added endpoint interceptors
  • endpoints and interceptors now support DI and are autowired by name
  • 'Groovier' endpoint functional test case implementation
Note: A massive thanks to Ivo Houbrechts (endpoint interceptors and WSDL generation) and Graeme Rocher (ideas for tightening the functional test case) for all their contributions to this release.

v. 0.1

  • added Endpoint artefact for providing web services
  • added EndpointFunctionalTest artefact for testing web service endpoints
  • Provided Groovy WebServiceTemplate for consuming web services