Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Andrey Adamovich - Groovy 2 Cookbook - 2013.pdf
Скачиваний:
44
Добавлен:
19.03.2016
Размер:
26.28 Mб
Скачать

Working with Web Services in Groovy

Issuing a SOAP request and parsing a response

SOAP stands for a Simple Object Access Protocol. And, indeed, the protocol is very simple and extensible, since it only defines the container parts of the message being transferred, such as, body, header, and fault. But it does not strictly define format or validation rules for the content of the message apart from requesting the message to be XML-compatible.

Also, SOAP has several low-level protocol bindings such as, SOAP-over-HTTP, SOAP-over-SMTP, and SOAP-over-JMS. HTTP binding is the most popular choice for SOAP-based web services.

Usually, a SOAP web service is associated with a Web Service Definition Language (WSDL) and probably a set of XML Schema Definition (XSD) that more precisely define the contents of the service operation requests and responses. But strictly speaking those are not required by the SOAP standard, even though they definitely help to understand the service input and output formats.

There is also a family of WS-* (WS-I, WS-Policy, WS-Addressing, WS-Security, and so on) standards that define different aspects of web service authentication, authorization, interoperability, notification, validation, and so on, which can enrich a SOAP message with additional headers, structures, and faults.

There are plenty of libraries available out there to support different WS-* and X-* flavors, but there is no single answer (at least in the open source world) to all of them.

In essence, any SOAP request is an HTTP POST request containing a SOAP Envelope with message data and headers.

In this recipe, we will focus on constructing simple SOAP requests at the HTTP protocol level using the HTTPBuilder library that we just encountered in the previous recipe, Issuing a REST request and parsing a response.

Getting ready

For testing purposes, we will use SOAP web services hosted at

http://www.holidaywebservice.com. We will try to call the GetMothersDay operation on the USHolidayDates service. The following XML snippet is the SOAP request we will send to the service:

<?xml version="1.0" encoding="UTF-8"?> <soap-env:Envelope

xmlns:soap-env='http://schemas.xmlsoap.org/soap/envelope/'> <soap-env:Header/>

<soap-env:Body> <GetMothersDay

276

www.it-ebooks.info

Chapter 8

xmlns='http://www.27seconds.com/Holidays/US/Dates/'>

<year>2013</year>

</GetMothersDay> </soap-env:Body>

</soap-env:Envelope>

As you can guess, it tries to retrieve a date of the Mother's Day for 2013.

How to do it...

Let's perform the following steps to construct our SOAP client for the holiday web service:

1.First of all we need to @Grab and import the required classes as shown in the following code snippet:

@Grab( group='org.codehaus.groovy.modules.http-builder', module='http-builder',

version='0.6'

)

import static groovyx.net.http.Method.* import static groovyx.net.http.ContentType.* import groovyx.net.http.HTTPBuilder

2.Then we need to create an instance of HTTPBuilder and point to our web service:

def baseUrl = 'http://www.holidaywebservice.com/' + 'Holidays/US/Dates/USHolidayDates.asmx'

def client = new HTTPBuilder(baseUrl)

3.Now, we need to define several namespace variables to refer to during the XML building stage:

def holidayBase = 'http://www.27seconds.com/Holidays' def holidayNS = "$holidayBase/US/Dates/"

def soapAction = "$holidayBase/US/Dates/GetMothersDay" def soapNS = 'http://schemas.xmlsoap.org/soap/envelope/'

4.At this point, we are ready to POST a SOAP request to the service:

def response = client.request( POST, XML ) { headers = [

'Content-Type': 'text/xml; charset=UTF-8', 'SOAPAction': soapAction

]

body = {

mkp.pi(xml:'version="1.0" encoding="UTF-8"') 'soap-env:Envelope'('xmlns:soap-env': soapNS) {

277

www.it-ebooks.info

Working with Web Services in Groovy

'soap-env:Header'() 'soap-env:Body' {

GetMothersDay('xmlns': holidayNS) { year(2013)

}

}

}

}

}

5.Now, we are ready to process the response, for example, by printing the following code line:

println response

How it works...

As you can guess, HTTPBuilder just makes a POST request. We also give a hint to HTTPBuilder that we are going to send XML data to the service. When that hint is given, the body parameter is processed with the help of StreamingMarkupBuilder (see the

Constructing XML content recipe in Chapter 5, Working with XML in Groovy) and encoded.

Since the service, which we are trying to connect to, is based on SOAP 1.1, we need to add a special SOAPAction header (in SOAP 1.2 that header is no longer needed). Also, the Content-Type header is mandatory, because otherwise the web service will not recognize the type of content we are trying to submit.

The response object is of the GPathResult type, which can be navigated, searched, and printed in the same way as any other XML structure in Groovy with the help of the GPath expressions. For more information on XML processing, refer to Chapter 5, Working with XML in Groovy.

There's more...

On the Groovy's website, you can find references to two other SOAP supporting libraries:

