Show Navigation

Grails & SOAP

Learn how to consume a SOAP endpoint from a Grails Application

Authors: Sergio del Amo

Grails Version: 4.0.1

1 Help with Grails

Grails Training - Developed and delivered by the folks who created and actively maintain the Grails framework!.

2 Getting Started

In this guide you are going to consume a SOAP webservice from a Grails Application.

2.1 What you will need

To complete this guide, you will need the following:

  • Some time on your hands

  • A decent text editor or IDE

  • JDK 1.8 or greater installed with JAVA_HOME configured appropriately

2.2 How to complete the guide

To get started do the following:


The Grails guides repositories contain two folders:

  • initial Initial project. Often a simple Grails app with some additional code to give you a head-start.

  • complete A completed example. It is the result of working through the steps presented by the guide and applying those changes to the initial folder.

To complete the guide, go to the initial folder

  • cd into grails-guides/grails-soap/initial

and follow the instructions in the next sections.

You can go right to the completed example if you cd into grails-guides/grails-soap/complete

3 Writing the Application

VIES (VAT Information Exchange System) is an electronic mean of validating VAT-identification numbers of economic operators registered in the European Union for cross border transactions on goods or services.

For example, if you create an e-commerce Web application in the European union you would need to check the validity of the purchaser’s VAT Number in order to create a proper invoice.

To automate the validation checks, the EU does not offer a REST endpoint but a SOAP service. Its WSDL file can be obtained here.

SOAP (originally Simple Object Access Protocol) is a protocol specification for exchanging structured information in the implementation of web services in computer networks. Its purpose is to induce extensibility, neutrality and independence

3.1 SOAP library

To consume the SOAP Web Service we use groovy-wslite. A library for Groovy that provides no-frills SOAP and REST webservice clients.

Add wslite dependency:

compile 'com.github.groovy-wslite:groovy-wslite:1.1.3'

We encapsulate the SOAP code in Grails service:

package demo

import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import wslite.soap.SOAPClient
import wslite.soap.SOAPResponse

class VatService {
    String url = ''
    SOAPClient client = new SOAPClient("${url}.wsdl")