GroovySOAP and GroovyWS. Both of them are considered deprecated in favor of the groovy-wslite library (https://github.com/jwagenleitner/groovy-wslite) developed by John Wagenleitner. The library has a terrific support for SOAP features and is a bit less verbose than plain HTTPBuilder.

278

www.it-ebooks.info

Chapter 8

The following script makes use of groovy-wslite and does exactly the same as the script described previously:

@Grab('com.github.groovy-wslite:groovy-wslite:0.8.0') import wslite.soap.*

def baseUrl = 'http://www.holidaywebservice.com/' + 'Holidays/US/Dates/USHolidayDates.asmx'

def client = new SOAPClient(baseUrl)

def holidayBase = 'http://www.27seconds.com/Holidays' def holidayNS = "$holidayBase/US/Dates/"

def soapAction = "$holidayBase/US/Dates/GetMothersDay"

def response = client.send(SOAPAction: soapAction) { body {

GetMothersDay('xmlns': holidayNS) { year(2013)

}

}

}

println response.GetMothersDayResponse

See also

ff http://groovy.codehaus.org/modules/http-builder/

ff https://github.com/jwagenleitner/groovy-wslite

ff http://groovy.codehaus.org/GroovyWS

ff http://groovy.codehaus.org/Groovy+SOAP

Consuming RSS and Atom feeds

RSS feeds and Atom feeds are a standardized way to distribute headlines and updates from websites and blogs. Both RSS and Atom feeds are XML documents. RSS is older but widely popular, while Atom is newer and has several advantages over RSS, chiefly the namespace support.

279

www.it-ebooks.info

Working with Web Services in Groovy

For the main differences between Atom and RSS, check the Wikipedia entry about RSS: http://en.wikipedia.org/wiki/RSS. Both formats are largely supported, and often blogs and sites output headline feeds in RSS and Atom at the same time.

In this recipe, we are going to cover the basics of RSS and Atom feed parsing with Groovy.

Getting ready

As RSS and Atom feeds are XML based, it's easy to parse them using one of the several tools offered by Groovy (see the Reading XML using XmlParser recipe in Chapter 5, Working with XML in Groovy).

We will show how to detect if a feed is RSS or Atom and parse it accordingly.

How to do it...

We will create a FeedParser class which will contain the code to open a URL of a feed (RSS or Atom), parse it, and return a list of the FeedEntry objects populated with the content of the feed entries:

1.Let's define the classes in a separate script file as follows: class FeedParser {

def readFeed(url) {

def xmlFeed = new XmlParser(false, true).parse(url) def feedList = []

if (isAtom(xmlFeed)) {

(0..< xmlFeed.entry.size()).each { def entry = xmlFeed.entry.get(it) feedList << new AtomFeedEntry(

entry.title.text(),

entry.author.text(),

entry.link.text(),

entry.published.text()

)

}

}else {

(0..< xmlFeed.channel.item.size()).each { def item = xmlFeed.channel.item.get(it) RSSFeedEntry feed = new RSSFeedEntry(

item.title.text(),

item.link.text(),

280

www.it-ebooks.info

Chapter 8

item.description.text(),

item.pubDate.text()

)

feedList << feed

}

}

feedList

}

def isAtom(Node node) {

def rootElementName = node.name()

if (rootElementName instanceof groovy.xml.QName) { return (rootElementName.localPart == 'feed') && (rootElementName.namespaceURI == 'http://www.w3.org/2005/Atom')

}

false

}

}

abstract class FeedEntry {

}

@groovy.transform.Canonical

class RSSFeedEntry extends FeedEntry { String title

String link String desc String pubDate

}

@groovy.transform.Canonical

class AtomFeedEntry extends FeedEntry { String title

String author String link String pubDate

}

281

www.it-ebooks.info

Working with Web Services in Groovy

2.The FeedParser class can be simply called within the same script file; for example, to print all the titles of the posts from the blog Lambda the Ultimate, which can be found at http://lambda-the-ultimate.org/:

def parser = new FeedParser()

def feedUrl = 'http://lambda-the-ultimate.org/rss.xml' def feed = parser.readFeed(feedUrl)

feed.each {

println "${it.title}"

}

3.After execution, the code should print a list of latest titles as shown in the following output:

Dynamic Region Inference

Dependently-Typed Metaprogramming (in Agda)

...

It's Alive! Continuous Feedback in UI Programming Feed size: 15

How it works...

The FeedParser class opens the XML stream (an HTTP URI), and after detecting if the feed is Atom-based or RSS-based, it processes each entry by creating an instance of the FeedEntry class, which contains headline data.

There's more...

Another approach to consume feeds using Groovy is using the Rome library. Rome is a venerable framework, which has been around for quite a long time, and it's the default library for consuming and producing RSS or Atom feeds in Java.

@Grab('org.rometools:rome-fetcher:1.2')

import org.rometools.fetcher.impl.HttpURLFeedFetcher

def feedFetcher = new HttpURLFeedFetcher()

def feedUrl = 'http://lambda-the-ultimate.org/rss.xml' def feed = feedFetcher.retrieveFeed(feedUrl.toURL())

feed.entries.each { println "${it.title}"

}

println "Feed size: ${feed.entries?.size()}"

282

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]