    Boolean validateVat(String memberStateCode, String vatNumberCode) {
        SOAPResponse response = client.send(SOAPAction: url) {
            body('xmlns': '') {
                checkVat {
        response.checkVatResponse.valid.text() == 'true'

We test it using the Grails Testing Framework

package demo

import spock.lang.Specification
import spock.lang.Unroll

class VatServiceSpec extends Specification implements ServiceUnitTest<VatService> {

    def "#memberState : #vatNumber #unrollDescription"(String memberState, String vatNumber, Boolean expected, String unrollDescription) {

        expected == service.validateVat(memberState, vatNumber)

        memberState | vatNumber   || expected
        'es'        | 'B99286353' || true
        'es'        | 'B19280031' || true
        'es'        | 'XXXXXXXXX' || false

        unrollDescription = expected ? 'is valid' : ' is not valid'

3.2 Countries

It is best to encapsulate the countries which we want to support in the configuration files.

That way, if a country leaves the EU (e.g. UK ) it is an easy change in our app.

Add a list of countries in application.yml

            code: AT
            name: Austria
            code: BE
            name: Belgium
            code: BG
            name: Bulgaria
            code: CY
            name: Cyprus
            code: CZ
            name: Czech Republic
            code: DE
            name: Germany
            code: DK
            name: Denmark
            code: EE
            name: Estonia
            code: EL
            name: Greece
            code: ES
            name: Spain
            code: FI
            name: Finland
            code: FR
            name: France
            code: GB
            name: United Kingdom
            code: HR
            name: Croatia
            code: HU
            name: Hungary
            code: IE
            name: Ireland
            code: IT
            name: Italy
            code: LT
            name: Lithuania
            code: LU
            name: Luxembourg
            code: LV
            name: Latvia
            code: MT
            name: Malta
            code: NL
            name: The Netherlands
            code: PL
            name: Poland
            code: PT
            name: Portugal
            code: RO
            name: Romania
            code: SE
            name: Sweden
            code: SI
            name: Slovenia
            code: SK
            name: Slovakia

Create a POGO:

package demo

import groovy.transform.CompileStatic

class Country {
    String code
    String name

Read the countries in a Grails Service:

package demo

import grails.config.Config
import groovy.transform.CompileStatic

class CountryService implements GrailsConfigurationAware {

    List<Country> countries = [] as List<Country>

    void setConfiguration(Config co) {
        List<Map> l = co.getProperty('eu.countries', List)
        for ( Map m : l ) {
            countries << new Country(name: m.get('name') as String, code: m.get('code') as String)

    List<Country> findAll() {


3.3 Controller and View

The app allow users to submit a form and check if a VAT Number is valid for a particular state.

We encapsulate the request in a command object:

package demo

import grails.validation.Validateable

class VatCommand implements Validateable {

    String code
    String vatNumber

    static constraints = {
        code nullable: false
        vatNumber nullable: false

Create a VatController to handle the request, collaborate with the services previously introduced and generate a response via flash messages or errors.

package demo

import groovy.transform.CompileStatic
import org.springframework.context.MessageSource

class VatController {

    static allowedMethods = [index: 'GET', validate: 'GET']

    VatService vatService

    CountryService countryService

    MessageSource messageSource

    def index() {
        [countries: countryService.findAll()]

    def validate(VatCommand cmd) {

        if ( cmd.hasErrors() ) {
            render view: 'index', model: [cmd: cmd, countries: countryService.findAll()]
        boolean isValid = vatService.validateVat(cmd.code, cmd.vatNumber)
        if ( isValid ) {
            flash.message = messageSource.getMessage('vat.valid',
                    [cmd.code, cmd.vatNumber] as Object[],
                    "${cmd.code} : ${cmd.vatNumber} is valid",

        } else {
            flash.error = messageSource.getMessage('vat.valid',
                    [cmd.code, cmd.vatNumber] as Object[],
                    "${cmd.code} : ${cmd.vatNumber} is NOT valid",
        render view: 'index', model: [cmd: cmd, countries: countryService.findAll()]


Create a GSP to render the form and render the flash messages or errors.

    <title>VAT Validator</title>
    <meta name="layout" content="main" />
    <style type="text/css">
        form ol li { list-style-type: none; }
<div id="content" role="main">
    <section class="row colset-2-its">
        <g:if test="${flash.message}">
            <p class="message">${flash.message}</p>
        <g:if test="${flash.error}">
            <p class="errors">${flash.error}</p>
        <g:if test="${cmd}">
            <g:hasErrors bean="${cmd}">
                <div class="errors">
                    <g:eachError><p><g:message error="${it}"/></p></g:eachError>
        <g:form controller="vat" method="GET">
                    <label for="code"><g:message code="" default="Country"/></label>
                    <g:select id="code" name='code' value="${cmd?.code}"
                              noSelection="${['null':'Select One...']}"
                              optionKey="code" optionValue="name"></g:select>
                    <label for="code"><g:message code="vat.vatNumber" default="VAT Number"/></label>
                    <g:textField name="vatNumber" id="vatNumber" vatNumber="${cmd?.vatNumber}"/>
                    <g:actionSubmit id="submit" value="${message(code:'vat.check', default: 'Check')}" action="validate"/>

3.4 Functional Test

Add several Geb dependencies. Read our guide Run Grails Geb Functional Tests with Multiple Browsers to learn more.

testCompile "org.grails.plugins:geb"
testCompile "org.mockito:mockito-core"
testCompile "org.seleniumhq.selenium:htmlunit-driver:2.35.1"
testRuntime 'net.sourceforge.htmlunit:htmlunit:2.35.0'
testCompile "org.seleniumhq.selenium:selenium-remote-driver:3.141.59"
testCompile "org.seleniumhq.selenium:selenium-api:3.141.59"
testCompile "org.seleniumhq.selenium:selenium-support:3.141.59"
testRuntime "org.seleniumhq.selenium:selenium-chrome-driver:3.141.59"
testRuntime "org.seleniumhq.selenium:selenium-firefox-driver:3.141.59"

Ensure you can pass system properties to the Gradle Task integrationTest. You can supply the geb enviroment via the System Property geb.env.

integrationTest {

Create a Geb Page to encapsulate the form markup. The use of Geb pages makes our tests easier to maintain.

package demo

import geb.Page

class VatFormPage extends Page {

    static url = '/vat/index'

    static content = {
        vatNumberInput { $('input#vatNumber', 0) }
        codeSelect { $('select#code', 0) }
        submitButton { $('input#submit', 0) }

    void validate(String code, String vatNumber ) {
        vatNumberInput = vatNumber
        codeSelect = code.toUpperCase()

Create a functional test which submits the form with several values:

package demo

import geb.spock.GebSpec
import grails.testing.mixin.integration.Integration
import spock.lang.Unroll

class VatControllerSpec extends GebSpec {

    def "#memberState : #vatNumber validation #unrollDescription when you submit the form"(String memberState, String vatNumber, Boolean expected, String unrollDescription) {

        VatFormPage page = VatFormPage
        page.validate(memberState, vatNumber)

        if ( expected) {
            browser.driver.pageSource.contains("${memberState} : ${vatNumber} is valid")
        } else {
            browser.driver.pageSource.contains("${memberState} : ${vatNumber} is NOT valid")

        memberState | vatNumber   || expected
        'ES'        | 'B99286353' || true
        'ES'        | 'B19280031' || true
        'ES'        | 'XXXXXXXXX' || false

        unrollDescription = expected ? 'is successful' : ' fails'

4 Help with Grails

Object Computing, Inc. (OCI) sponsored the creation of this Guide. A variety of consulting and support services are available.

OCI is Home to Grails

Meet the Team