KEMBAR78
XML and Web Services with Groovy | PDF
XML and Web
Services with Groovy
Dr Paul King, ASERT: @paulk_asert, paulk@asert.com.au
                                                SpringOne2GX - 1
What is Groovy?
                    • “Groovy is like a super version
                      of Java. It can leverage Java's
                      enterprise capabilities but also
                      has cool productivity features like closures,
                      DSL support, builders and dynamic typing.”
© ASERT 2006-2009




                     Groovy = Java –   boiler plate code
                                   +   optional dynamic typing
                                   +   closures
                                   +   domain specific languages
                                   +   builders
                                   +   metaprogramming
                                                               SpringOne2GX - 2
Growing Acceptance …
  A slow and steady start but now gaining in
  momentum, maturity and mindshare




Now free
… Growing Acceptance …
© ASERT 2006-2009




                                             SpringOne2gx_Oct2009 - 4
… Growing Acceptance …
© ASERT 2006-2009




                     Groovy and Grails downloads:
                     70-90K per month and growing   SpringOne2gx_Oct2009 - 5
… Growing Acceptance …
© ASERT 2006-2009




                             Source: http://www.micropoll.com/akira/mpresult/501697-116746




                                                      Source: http://www.grailspodcast.com/
                                                                           SpringOne2gx_Oct2009 - 6
… Growing Acceptance …
© ASERT 2006-2009




                          http://www.jroller.com/scolebourne/entry/devoxx_2008_whiteboard_votes




                                                               http://www.java.net
                                                                               SpringOne2gx_Oct2009 - 7
… Growing Acceptance …
                What alternative JVM language are you using or intending to use
© ASERT 2006-2009




                                 http://www.leonardoborges.com/writings
                                                                          SpringOne2gx_Oct2009 - 8
… Growing Acceptance …
© ASERT 2006-2009




                    http://it-republik.de/jaxenter/quickvote/results/1/poll/44 (translated using http://babelfish.yahoo.com)
                                                                                                                          SpringOne2gx_Oct2009 - 9
… Growing Acceptance
© ASERT 2006-2009




                                           SpringOne2gx_Oct2009 - 10
Better XML Manipulation...


                    <records>                                                                     records.xml
                       <car name='HSV Maloo' make='Holden' year='2006'>
                         <country>Australia</country>
                         <record type='speed'>Production Pickup Truck with speed of 271kph</record>
                       </car>
© ASERT 2006-2009




                       <car name='P50' make='Peel' year='1962'>
                         <country>Isle of Man</country>
                         <record type='size'>Smallest Street-Legal Car at 99cm wide and 59 kg weight</record>
                       </car>
                       <car name='Royale' make='Bugatti' year='1931'>
                         <country>France</country>
                         <record type='price'>Most Valuable Car at $15 million</record>
                       </car>
                    </records>




                                                                                                      AUG 2009 - 11
...Better XML Manipulation...
                    import   org.w3c.dom.Document;
                    import   org.w3c.dom.NodeList;
                    import   org.w3c.dom.Node;
                    import   org.xml.sax.SAXException;

                    import   javax.xml.parsers.DocumentBuilderFactory;
                    import   javax.xml.parsers.DocumentBuilder;
                    import   javax.xml.parsers.ParserConfigurationException;
                    import   java.io.File;
                    import   java.io.IOException;

                    public class FindYearsJava {
                        public static void main(String[] args) {
© ASERT 2006-2009




                            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
                            try {
                                DocumentBuilder builder = builderFactory.newDocumentBuilder();
                                Document document = builder.parse(new File("records.xml"));
                                NodeList list = document.getElementsByTagName("car");
                                for (int i = 0; i < list.getLength(); i++) {
                                    Node n = list.item(i);
                                    Node year = n.getAttributes().getNamedItem("year");
                                    System.out.println("year = " + year.getTextContent());
                                }
                            } catch (ParserConfigurationException e) {
                                e.printStackTrace();
                            } catch (SAXException e) {
                                e.printStackTrace();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                                                                                                            AUG 2009 - 12
...Better XML Manipulation...
                    import   org.w3c.dom.Document;
                    import   org.w3c.dom.NodeList;
                    import   org.w3c.dom.Node;                                          boilerplate
                    import   org.xml.sax.SAXException;

                    import   javax.xml.parsers.DocumentBuilderFactory;
                    import   javax.xml.parsers.DocumentBuilder;
                    import   javax.xml.parsers.ParserConfigurationException;
                    import   java.io.File;
                    import   java.io.IOException;

                    public class FindYearsJava {
                        public static void main(String[] args) {
© ASERT 2006-2009




                            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
                            try {
                                DocumentBuilder builder = builderFactory.newDocumentBuilder();
                                Document document = builder.parse(new File("records.xml"));
                                NodeList list = document.getElementsByTagName("car");
                                for (int i = 0; i < list.getLength(); i++) {
                                    Node n = list.item(i);
                                    Node year = n.getAttributes().getNamedItem("year");
                                    System.out.println("year = " + year.getTextContent());
                                }
                            } catch (ParserConfigurationException e) {
                                e.printStackTrace();
                            } catch (SAXException e) {
                                e.printStackTrace();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                                                                                                            AUG 2009 - 13
...Better XML Manipulation



                    def records = new XmlParser().parse("records.xml")
                    records.car.each {
© ASERT 2006-2009




                        println "year = ${it.@year}"
                    }



                                                   year = 2006
                                                   year = 1962
                                                   year = 1931


                                                                 AUG 2009 - 14
XML


      SpringOne 2GX 2009. All rights reserved. Do not distribute without permission.
Groovy and XML ...
                    • Reading XML
                      – Special Groovy support:
                        XmlParser, XmlSlurper, DOMCategory
                      – Or Groovy sugar for your current favorites:
                        DOM, SAX, StAX, DOM4J, JDom, XOM, XPath,
                        XSLT, XQuery, etc.
© ASERT 2006-2009




                    • Creating XML
                      – Special Groovy support:
                        MarkupBuilder and StreamingMarkupBuilder
                      – Or again, enhanced syntax for your current
                        favorites

                                                            SpringOne2GX - 16
... Groovy and XML ...
                    • Updating XML
                      – Using above: read followed by create
                      – Can be done with XmlParser, XmlSlurper,
                        DOMCategory
                      – Or with your Java favorites
© ASERT 2006-2009




                    • Verifying XML
                      – Also DTD, W3C XML Schema, Relax NG in a
                        similar fashion to Java mechanisms for these
                        features




                                                             SpringOne2GX - 17
... Groovy and XML
                    • So many technologies – how to choose?
                      – Normally just use XmlSlurper and
                        StreamingMarkupBuilder
                      – Or if you want a DOM, use XmlParser and
                        MarkupBuilder or DOMBuilder
                      – Or if you must have a W3C DOM, use
© ASERT 2006-2009




                        DOMCategory
                      – Or if you expect to have a large amount of
                        legacy or Java parsing code, you can stick
                        with your favorite Java XML API/stack



                                                              SpringOne2GX - 18
An Xml Example ...
                    import groovy.xml.dom.DOMCategory

                    class Flights {
                        static final String XML = '''
                            <trip>
                                <flight hours="13">
                                    <from>Brisbane</from>
© ASERT 2006-2009




                                    <to>Los Angeles</to>
                                </flight>
                                <flight hours="4">
                                    <from>Los Angeles</from>
                                    <to>New Orleans</to>
                                </flight>
                            </trip>
                        '''
                    ...
                                                               SpringOne2GX - 19
... An Xml Example
                    ...
                          static final Reader getReader() {
                              new StringReader(XML)
                          }
                          static final Set getCities(flights) {
                              Set cities = []
© ASERT 2006-2009




                              use(DOMCategory) {
                                  flights.each { f ->
                                      cities += f.to[0].text()
                                      cities += f.from[0].text()
                                  }
                              }                    You can mostly ignore
                              cities               the details here for now.
                          }                        We’ll cover DOMCategory
                    }                                  in more detail shortly.

                                                                         SpringOne2GX - 20
XmlParser
                    def trip = new XmlParser().parseText(Flights.XML)
                    assert trip.flight[0].to.text() == 'Los Angeles'
                    assert trip.flight[1].@hours == '4'

                    Set cities = trip.flight.from*.text() +
                            trip.flight.to*.text()
© ASERT 2006-2009




                    assert cities == ['Brisbane', 'Los Angeles',
                                      'New Orleans'] as Set

                    assert trip.flight.@hours == ['13', '4']
                    assert trip.flight.@hours*.toInteger().sum() == 17


                          • Builds an in-memory DOM tree

                                                               SpringOne2GX - 21
XmlParser – Under the covers
                    • For a JavaBean, this Groovy expression:
                       trip.flight[0].to[0].text()

                    • Is “roughly” converted to:
                       trip.getflight().get(0).getTo().get(0).text()
© ASERT 2006-2009




                      where getFlight() and getTo() return a List
                    • But for XML Parser, it overrides this mechanism
                      and “roughly” converts to:
                       trip.getByName('flight').get(0).
                                getByName('to').get(0).text()

                      where getByName is a Groovy method similar to
                      getElementsByTagName in org.w3c.dom.Element
                                                                SpringOne2GX - 22
XmlSlurper...
                    • The same again using XmlSlurper
                      – Mostly identical syntax and capabilities
                    def trip = new XmlSlurper().parseText(Flights.XML)
                    assert trip.flight[0].to.text() == 'Los Angeles'
                    assert trip.flight[1].@hours == '4'
© ASERT 2006-2009




                    Set cities = trip.flight.from*.text() +
                            trip.flight.to*.text()
                    assert cities == ['Brisbane', 'Los Angeles',
                                      'New Orleans'] as Set

                    assert trip.flight.@hours.list() == ['13', '4']
                    assert trip.flight.@hours*.toInteger().sum() == 17

                      – But features lazy evaluation of expressions
                      – Consider this for streaming scenarios         SpringOne2GX - 23
...XmlSlurper...
                    • What does Lazy mean?

                     def   trip = new XmlSlurper().parseText(Flights.XML)
                     def   moreThanFiveHours = { f -> f.@hours.toInteger() > 5 }
                     def   arrivingLax = { f -> f.to == 'Los Angeles' }
                     def   departingOz = { f -> f.from == 'Brisbane' }
© ASERT 2006-2009




                     def longFlights = trip.flight.findAll(moreThanFiveHours)
                     def longLaxFlights = longFlights.findAll(arrivingLax)
                     def longOzLaxFlights = longLaxFlights.findAll(departingOz)
                     assert longOzLaxFlights.@hours == '13'




                                                                          SpringOne2GX - 24
...XmlSlurper
                    Light-weight scanning here

                     def   trip = new XmlSlurper().parseText(Flights.XML)
                     def   moreThanFiveHours = { f -> f.@hours.toInteger() > 5 }
                     def   arrivingLax = { f -> f.to == 'Los Angeles' }
                     def   departingOz = { f -> f.from == 'Brisbane' }
© ASERT 2006-2009




                     def longFlights = trip.flight.findAll(moreThanFiveHours)
                     def longLaxFlights = longFlights.findAll(arrivingLax)
                     def longOzLaxFlights = longLaxFlights.findAll(departingOz)
                     assert longOzLaxFlights.@hours == '13'


                                                        Lazy expression storage
                       Usage triggers evaluation         but deferred evaluation

                    • This may look puzzling at first
                      – But the good news is you don’t normally haveSpringOne2GX - 25
                                                                     to care
A Namespace Example
                    class Books {
                      static final String XML = '''
                      <rdf:rdf xmlns:bibterm="http://www.book-stuff.com/terms/"
                           xmlns:dc="http://purl.org/dc/elements/1.0/"
                           xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
                        <rdf:description rdf:about="http://www.book-stuff.com/bib">
                          <bibterm:book rdf:parseType="Resource">
                            <bibterm:year>2007</bibterm:year>
                            <dc:title>Groovy in Action</dc:title>
© ASERT 2006-2009




                            <bibterm:author rdf:parseType="Resource">
                              <bibterm:last>König</bibterm:last>
                              <bibterm:first>Dierk</bibterm:first>
                            </bibterm:author>
                            <rdf:comment>
                              Coauthors: Andrew Glover, Paul King,
                              Guillaume Laforge and Jon Skeet
                            </rdf:comment>
                          </bibterm:book>
                        </rdf:description>
                      </rdf:rdf>
                      ''' ...
                                                                           SpringOne2GX - 26
XmlParser and Namespaces
                    • Recommended syntax:
                       import groovy.xml.*
                       def book = new XmlParser().parseText(Books.XML)
                       def rdf = new Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
                       def dc = new Namespace('http://purl.org/dc/elements/1.0/')
                       def bibterm = new Namespace('http://www.book-stuff.com/terms/')
                       def b = book[rdf.description][bibterm.book]
                       assert b[dc.title].text() == 'Groovy in Action'
                       assert b[bibterm.year].text() == '2007'
© ASERT 2006-2009




                    • Options
                       // use   string style matching (exact match on prefix or wildcard or URI)
                       assert   b.'bibterm:year'.text() == '2007'
                       assert   b.'*:year'.text() == '2007'
                       assert   b.'http://www.book-stuff.com/terms/:year'.text() == '2007'

                       // Namespace is a QName factory but you can use QName directly
                       def bibtermYear = new QName('http://www.book-stuff.com/terms/', 'year')
                       assert b[bibtermYear].text() == '2007'

                       // use QName with wildcards
                       def anyYear = new QName('*', 'year')
                       assert b[anyYear].text() == '2007'
                                                                                      SpringOne2GX - 27
XmlSlurper and Namespaces

                    def book = new XmlSlurper().parseText(Books.XML)
                    book.declareNamespace(
                        rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
                        dc: 'http://purl.org/dc/elements/1.0/',
                        bibterm: 'http://www.book-stuff.com/terms/')
                    def b = book.'rdf:description'.'bibterm:book'
© ASERT 2006-2009




                    assert b.'dc:title' == 'Groovy in Action'
                    assert b.'bibterm:year' == '2007'




                                                                      SpringOne2GX - 28
Other GPath Features
                    • Similar syntax for XmlSlurper and DOMCategory

                      def trip = new XmlParser().parseText(Flights.XML)
                      assert trip.'**'*.name() ==
                          ['trip', 'flight', 'from', 'to', 'flight', 'from', 'to']
                      assert trip.depthFirst()*.name() ==
                          ['trip', 'flight', 'from', 'to', 'flight', 'from', 'to']
                      assert trip.breadthFirst()*.name() ==
© ASERT 2006-2009




                          ['trip', 'flight', 'flight', 'from', 'to', 'from', 'to']
                      assert trip.'**'.from*.text() == ['Brisbane', 'Los Angeles']




                                                                            SpringOne2GX - 29
What about non-XML?
                     @Grab('nekohtml:nekohtml:1.9.6.2')
                     import org.cyberneko.html.parsers.SAXParser

                     def neko = new SAXParser()
                     neko.setFeature('http://xml.org/sax/features/namespaces', false)
                     def page = new XmlParser(neko).parse('http://groovy.codehaus.org/')
                     def data = page.depthFirst().A.'@href'.grep{
                         it != null && it.endsWith('.html')
                     }
© ASERT 2006-2009




                     data.each { println it }

                     def neko = new SAXParser() // alternate style
                     def page = new XmlSlurper(neko).parse('http://groovy.codehaus.org/')
                     def data = page.depthFirst().grep{
                         it.name() == 'A' && it.@href.toString().endsWith('.html')
                     }.'@href'
                     data.each { println it }

                    http://groovy.javanicus.com/search.html
                    http://www.dcs.napier.ac.uk/~cs05/groovy/groovy.html
                    http://www.ej-technologies.com/products/jprofiler/overview.html
                                                                                      SpringOne2GX - 30
Raw DOM

                    import groovy.xml.DOMBuilder

                    def trip = DOMBuilder.parse(Flights.reader).documentElement
                    def flights = trip.getElementsByTagName('flight')

                    def dest = flights.item(0).getElementsByTagName('to').item(0)
© ASERT 2006-2009




                    assert dest.firstChild.nodeValue == 'Los Angeles'

                    assert flights.item(1).getAttribute('hours') == '4'

                    assert Flights.getCities(flights) ==
                            ['Brisbane', 'Los Angeles', 'New Orleans'] as Set




                                                                          SpringOne2GX - 31
DOM plus metaprogramming

                    import groovy.xml.DOMBuilder
                    import org.w3c.dom.Element

                    def trip = DOMBuilder.parse(Flights.reader).documentElement

                    Element.metaClass.element = { t, i ->
© ASERT 2006-2009




                            delegate.getElementsByTagName(t).item(i) }
                    Element.metaClass.text = {-> delegate.firstChild.nodeValue }

                    assert trip.element('flight', 0).element('to', 0).text() ==
                            'Los Angeles'
                    assert trip.element('flight', 1).getAttribute('hours') == '4'




                                                                        SpringOne2GX - 32
DOMCategory
                    import groovy.xml.DOMBuilder
                    import groovy.xml.dom.DOMCategory

                    def doc = DOMBuilder.parse(Flights.reader)
                    def trip = doc.documentElement
© ASERT 2006-2009




                    use(DOMCategory) {
                        assert trip.flight[0].to[0].text() ==
                            'Los Angeles'
                        assert trip.flight[1].'@hours' == '4'
                        assert Flights.getCities(trip.flight) ==
                            ['Brisbane', 'Los Angeles',
                             'New Orleans'] as Set
                    }

                                                            SpringOne2GX - 33
DOM4J


                    @Grab('dom4j:dom4j:1.6.1')
                    import org.dom4j.io.SAXReader

                    def trip = new SAXReader().read(Flights.reader).rootElement
© ASERT 2006-2009




                    assert trip.elements()[0].elementText('to') == 'Los Angeles'
                    assert trip.elements()[1].attributeValue('hours') == '4'




                                                                        SpringOne2GX - 34
JDOM


                    @Grab('org.jdom:jdom:1.1')
                    import org.jdom.input.SAXBuilder

                    def b = new SAXBuilder()
                    def trip = b.build(Flights.reader).rootElement
© ASERT 2006-2009




                    assert trip.children[0].getChildText('to') == 'Los Angeles'
                    assert trip.children[1].getAttribute('hours').value == '4'




                                                                       SpringOne2GX - 35
XOM

                    @Grab('xom:xom:1.1')
                    import nu.xom.Builder

                    def doc = new Builder().build(Flights.reader)
                    def flights = doc.rootElement.childElements
                    def to = flights.get(0).getFirstChildElement('to')
© ASERT 2006-2009




                    assert to.value == 'Los Angeles'
                    def hours = flights.get(1).getAttribute('hours')
                    assert hours.value == '4'




                                                                 SpringOne2GX - 36
StAX
                    import static javax.xml.stream.XMLInputFactory.newInstance as staxFactory
                    import javax.xml.stream.XMLStreamReader as StaxReader

                    def flights = []
                    def flight
                    def seenTag
                    StaxReader.metaClass.attr = { s -> delegate.getAttributeValue(null, s) }

                    def reader = staxFactory().createXMLStreamReader(Flights.reader)
                    while (reader.hasNext()) {
                        def name = reader.localName
© ASERT 2006-2009




                        if (reader.startElement) {
                            if (name == 'flight') flight = [hours:reader.attr('hours')]
                            else if (name in ['from', 'to']) seenTag = name
                        } else if (reader.characters) {
                            if (seenTag) flight[seenTag] = reader.text
                        } else if (reader.endElement) {
                            if (name == 'flight') flights += flight
                            seenTag = null
                        }
                        reader.next()
                    }

                    assert flights[0].to == 'Los Angeles'
                    assert flights[1].hours == '4'
                                                                                     SpringOne2GX - 37
SAX
                    import javax.xml.parsers.SAXParserFactory
                    import org.xml.sax.*
                    import org.xml.sax.helpers.DefaultHandler

                    class TripHandler extends DefaultHandler {
                        def flights = []
                        private flight, seenTag

                        void startElement(String ns, String localName, String qName, Attributes atts) {
                            if (qName == 'flight') flight = [hours:atts.getValue('hours')]
                            else if (qName in ['from', 'to']) seenTag = qName
                        }
© ASERT 2006-2009




                        public void endElement(String uri, String localName, String qName) {
                            if (qName == 'flight') flights += flight
                            seenTag = null
                        }

                        public void characters(char[] ch, int start, int length) {
                            if (seenTag) flight[seenTag] = new String(ch, start, length)
                        }
                    }

                    def handler = new TripHandler()
                    def reader = SAXParserFactory.newInstance().newSAXParser().xMLReader
                    reader.setContentHandler(handler)
                    reader.parse(new InputSource(Flights.reader))

                    assert handler.flights[0].to == 'Los Angeles'
                    assert handler.flights[1].hours == '4'
                                                                                               SpringOne2GX - 38
XPath
                    import javax.xml.xpath.*
                    import groovy.xml.DOMBuilder

                    def xpath = XPathFactory.newInstance().newXPath()
                    def trip = DOMBuilder.parse(Flights.reader).documentElement
                    assert xpath.evaluate('flight/to/text()', trip) ==
                            'Los Angeles'
                    assert xpath.evaluate('flight[2]/@hours', trip) == '4'
© ASERT 2006-2009




                    def flights = xpath.evaluate( 'flight', trip,
                            XPathConstants.NODESET )
                    def hoursAsInt = { n ->
                            xpath.evaluate('@hours', n).toInteger() }
                    assert flights.collect(hoursAsInt).sum() == 17

                    assert Flights.getCities(flights) == ['Brisbane',
                            'Los Angeles', 'New Orleans'] as Set


                                                                        SpringOne2GX - 39
XPath with DOMCategory


                    import groovy.xml.DOMBuilder
                    import groovy.xml.dom.DOMCategory
                    import static javax.xml.xpath.XPathConstants.*

                    def trip = DOMBuilder.parse(Flight.reader).documentElement
© ASERT 2006-2009




                    use (DOMCategory) {
                       assert trip.xpath('flight/to/text()') == 'Los Angeles'
                       assert trip.xpath('flight[2]/@hours', NUMBER) == 4
                       flights = trip.xpath('flight', NODESET)
                       def hoursAsNum = { n -> n.xpath('@hours', NUMBER) }
                       assert flights.collect(hoursAsNum).sum() == 17
                    }




                                                                       SpringOne2GX - 40
Xalan XPath
                    @Grab('xalan:xalan:2.7.1')
                    import static org.apache.xpath.XPathAPI.*
                    import groovy.xml.DOMBuilder

                    def trip = DOMBuilder.parse(Flights.reader).documentElement

                    assert eval(trip, 'flight/to/text()').str() ==
© ASERT 2006-2009




                            'Los Angeles'
                    assert eval(trip, 'flight[2]/@hours').str() == '4'

                    def flights = selectNodeList(trip, '//flight')
                    def hoursAsInt = { n ->
                            eval(n, '@hours').str().toInteger() }
                    assert flights.collect(hoursAsInt).sum() == 17

                    assert Flights.getCities(flights) == ['Brisbane',
                            'Los Angeles', 'New Orleans'] as Set


                                                                         SpringOne2GX - 41
Jaxen XPath
                    @Grab('jaxen#jaxen;1.1.1')
                    import org.jaxen.dom.DOMXPath
                    import groovy.xml.DOMBuilder

                    def trip = DOMBuilder.parse(Flights.reader).documentElement
                    assert new DOMXPath('flight/to/text()').
                            stringValueOf(trip) == 'Los Angeles'
© ASERT 2006-2009




                    assert new DOMXPath('flight[2]/@hours').
                            stringValueOf(trip) == '4'

                    def flights = new DOMXPath('flight').selectNodes(trip)
                    def hoursAsInt = { n ->
                            new DOMXPath('@hours').numberValueOf(n) }
                    assert flights.collect(hoursAsInt).sum() == 17

                    assert Flights.getCities(flights) == ['Brisbane',
                            'Los Angeles', 'New Orleans'] as Set

                                                                        SpringOne2GX - 42
JSR 225 - XQJ
                    import net.sf.saxon.xqj.SaxonXQDataSource
                    import javax.xml.xquery.XQSequence
                    XQSequence.metaClass.collect = { Closure c ->
                        def items = []
                        while (delegate.next()) items += c(delegate)
                        items
                    }
                    def asString = { seq -> seq.getItemAsString(null) }
© ASERT 2006-2009




                    def hourAttr = { it.item.node.getAttribute('hours') as int }
                    def flights = "document { ${Flights.XML} }"
                    def exp =
                        new SaxonXQDataSource().connection.createExpression()
                    def seq = exp.executeQuery("$flights/trip/flight/to/text()")
                    assert seq.collect(asString) == ['Los Angeles',
                                                     'New Orleans']
                    seq = exp.executeQuery("$flights/trip/flight")
                    assert seq.collect(hourAttr).sum() == 17


                                                                        SpringOne2GX - 43
XSLT...
                    import static javax.xml.transform.TransformerFactory.newInstance as xsltFactory
                    import javax.xml.transform.stream.*
                    def xslt = '''
                    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
                      <xsl:template match="/trip">
                        <html>
                          <body>
                            <h1>Flights</h1>
                            <ul>
                              <xsl:apply-templates select="flight"/>
                            </ul>
© ASERT 2006-2009




                          </body>
                        </html>
                      </xsl:template>
                      <xsl:template match="flight">
                        <li>
                          <xsl:value-of select="from"/> => <xsl:value-of select="to"/>
                        </li>
                      </xsl:template>
                    </xsl:stylesheet>
                    '''.trim()
                    def transformer = xsltFactory().newTransformer(
                            new StreamSource(new StringReader(xslt)))
                    transformer.transform(new StreamSource(Flights.reader),
                            new StreamResult(System.out))
                                                                                       SpringOne2GX - 44
...XSLT
                    import static javax.xml.transform.TransformerFactory.newInstance as xsltFactory
                    import javax.xml.transform.stream.*
                    def xslt = '''
                                                           <html>
                    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
                      <xsl:template match="/trip">           <body>
                        <html>                                 <h1>Flights</h1>
                          <body>                               <ul>
                            <h1>Flights</h1>                     <li>Brisbane =&gt; Los Angeles</li>
                            <ul>                                 <li>Los Angeles =&gt; New Orleans</li>
                                                               </ul>
                              <xsl:apply-templates select="flight"/>
                            </ul>                            </body>
© ASERT 2006-2009




                          </body>                          </html>
                        </html>
                      </xsl:template>
                      <xsl:template match="flight">
                        <li>
                          <xsl:value-of select="from"/> => <xsl:value-of select="to"/>
                        </li>
                      </xsl:template>
                    </xsl:stylesheet>
                    '''.trim()
                    def transformer = xsltFactory().newTransformer(
                            new StreamSource(new StringReader(xslt)))
                    transformer.transform(new StreamSource(Flights.reader),
                            new StreamResult(System.out))
                                                                                           SpringOne2GX - 45
MarkupBuilder

                    import groovy.xml.MarkupBuilder

                    def writer = new StringWriter()
                    def xml = new MarkupBuilder(writer)
                    xml.flights {
                      flight(hours:13) {
© ASERT 2006-2009




                        from('Brisbane')
                        to('Los Angeles')
                      }
                      flight(hours:4) {
                        from('Los Angeles')
                        to('New Orleans')
                      }
                    }
                    println writer



                                                          SpringOne2GX - 46
StreamingMarkupBuilder

                    import groovy.xml.StreamingMarkupBuilder

                    def writer = new StreamingMarkupBuilder().bind {
                      flights {
                        flight(hours: 13) {
                          from('Brisbane')
© ASERT 2006-2009




                          to('Los Angeles')
                        }
                        flight(hours: 4) {
                          from('Los Angeles')
                          to('New Orleans')
                        }
                      }
                    }
                    println writer



                                                                  SpringOne2GX - 47
DOMBuilder

                    import groovy.xml.*

                    def builder = DOMBuilder.newInstance()
                    def root = builder.flights {
                        flight(hours: 13) {
                          from('Brisbane')
© ASERT 2006-2009




                          to('Los Angeles')
                        }
                        flight(hours: 4) {
                          from('Los Angeles')
                          to('New Orleans')
                        }
                    }
                    new XmlNodePrinter().print(root)




                                                             SpringOne2GX - 48
MarkupBuilder with Namespaces
                    import groovy.xml.MarkupBuilder

                    def writer = new StringWriter()
                    def xml = new MarkupBuilder(writer)
                    xml.'rdf:description'(
                        'xmlns:bibterm': "http://www.book-stuff.com/terms/",
                        'xmlns:dc': "http://purl.org/dc/elements/1.0/",
                        'xmlns:rdf': "http://www.w3.org/1999/02/22-rdf-syntax-
© ASERT 2006-2009




                    ns#") {
                      'bibterm:book' {
                        'dc:title'('Groovy in Action')
                        'bibterm:year'('2007')
                      }
                                     <rdf:description xmlns:dc='http://purl.org/dc/elements/1.0/'
                    }                    xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
                    println writer       xmlns:bibterm='http://www.book-stuff.com/terms/'>
                                           <bibterm:book>
                                             <dc:title>Groovy in Action</dc:title>
                                             <bibterm:year>2007</bibterm:year>
                                           </bibterm:book>
                                          </rdf:description>
                                                                                       SpringOne2GX - 49
StreamingMarkupBuilder with Namespaces
                    import groovy.xml.*

                    XmlUtil.serialize(new StreamingMarkupBuilder().bind {
                      mkp.declareNamespace(
                          bibterm: "http://www.book-stuff.com/terms/",
                          dc: "http://purl.org/dc/elements/1.0/",
                          rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
                      'rdf:description' {
© ASERT 2006-2009




                        'bibterm:book' {
                          'dc:title'('Groovy in Action')
                          'bibterm:year'('2007')
                        }
                      }              <rdf:description xmlns:dc='http://purl.org/dc/elements/1.0/'
                                         xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
                    }, System.out)
                                             xmlns:bibterm='http://www.book-stuff.com/terms/'>
                                          <bibterm:book>
                                            <dc:title>Groovy in Action</dc:title>
                                            <bibterm:year>2007</bibterm:year>
                                          </bibterm:book>
                                         </rdf:description>
                                                                                        SpringOne2GX - 50
DOMBuilder with Namespaces
                    import groovy.xml.DOMBuilder

                    def b = DOMBuilder.newInstance()
                    def root = b.'rdf:description'(
                        'xmlns:bibterm': "http://www.book-stuff.com/terms/",
                        'xmlns:dc': "http://purl.org/dc/elements/1.0/",
                        'xmlns:rdf': "http://www.w3.org/1999/02/22-rdf-syntax-ns#") {
                      'bibterm:book' {
                        'dc:title'('Groovy in Action')
© ASERT 2006-2009




                        'bibterm:year'('2007')
                      }
                    }
                    new XmlNodePrinter().print(root)

                                       <rdf:description xmlns:dc='http://purl.org/dc/elements/1.0/'
                                           xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
                                           xmlns:bibterm='http://www.book-stuff.com/terms/'>
                                        <bibterm:book>
                                          <dc:title>Groovy in Action</dc:title>
                                          <bibterm:year>2007</bibterm:year>
                                        </bibterm:book>
                                       </rdf:description>
                                                                                        SpringOne2GX - 51
DOMBuilder with NamespaceBuilder
                    import groovy.xml.*
                    def b = NamespaceBuilder.newInstance(DOMBuilder.newInstance())
                    b.namespace('http://www.book-stuff.com/terms/', 'bibterm')
                    b.namespace('http://purl.org/dc/elements/1.0/', 'dc')
                    b.namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf')

                    def root = b.'rdf:description' {
                      'bibterm:book' {
                        'dc:title'('Groovy in Action')
© ASERT 2006-2009




                        'bibterm:year'('2007')
                      }
                    }
                    new XmlNodePrinter().print(root)

                    <?xml version="1.0" encoding="UTF-8"?>
                    <rdf:description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
                     <bibterm:book xmlns:bibterm="http://www.book-stuff.com/terms/">
                       <dc:title xmlns:dc="http://purl.org/dc/elements/1.0/">Groovy in Action</dc:title>
                       <bibterm:year>2007</bibterm:year>
                     </bibterm:book>
                    </rdf:description>

                                                                                               SpringOne2GX - 52
StaxBuilder
                    import javax.xml.stream.XMLOutputFactory
                    import groovy.xml.StaxBuilder

                    def factory = XMLOutputFactory.newInstance()
                    def writer = new StringWriter()
                    def xmlwriter = factory.createXMLStreamWriter(writer)
                    def builder = new StaxBuilder(xmlwriter)
                    builder.flights {
© ASERT 2006-2009




                      flight(hours: 13) {
                        from('Brisbane')
                        to('Los Angeles')
                      }
                      flight(hours: 4) {
                        from('Los Angeles')
                        to('New Orleans')
                      }
                    }
                    println writer

                                                                     SpringOne2GX - 53
StaxBuilder for non-XML (JSON)
                    @Grab('org.codehaus.jettison#jettison;1.1')
                    import org.codehaus.jettison.mapped.*
                    import groovy.xml.StaxBuilder

                    def writer = new StringWriter()
                    def con = new MappedNamespaceConvention()
                    def mappedWriter = new MappedXMLStreamWriter(con,
                    writer)
                    def builder = new StaxBuilder(mappedWriter)
© ASERT 2006-2009




                    builder.flights {
                      flight(hours: 13) {
                        from('Brisbane')                 {"flights":{"flight":[
                        to('Los Angeles')                 { "@hours":"13",
                      }                                     "from":"Brisbane",
                      flight(hours: 4) {                    "to":"Los Angeles" },
                        from('Los Angeles')               { "@hours":"4",
                        to('New Orleans')                   "from":"Los Angeles",
                      }                                     "to":"New Orleans" }
                    }                                    ]}}
                    println writer
                                                                         SpringOne2GX - 54
Updating XML

                                                       <shopping>
                    <shopping>                           <category type="groceries">
                      <category type="groceries">          <item>Luxury Chocolate</item>
                        <item>Chocolate</item>             <item>Luxury Coffee</item>
                        <item>Coffee</item>              </category>
                      </category>                        <category type="supplies">
                      <category type="supplies">           <item>Paper</item>
                        <item>Paper</item>                 <item quantity="6"
© ASERT 2006-2009




                        <item quantity="4">Pens</item>            when="Urgent">Pens</item>
                      </category>                        </category>
                      <category type="present">          <category type="present">
                        <item when="Aug 10">               <item>Mum's Birthday</item>
                          Kathryn's Birthday               <item when="Oct 15">
                        </item>                              Monica's Birthday
                      </category>                          </item>
                    </shopping>                          </category>
                                                       </shopping>




                                                                                 SpringOne2GX - 55
Updating with XmlParser
                    ...
                    def root = new XmlParser().parseText(Shopping.XML)

                    // modify groceries: luxury items please
                    root.category.find{ it.@type == 'groceries' }.item.each{ g ->
                        g.value = 'Luxury ' + g.text()
                    }

                    // modify supplies: we need extra pens now
                    root.category.find{
© ASERT 2006-2009




                        it.@type == 'supplies'
                    }.item.findAll{ it.text() == 'Pens' }.each{ p ->
                        p.@quantity = p.@quantity.toInteger() + 2
                        p.@when = 'Urgent'
                    }

                    // modify presents: August has come and gone
                    def presents = root.category.find{ it.@type == 'present' }
                    presents.children().clear()
                    presents.appendNode('item', "Mum's Birthday")
                    presents.appendNode('item', [when:'Oct 15'], "Monica's Birthday")
                    ...

                                                                                    SpringOne2GX - 56
Updating with XmlSlurper
                    ...
                    // modify groceries: luxury items please
                    def groceries = root.category.find{ it.@type == 'groceries' }
                    (0..<groceries.item.size()).each {
                        groceries.item[it] = 'Luxury ' + groceries.item[it]
                    }

                    // modify supplies: we need extra pens now
                    root.category.find{
                        it.@type == 'supplies'
© ASERT 2006-2009




                    }.item.findAll{ it.text() == 'Pens' }.each { p ->
                        p.@quantity = (p.@quantity.toInteger() + 2).toString()
                        p.@when = 'Urgent'
                    }

                    // modify presents: August has come and gone
                    root.category.find{ it.@type == 'present' }.replaceNode{ node ->
                        category(type:'present'){
                            item("Mum's Birthday")
                            item("Monica's Birthday", when:'Oct 15')
                        }
                    }
                    ...
                                                                                 SpringOne2GX - 57
Updating with DOMCategory
                    use(DOMCategory) {
                       // modify groceries: luxury items please
                       def groceries = root.category.find{ it.'@type' == 'groceries' }.item
                       groceries.each { g ->
                          g.value = 'Luxury ' + g.text()
                       }

                        // modify supplies: we need extra pens now
                        def supplies = root.category.find{ it.'@type' == 'supplies' }.item
                        supplies.findAll{ it.text() == 'Pens' }.each { p ->
© ASERT 2006-2009




                           p['@quantity'] = p.'@quantity'.toInteger() + 2
                           p['@when'] = 'Urgent'
                        }

                        // modify presents: August has come and gone
                        def presents = root.category.find{ it.'@type' == 'present' }
                        presents.item.each { presents.removeChild(it) }
                        presents.appendNode('item', "Mum's Birthday")
                        presents.appendNode('item', [when:'Oct 15'], "Monica's Birthday")
                        ...
                    }


                                                                                   SpringOne2GX - 58
Validating against a DTD...
                    def xml = '''
                    <!DOCTYPE records [
                        <!ELEMENT car (country,record)>
                        <!ATTLIST car
                            make NMTOKEN #REQUIRED
                            name CDATA #REQUIRED
                            year NMTOKEN #REQUIRED
                        >
                        <!ELEMENT country (#PCDATA)>
                        <!ELEMENT record (#PCDATA)>
© ASERT 2006-2009




                        <!ATTLIST record type NMTOKEN #REQUIRED>
                        <!ELEMENT records (car+)>
                    ]>
                    <records>
                      <car name="HSV Maloo" make="Holden" year="2006">
                        <country>Australia</country>
                        <record
                            type="speed">Production Pickup Truck with speed of 271kph</record>
                      </car>
                      ...
                    </records>
                    '''.trim()


                                                                                      SpringOne2GX - 59
...Validating against a DTD

                    def validating = true
                    def namespaceAware = false




                    new XmlParser(validating, namespaceAware).parseText(xml)
© ASERT 2006-2009




                    new XmlSlurper(validating, namespaceAware).parseText(xml)




                    import groovy.xml.DOMBuilder
                    DOMBuilder.parse(new StringReader(xml), validating, namespaceAware)




                                                                                SpringOne2GX - 60
Validating against a W3C Schema...
                    def xsd = '''
                    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
                      <xs:element name="records">
                        <xs:complexType>
                          <xs:sequence>
                            <xs:element maxOccurs="unbounded" ref="car"/>
                          </xs:sequence>
                        </xs:complexType>
                      </xs:element>
                      <xs:element name="car">
                        <xs:complexType>
                          <xs:sequence>
© ASERT 2006-2009




                            <xs:element ref="country"/>
                            <xs:element ref="record"/>
                          </xs:sequence>
                          <xs:attribute name="make" use="required" type="xs:NCName"/>
                          <xs:attribute name="name" use="required"/>
                          <xs:attribute name="year" use="required" type="xs:integer"/>
                        </xs:complexType>
                      </xs:element>
                      <xs:element name="country" type="xs:string"/>
                      <xs:element name="record">
                        <xs:complexType mixed="true">
                          <xs:attribute name="type" use="required" type="xs:NCName"/>
                        </xs:complexType>
                      </xs:element>
                    </xs:schema>
                    '''.trim()
                                                                                               SpringOne2GX - 61
...Validating against a W3C Schema

                    import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI
                    import javax.xml.transform.stream.StreamSource
                    import javax.xml.validation.SchemaFactory

                    def factory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI)
                    // println factory.isSchemaLanguageSupported(W3C_XML_SCHEMA_NS_URI)
© ASERT 2006-2009




                    def schema =
                        factory.newSchema(new StreamSource(new StringReader(xsd)))
                    def validator = schema.newValidator()
                    validator.validate(
                        new StreamSource(new StringReader(XmlExamples.CAR_RECORDS)))




                                                                                 SpringOne2GX - 62
Validating against a RelaxNG Schema...
                    def rng = '''
                    <grammar xmlns="http://relaxng.org/ns/structure/1.0"
                             datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
                        <start>
                            <ref name="records"/>
                        </start>
                        <define name="car">
                            <element name="car">
                                <attribute name="make">
                                    <data type="token"/>                 ...
                                </attribute>                             <define name="record">
                                <attribute name="name">
© ASERT 2006-2009




                                                                             <element name="record">
                                    <text/>                                      <attribute name="type">
                                </attribute>                                         <data type="token"/>
                                <attribute name="year">                          </attribute>
                                    <data type="integer"/>                       <text/>
                                </attribute>                                 </element>
                                <ref name="country"/>                    </define>
                                <ref name="record"/>                     <define name="records">
                            </element>                                       <element name="records">
                        </define>                                                <oneOrMore>
                        <define name="country">                                      <ref name="car"/>
                            <element name="country">                             </oneOrMore>
                                <text/>                                      </element>
                            </element>                                   </define>
                        </define>                                    </grammar>
                        ...                                          '''.trim()
                                                                                               SpringOne2GX - 63
...Validating against a RelaxNG Schema

                    // require isorelax.jar, isorelax-jaxp-bridge.jar
                    // require one of Jing, MSV, ...
                    import static javax.xml.XMLConstants.RELAXNG_NS_URI
                    import javax.xml.transform.stream.StreamSource
                    import javax.xml.validation.SchemaFactory

                    def factory = SchemaFactory.newInstance(RELAXNG_NS_URI)
© ASERT 2006-2009




                    def schema =
                        factory.newSchema(new StreamSource(new StringReader(rng)))
                    def validator = schema.newValidator()
                    validator.validate(new StreamSource(new StringReader(CAR_RECORDS)))




                                                                              SpringOne2GX - 64
WEB SERVICES


      SpringOne 2GX 2009. All rights reserved. Do not distribute without permission.
Groovy and Web Services
                    • SOAP Web Services
                      – GroovySOAP using XFire for Java 1.4
                      – GroovyWS using CXF for Java 1.5+
                      – JAXB out of the box for Java 6
                      – CXF, Axis2, Spring Web Services
© ASERT 2006-2009




                    • RESTful Options
                      – Restlet.org, RESTlet DSL, roll your own
                      – JAX-RS: Jersey, CXF, JBoss RESTeasy
                    • Frameworks layered upon SOA
                      – Synapse, Tuscany, ServiceMix


                                                                  SpringOne2GX - 66
GroovySOAP
                    class MathService {                   • XFire based
                       double add(double a, double b) {
                          a + b                           • Java 1.4
                       }
                       double square(double c) {
                          c * c
                       }
                    }       import groovy.net.soap.SoapServer
© ASERT 2006-2009




                            def server = new SoapServer('localhost', 6789)
                            server.setNode('MathService')
                            server.start()

                    import groovy.net.soap.SoapClient

                    def url = 'http://localhost:6789/MathServiceInterface?wsdl'
                    def math = new SoapClient(url)
                    assert math.add(1.0, 2.0) == 3.0
                    assert math.square(3.0) == 9.0
                                                                        SpringOne2GX - 67
GroovyWS
                    class MathService {                         • CXF based
                        double add(double a, double b) {
                            a + b                               • Java 1.5+
                        }
                        double square(double c) {
                            c * c
                        }
                    }
                    import groovyx.net.ws.WSServer
© ASERT 2006-2009




                    def server = new WSServer()
                    server.setNode MathService.name,
                        "http://localhost:6980/MathService"

                           import groovyx.net.ws.WSClient
                           def url = "http://localhost:6980/MathService?wsdl"
                           def proxy = new WSClient(url, this.class.classLoader)
                           def result = proxy.add(1.0d, 2.0d)
                           assert result == 3.0d
                           result = proxy.square(3.0d)
                           assert result == 9.0d
                                                                             SpringOne2GX - 68
JAXB Server
                    import   javax.xml.ws.Endpoint
                    import   javax.jws.WebService
                    import   javax.jws.soap.SOAPBinding
                    import   javax.jws.WebMethod

                    @WebService(name="Echo", serviceName="EchoService",
                                targetNamespace="http://jaxws.asert.com")
                    @SOAPBinding(style=SOAPBinding.Style.RPC)
© ASERT 2006-2009




                    class EchoImpl {
                        @WebMethod(operationName = "echo")
                        String echo(String message) {
                            println "Received: $message"
                            "nYou said: " + message
                        }
                    }

                    Endpoint.publish("http://localhost:8080/Echo", new EchoImpl())
                    println 'EchoService published and running ...'



                                                                            SpringOne2GX - 69
JAXB Client
                    • JAXB Client
                    import javax.xml.namespace.QName
                    import com.asert.jaxws.EchoService

                    def url = new URL("http://localhost:8080/Echo?wsdl")
                    def qname = new QName("http://jaxws.asert.com", "EchoService")
                    def echoServer = new EchoService(url, qname).echoPort
© ASERT 2006-2009




                    println echoServer.echo("Today is ${new Date()}")



                    • Build instructions
                     wsimport -d ../build -p com.asert.jaxws http://localhost:8080/Echo?wsdl




                                                                                     SpringOne2GX - 70
Raw CXF
                    • Apache CXF helps you build and develop
                      services. You can use frontend programming
                      APIs, like JAX-WS and support is provided for
                      SOAP, XML/HTTP, RESTful HTTP, ...
                      over HTTP, JMS, JBI, ...
                       – Follow instructions for Java but there is also
© ASERT 2006-2009




                         some special things you can do with Groovy




                                                                SpringOne2GX - 71
Axis2
                    • Apache Axis is a comprehensive
                      implementation of SOAP
                       – Follow the instructions for Java but use
                         Groovy instead and precompile
                       – Use GroovyShell to call script at runtime
                    • Another article:
© ASERT 2006-2009




                      – http://www.developer.com/services/article.ph
                        p/10928_3570031_2




                                                                SpringOne2GX - 72
RESTful options
                    • Several Options
                      – Already supported in discussed
                        frameworks, e.g. CXF
                      – Groovy Restlet DSL
                        http://docs.codehaus.org/display/GROOVY/GroovyRestlet
                      – Jersey
© ASERT 2006-2009




                        http://wikis.sun.com/display/Jersey/Main
                      – restlet.org
                        http://www.restlet.org




                                                                          SpringOne2GX - 73
restlet.org
  import org.restlet.*
  import org.restlet.data.*
  class MailboxResource extends Restlet {
      void handle(Request request, Response response) {
          switch (request.method) {
              case Method.GET:
                  handleGet(request, response)
                  break

                        case Method.PUT:
                            handlePut(request, response)
© ASERT 2006-2009




                            break

                        case Method.POST:
                            handlePost(request, response)
                            break

                        default:
                            // The request method is not allowed; set an error status
                            response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED)
                            response.setAllowedMethods([Method.GET, Method.PUT, Method.POST] as Set)
                    }
      }
      void handleGet(request, response) {
          response.setEntity("Hello, world!", MediaType.TEXT_PLAIN)
      }
  // ...
                                                                                          SpringOne2GX - 74
GroovyRestlet DSL
                    builder.component {
                      current.servers.add(protocol.HTTP, 8182)
                      application(uri: "") {
                        router {
                          def guard = guard(uri: "/docs", scheme: challengeScheme.HTTP_BASIC,
                                  realm: "Restlet Tutorials")
                          guard.secrets.put("scott", "tiger".toCharArray())
                          guard.next = directory(root: "", autoAttach: false)
                          restlet(uri: "/users/{user}", handle: {req, resp ->
                            resp.setEntity("Account of user "${req.attributes.get('user')}"",
                                    mediaType.TEXT_PLAIN)
© ASERT 2006-2009




                          })
                          restlet(uri: "/users/{user}/orders", handle: {req, resp ->
                            resp.setEntity("Orders or user "${req.attributes.get('user')}"",
                                    mediaType.TEXT_PLAIN)
                          })
                          restlet(uri: "/users/{user}/orders/{order}", handle: {req, resp ->
                            def attrs = req.attributes
                            def message =
                            "Order "${attrs.get('order')}" for User "${attrs.get('user')}""
                            resp.setEntity(message, mediaType.TEXT_PLAIN)
                          })
                        }
                      }
                    }.start()      Source: http://docs.codehaus.org/display/GROOVY/GroovyRestlet
                                                                                       SpringOne2GX - 75
Jersey...
                    package com.asert

                    import   javax.ws.rs.GET
                    import   javax.ws.rs.Path
                    import   javax.ws.rs.Produces
                    import   static com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory.*

                    @Path ("/helloworld")
                    class HelloWorldResource {
                        @GET @Produces("text/plain")
© ASERT 2006-2009




                        String getPlainMessage() {
                            "Hello World"
                        }
                    }

                    def baseUri = "http://localhost:9998/"
                    def initParams = ["com.sun.jersey.config.property.packages": "com.asert"]

                    println """
                    Starting grizzly with Jersey...
                    App WADL available at ${baseUri}application.wadl
                    App available at ${baseUri}helloworld
                    """
                    create(baseUri, initParams)

                                                                                             SpringOne2GX - 76
...Jersey...
                    import javax.ws.rs.PathParam
                    import javax.xml.bind.annotation.*

                    class PrettyXml {
                        static print(node) {
                            def writer = new StringWriter()
                            new XmlNodePrinter(new PrintWriter(writer)).print(node)
                            writer.toString()
                        }
                    }
© ASERT 2006-2009




                    @XmlRootElement
                    @XmlAccessorType (XmlAccessType.FIELD)
                    class FlightInfoBean {
                        @XmlAttribute String hours
                        @XmlElement String from, to
                        static populate(num) {
                            def trip = new XmlParser().parse(Flights.reader)
                            def f = trip.flight[num as int]
                            new FlightInfoBean(hours:f.@hours,
                                               from:f.from.text(),
                                               to:f.to.text())
                        }
                    }
                                                                                SpringOne2GX - 77
...Jersey
                    @Path("/flight/xml/{flightnum}")
                    class FlightInfoXml {
                        @GET @Produces("text/xml")
                        String getXmlMessage(@PathParam('flightnum') String num) {
                            def trip = new XmlParser().parse(Flights.reader)
                            PrettyXml.print(trip.flight[num as int])
                        }
                    }

                    @Path("/flight/json/{flightnum}")
© ASERT 2006-2009




                    class FlightInfoJson {
                        @GET @Produces("application/json")
                        FlightInfoBean getJsonMessage(@PathParam('flightnum') String num) {
                            FlightInfoBean.populate(num)
                        }
                    }

                    @Path("/flight/atom/{flightnum}")
                    class FlightInfoAtom {
                        @GET @Produces("application/atom")
                        FlightInfoBean getAtomMessage(@PathParam('flightnum') String num) {
                            FlightInfoBean.populate(num)
                        }
                    }
                                                                                     SpringOne2GX - 78
Jersey output
                    > curl http://localhost:9998/helloworld
                    Hello World

                    > curl http://localhost:9998/flight/xml/1
                    <flight hours="4">
                     <from>
                       Los Angeles
                     </from>
                     <to>
                       New Orleans
© ASERT 2006-2009




                     </to>
                    </flight>

                    > curl http://localhost:9998/flight/json/1
                    {"@hours":"4","from":"Los Angeles","to":"New Orleans"}

                    > curl http://localhost:9998/flight/atom/1
                    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
                    <flightInfoBean hours="4">
                    <from>Los Angeles</from>
                    <to>New Orleans</to>
                    </flightInfoBean>

                                                                              SpringOne2GX - 79
Synapse
                    • Apache Synapse is a simple, lightweight and
                      high performance Enterprise Service Bus (ESB)
                      with support for XML, Web services, binary and
                      text formats
                       – Groovy scripting, endpoints, Synapse DSL
                       – https://svn.apache.org/repos/asf/synapse/
© ASERT 2006-2009




                         trunk/java/src/site/resources/presentations/
                         makingsoagroovyfremantle.pdf




                                                              SpringOne2GX - 80
ServiceMix
                    • ServiceMix is an open source Enterprise Service
                      Bus (ESB) combining Service Oriented
                      Architecture (SOA), Event Driven Architecture
                      (EDA) and Java Business Integration (JBI)
                      functionality
                       – You can use ServiceMix Scripting
© ASERT 2006-2009




                       – You can use legacy ScriptComponent and
                         GroovyComponent
                       – You can write Groovy JBI components




                                                              SpringOne2GX - 81
Tuscany...
                    • Tuscany embodies Service Component
                      Architecture (SCA) which defines a simple,
                      service-based model for construction, assembly
                      and deployment of a network of services
                      (existing and new ones) that are defined in a
                      language-neutral way.”
© ASERT 2006-2009




                       – You can define your services using Groovy
                         either using Java mechanisms
                         or Scripting integration




                                                             SpringOne2GX - 82
...Tuscany...
                    <composite ...>
                        <component name="CalculatorServiceComponent" .../>
                        <component name="AddServiceComponent" .../>
                        <component name="SubtractServiceComponent">
                            <tuscany:implementation.java
                                class="calculator.SubtractServiceImpl"/>
                        </component>
                        <component name="MultiplyServiceComponent">
© ASERT 2006-2009




                            <tuscany:implementation.script language="groovy">
                                def multiply(n1, n2) { using groovyc
                                  Compile your Groovy
                                }
                                   (with*or without annotations) then
                                      n1   n2

                            </tuscany:implementation.script> Need
                                    treat just like normal Java.
                        </component>
                                 groovy jar in your runtime classpath.
                        <component name="DivideServiceComponent">
                            <tuscany:implementation.script
                                script="calculator/DivideServiceImpl.groovy"/>
                        </component>
                    </composite>

                                                                       SpringOne2GX - 83
...Tuscany
                    <composite ...>
                                                    With Groovy scripts either
                        <component name="CalculatorServiceComponent" .../>
                        <component name="AddServiceComponent" .../> files – no
                                                     embedded or in
                        <component name="SubtractServiceComponent">
                                                      compilation necessary.
                            <tuscany:implementation.java
                                class="calculator.SubtractServiceImpl"/>
                        </component>
                        <component name="MultiplyServiceComponent">
© ASERT 2006-2009




                            <tuscany:implementation.script language="groovy">
                                def multiply(n1, n2) {
                                    n1 * n2
                                }
                            </tuscany:implementation.script>
                        </component>
                        <component name="DivideServiceComponent">
                            <tuscany:implementation.script
                                script="calculator/DivideServiceImpl.groovy"/>
                        </component>
                    </composite>

                                                                       SpringOne2GX - 84
More Information: on the web
                    • Web sites
                      –   http://groovy.codehaus.org
                      –   http://grails.codehaus.org
                      –   http://pleac.sourceforge.net/pleac_groovy (many examples)
                      –   http://www.asert.com.au/training/java/GV110.htm (workshop)
                    • Mailing list for users
                      – user@groovy.codehaus.org
© ASERT 2006-2009




                    • Information portals
                      – http://www.aboutgroovy.org
                      – http://www.groovyblogs.org
                    • Documentation (1000+ pages)
                      – Getting Started Guide, User Guide, Developer Guide, Testing
                        Guide, Cookbook Examples, Advanced Usage Guide
                    • Books
                      – Several to choose from ...
                                                                              SpringOne2GX - 85
More Information: Groovy in Action
© ASERT 2006-2009




                                                    SpringOne2GX - 86

XML and Web Services with Groovy

  • 1.
    XML and Web Serviceswith Groovy Dr Paul King, ASERT: @paulk_asert, paulk@asert.com.au SpringOne2GX - 1
  • 2.
    What is Groovy? • “Groovy is like a super version of Java. It can leverage Java's enterprise capabilities but also has cool productivity features like closures, DSL support, builders and dynamic typing.” © ASERT 2006-2009 Groovy = Java – boiler plate code + optional dynamic typing + closures + domain specific languages + builders + metaprogramming SpringOne2GX - 2
  • 3.
    Growing Acceptance … A slow and steady start but now gaining in momentum, maturity and mindshare Now free
  • 4.
    … Growing Acceptance… © ASERT 2006-2009 SpringOne2gx_Oct2009 - 4
  • 5.
    … Growing Acceptance… © ASERT 2006-2009 Groovy and Grails downloads: 70-90K per month and growing SpringOne2gx_Oct2009 - 5
  • 6.
    … Growing Acceptance… © ASERT 2006-2009 Source: http://www.micropoll.com/akira/mpresult/501697-116746 Source: http://www.grailspodcast.com/ SpringOne2gx_Oct2009 - 6
  • 7.
    … Growing Acceptance… © ASERT 2006-2009 http://www.jroller.com/scolebourne/entry/devoxx_2008_whiteboard_votes http://www.java.net SpringOne2gx_Oct2009 - 7
  • 8.
    … Growing Acceptance… What alternative JVM language are you using or intending to use © ASERT 2006-2009 http://www.leonardoborges.com/writings SpringOne2gx_Oct2009 - 8
  • 9.
    … Growing Acceptance… © ASERT 2006-2009 http://it-republik.de/jaxenter/quickvote/results/1/poll/44 (translated using http://babelfish.yahoo.com) SpringOne2gx_Oct2009 - 9
  • 10.
    … Growing Acceptance ©ASERT 2006-2009 SpringOne2gx_Oct2009 - 10
  • 11.
    Better XML Manipulation... <records> records.xml <car name='HSV Maloo' make='Holden' year='2006'> <country>Australia</country> <record type='speed'>Production Pickup Truck with speed of 271kph</record> </car> © ASERT 2006-2009 <car name='P50' make='Peel' year='1962'> <country>Isle of Man</country> <record type='size'>Smallest Street-Legal Car at 99cm wide and 59 kg weight</record> </car> <car name='Royale' make='Bugatti' year='1931'> <country>France</country> <record type='price'>Most Valuable Car at $15 million</record> </car> </records> AUG 2009 - 11
  • 12.
    ...Better XML Manipulation... import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.w3c.dom.Node; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; public class FindYearsJava { public static void main(String[] args) { © ASERT 2006-2009 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder builder = builderFactory.newDocumentBuilder(); Document document = builder.parse(new File("records.xml")); NodeList list = document.getElementsByTagName("car"); for (int i = 0; i < list.getLength(); i++) { Node n = list.item(i); Node year = n.getAttributes().getNamedItem("year"); System.out.println("year = " + year.getTextContent()); } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } AUG 2009 - 12
  • 13.
    ...Better XML Manipulation... import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.w3c.dom.Node; boilerplate import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; public class FindYearsJava { public static void main(String[] args) { © ASERT 2006-2009 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder builder = builderFactory.newDocumentBuilder(); Document document = builder.parse(new File("records.xml")); NodeList list = document.getElementsByTagName("car"); for (int i = 0; i < list.getLength(); i++) { Node n = list.item(i); Node year = n.getAttributes().getNamedItem("year"); System.out.println("year = " + year.getTextContent()); } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } AUG 2009 - 13
  • 14.
    ...Better XML Manipulation def records = new XmlParser().parse("records.xml") records.car.each { © ASERT 2006-2009 println "year = ${it.@year}" } year = 2006 year = 1962 year = 1931 AUG 2009 - 14
  • 15.
    XML SpringOne 2GX 2009. All rights reserved. Do not distribute without permission.
  • 16.
    Groovy and XML... • Reading XML – Special Groovy support: XmlParser, XmlSlurper, DOMCategory – Or Groovy sugar for your current favorites: DOM, SAX, StAX, DOM4J, JDom, XOM, XPath, XSLT, XQuery, etc. © ASERT 2006-2009 • Creating XML – Special Groovy support: MarkupBuilder and StreamingMarkupBuilder – Or again, enhanced syntax for your current favorites SpringOne2GX - 16
  • 17.
    ... Groovy andXML ... • Updating XML – Using above: read followed by create – Can be done with XmlParser, XmlSlurper, DOMCategory – Or with your Java favorites © ASERT 2006-2009 • Verifying XML – Also DTD, W3C XML Schema, Relax NG in a similar fashion to Java mechanisms for these features SpringOne2GX - 17
  • 18.
    ... Groovy andXML • So many technologies – how to choose? – Normally just use XmlSlurper and StreamingMarkupBuilder – Or if you want a DOM, use XmlParser and MarkupBuilder or DOMBuilder – Or if you must have a W3C DOM, use © ASERT 2006-2009 DOMCategory – Or if you expect to have a large amount of legacy or Java parsing code, you can stick with your favorite Java XML API/stack SpringOne2GX - 18
  • 19.
    An Xml Example... import groovy.xml.dom.DOMCategory class Flights { static final String XML = ''' <trip> <flight hours="13"> <from>Brisbane</from> © ASERT 2006-2009 <to>Los Angeles</to> </flight> <flight hours="4"> <from>Los Angeles</from> <to>New Orleans</to> </flight> </trip> ''' ... SpringOne2GX - 19
  • 20.
    ... An XmlExample ... static final Reader getReader() { new StringReader(XML) } static final Set getCities(flights) { Set cities = [] © ASERT 2006-2009 use(DOMCategory) { flights.each { f -> cities += f.to[0].text() cities += f.from[0].text() } } You can mostly ignore cities the details here for now. } We’ll cover DOMCategory } in more detail shortly. SpringOne2GX - 20
  • 21.
    XmlParser def trip = new XmlParser().parseText(Flights.XML) assert trip.flight[0].to.text() == 'Los Angeles' assert trip.flight[1].@hours == '4' Set cities = trip.flight.from*.text() + trip.flight.to*.text() © ASERT 2006-2009 assert cities == ['Brisbane', 'Los Angeles', 'New Orleans'] as Set assert trip.flight.@hours == ['13', '4'] assert trip.flight.@hours*.toInteger().sum() == 17 • Builds an in-memory DOM tree SpringOne2GX - 21
  • 22.
    XmlParser – Underthe covers • For a JavaBean, this Groovy expression: trip.flight[0].to[0].text() • Is “roughly” converted to: trip.getflight().get(0).getTo().get(0).text() © ASERT 2006-2009 where getFlight() and getTo() return a List • But for XML Parser, it overrides this mechanism and “roughly” converts to: trip.getByName('flight').get(0). getByName('to').get(0).text() where getByName is a Groovy method similar to getElementsByTagName in org.w3c.dom.Element SpringOne2GX - 22
  • 23.
    XmlSlurper... • The same again using XmlSlurper – Mostly identical syntax and capabilities def trip = new XmlSlurper().parseText(Flights.XML) assert trip.flight[0].to.text() == 'Los Angeles' assert trip.flight[1].@hours == '4' © ASERT 2006-2009 Set cities = trip.flight.from*.text() + trip.flight.to*.text() assert cities == ['Brisbane', 'Los Angeles', 'New Orleans'] as Set assert trip.flight.@hours.list() == ['13', '4'] assert trip.flight.@hours*.toInteger().sum() == 17 – But features lazy evaluation of expressions – Consider this for streaming scenarios SpringOne2GX - 23
  • 24.
    ...XmlSlurper... • What does Lazy mean? def trip = new XmlSlurper().parseText(Flights.XML) def moreThanFiveHours = { f -> f.@hours.toInteger() > 5 } def arrivingLax = { f -> f.to == 'Los Angeles' } def departingOz = { f -> f.from == 'Brisbane' } © ASERT 2006-2009 def longFlights = trip.flight.findAll(moreThanFiveHours) def longLaxFlights = longFlights.findAll(arrivingLax) def longOzLaxFlights = longLaxFlights.findAll(departingOz) assert longOzLaxFlights.@hours == '13' SpringOne2GX - 24
  • 25.
    ...XmlSlurper Light-weight scanning here def trip = new XmlSlurper().parseText(Flights.XML) def moreThanFiveHours = { f -> f.@hours.toInteger() > 5 } def arrivingLax = { f -> f.to == 'Los Angeles' } def departingOz = { f -> f.from == 'Brisbane' } © ASERT 2006-2009 def longFlights = trip.flight.findAll(moreThanFiveHours) def longLaxFlights = longFlights.findAll(arrivingLax) def longOzLaxFlights = longLaxFlights.findAll(departingOz) assert longOzLaxFlights.@hours == '13' Lazy expression storage Usage triggers evaluation but deferred evaluation • This may look puzzling at first – But the good news is you don’t normally haveSpringOne2GX - 25 to care
  • 26.
    A Namespace Example class Books { static final String XML = ''' <rdf:rdf xmlns:bibterm="http://www.book-stuff.com/terms/" xmlns:dc="http://purl.org/dc/elements/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:description rdf:about="http://www.book-stuff.com/bib"> <bibterm:book rdf:parseType="Resource"> <bibterm:year>2007</bibterm:year> <dc:title>Groovy in Action</dc:title> © ASERT 2006-2009 <bibterm:author rdf:parseType="Resource"> <bibterm:last>König</bibterm:last> <bibterm:first>Dierk</bibterm:first> </bibterm:author> <rdf:comment> Coauthors: Andrew Glover, Paul King, Guillaume Laforge and Jon Skeet </rdf:comment> </bibterm:book> </rdf:description> </rdf:rdf> ''' ... SpringOne2GX - 26
  • 27.
    XmlParser and Namespaces • Recommended syntax: import groovy.xml.* def book = new XmlParser().parseText(Books.XML) def rdf = new Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#') def dc = new Namespace('http://purl.org/dc/elements/1.0/') def bibterm = new Namespace('http://www.book-stuff.com/terms/') def b = book[rdf.description][bibterm.book] assert b[dc.title].text() == 'Groovy in Action' assert b[bibterm.year].text() == '2007' © ASERT 2006-2009 • Options // use string style matching (exact match on prefix or wildcard or URI) assert b.'bibterm:year'.text() == '2007' assert b.'*:year'.text() == '2007' assert b.'http://www.book-stuff.com/terms/:year'.text() == '2007' // Namespace is a QName factory but you can use QName directly def bibtermYear = new QName('http://www.book-stuff.com/terms/', 'year') assert b[bibtermYear].text() == '2007' // use QName with wildcards def anyYear = new QName('*', 'year') assert b[anyYear].text() == '2007' SpringOne2GX - 27
  • 28.
    XmlSlurper and Namespaces def book = new XmlSlurper().parseText(Books.XML) book.declareNamespace( rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', dc: 'http://purl.org/dc/elements/1.0/', bibterm: 'http://www.book-stuff.com/terms/') def b = book.'rdf:description'.'bibterm:book' © ASERT 2006-2009 assert b.'dc:title' == 'Groovy in Action' assert b.'bibterm:year' == '2007' SpringOne2GX - 28
  • 29.
    Other GPath Features • Similar syntax for XmlSlurper and DOMCategory def trip = new XmlParser().parseText(Flights.XML) assert trip.'**'*.name() == ['trip', 'flight', 'from', 'to', 'flight', 'from', 'to'] assert trip.depthFirst()*.name() == ['trip', 'flight', 'from', 'to', 'flight', 'from', 'to'] assert trip.breadthFirst()*.name() == © ASERT 2006-2009 ['trip', 'flight', 'flight', 'from', 'to', 'from', 'to'] assert trip.'**'.from*.text() == ['Brisbane', 'Los Angeles'] SpringOne2GX - 29
  • 30.
    What about non-XML? @Grab('nekohtml:nekohtml:1.9.6.2') import org.cyberneko.html.parsers.SAXParser def neko = new SAXParser() neko.setFeature('http://xml.org/sax/features/namespaces', false) def page = new XmlParser(neko).parse('http://groovy.codehaus.org/') def data = page.depthFirst().A.'@href'.grep{ it != null && it.endsWith('.html') } © ASERT 2006-2009 data.each { println it } def neko = new SAXParser() // alternate style def page = new XmlSlurper(neko).parse('http://groovy.codehaus.org/') def data = page.depthFirst().grep{ it.name() == 'A' && it.@href.toString().endsWith('.html') }.'@href' data.each { println it } http://groovy.javanicus.com/search.html http://www.dcs.napier.ac.uk/~cs05/groovy/groovy.html http://www.ej-technologies.com/products/jprofiler/overview.html SpringOne2GX - 30
  • 31.
    Raw DOM import groovy.xml.DOMBuilder def trip = DOMBuilder.parse(Flights.reader).documentElement def flights = trip.getElementsByTagName('flight') def dest = flights.item(0).getElementsByTagName('to').item(0) © ASERT 2006-2009 assert dest.firstChild.nodeValue == 'Los Angeles' assert flights.item(1).getAttribute('hours') == '4' assert Flights.getCities(flights) == ['Brisbane', 'Los Angeles', 'New Orleans'] as Set SpringOne2GX - 31
  • 32.
    DOM plus metaprogramming import groovy.xml.DOMBuilder import org.w3c.dom.Element def trip = DOMBuilder.parse(Flights.reader).documentElement Element.metaClass.element = { t, i -> © ASERT 2006-2009 delegate.getElementsByTagName(t).item(i) } Element.metaClass.text = {-> delegate.firstChild.nodeValue } assert trip.element('flight', 0).element('to', 0).text() == 'Los Angeles' assert trip.element('flight', 1).getAttribute('hours') == '4' SpringOne2GX - 32
  • 33.
    DOMCategory import groovy.xml.DOMBuilder import groovy.xml.dom.DOMCategory def doc = DOMBuilder.parse(Flights.reader) def trip = doc.documentElement © ASERT 2006-2009 use(DOMCategory) { assert trip.flight[0].to[0].text() == 'Los Angeles' assert trip.flight[1].'@hours' == '4' assert Flights.getCities(trip.flight) == ['Brisbane', 'Los Angeles', 'New Orleans'] as Set } SpringOne2GX - 33
  • 34.
    DOM4J @Grab('dom4j:dom4j:1.6.1') import org.dom4j.io.SAXReader def trip = new SAXReader().read(Flights.reader).rootElement © ASERT 2006-2009 assert trip.elements()[0].elementText('to') == 'Los Angeles' assert trip.elements()[1].attributeValue('hours') == '4' SpringOne2GX - 34
  • 35.
    JDOM @Grab('org.jdom:jdom:1.1') import org.jdom.input.SAXBuilder def b = new SAXBuilder() def trip = b.build(Flights.reader).rootElement © ASERT 2006-2009 assert trip.children[0].getChildText('to') == 'Los Angeles' assert trip.children[1].getAttribute('hours').value == '4' SpringOne2GX - 35
  • 36.
    XOM @Grab('xom:xom:1.1') import nu.xom.Builder def doc = new Builder().build(Flights.reader) def flights = doc.rootElement.childElements def to = flights.get(0).getFirstChildElement('to') © ASERT 2006-2009 assert to.value == 'Los Angeles' def hours = flights.get(1).getAttribute('hours') assert hours.value == '4' SpringOne2GX - 36
  • 37.
    StAX import static javax.xml.stream.XMLInputFactory.newInstance as staxFactory import javax.xml.stream.XMLStreamReader as StaxReader def flights = [] def flight def seenTag StaxReader.metaClass.attr = { s -> delegate.getAttributeValue(null, s) } def reader = staxFactory().createXMLStreamReader(Flights.reader) while (reader.hasNext()) { def name = reader.localName © ASERT 2006-2009 if (reader.startElement) { if (name == 'flight') flight = [hours:reader.attr('hours')] else if (name in ['from', 'to']) seenTag = name } else if (reader.characters) { if (seenTag) flight[seenTag] = reader.text } else if (reader.endElement) { if (name == 'flight') flights += flight seenTag = null } reader.next() } assert flights[0].to == 'Los Angeles' assert flights[1].hours == '4' SpringOne2GX - 37
  • 38.
    SAX import javax.xml.parsers.SAXParserFactory import org.xml.sax.* import org.xml.sax.helpers.DefaultHandler class TripHandler extends DefaultHandler { def flights = [] private flight, seenTag void startElement(String ns, String localName, String qName, Attributes atts) { if (qName == 'flight') flight = [hours:atts.getValue('hours')] else if (qName in ['from', 'to']) seenTag = qName } © ASERT 2006-2009 public void endElement(String uri, String localName, String qName) { if (qName == 'flight') flights += flight seenTag = null } public void characters(char[] ch, int start, int length) { if (seenTag) flight[seenTag] = new String(ch, start, length) } } def handler = new TripHandler() def reader = SAXParserFactory.newInstance().newSAXParser().xMLReader reader.setContentHandler(handler) reader.parse(new InputSource(Flights.reader)) assert handler.flights[0].to == 'Los Angeles' assert handler.flights[1].hours == '4' SpringOne2GX - 38
  • 39.
    XPath import javax.xml.xpath.* import groovy.xml.DOMBuilder def xpath = XPathFactory.newInstance().newXPath() def trip = DOMBuilder.parse(Flights.reader).documentElement assert xpath.evaluate('flight/to/text()', trip) == 'Los Angeles' assert xpath.evaluate('flight[2]/@hours', trip) == '4' © ASERT 2006-2009 def flights = xpath.evaluate( 'flight', trip, XPathConstants.NODESET ) def hoursAsInt = { n -> xpath.evaluate('@hours', n).toInteger() } assert flights.collect(hoursAsInt).sum() == 17 assert Flights.getCities(flights) == ['Brisbane', 'Los Angeles', 'New Orleans'] as Set SpringOne2GX - 39
  • 40.
    XPath with DOMCategory import groovy.xml.DOMBuilder import groovy.xml.dom.DOMCategory import static javax.xml.xpath.XPathConstants.* def trip = DOMBuilder.parse(Flight.reader).documentElement © ASERT 2006-2009 use (DOMCategory) { assert trip.xpath('flight/to/text()') == 'Los Angeles' assert trip.xpath('flight[2]/@hours', NUMBER) == 4 flights = trip.xpath('flight', NODESET) def hoursAsNum = { n -> n.xpath('@hours', NUMBER) } assert flights.collect(hoursAsNum).sum() == 17 } SpringOne2GX - 40
  • 41.
    Xalan XPath @Grab('xalan:xalan:2.7.1') import static org.apache.xpath.XPathAPI.* import groovy.xml.DOMBuilder def trip = DOMBuilder.parse(Flights.reader).documentElement assert eval(trip, 'flight/to/text()').str() == © ASERT 2006-2009 'Los Angeles' assert eval(trip, 'flight[2]/@hours').str() == '4' def flights = selectNodeList(trip, '//flight') def hoursAsInt = { n -> eval(n, '@hours').str().toInteger() } assert flights.collect(hoursAsInt).sum() == 17 assert Flights.getCities(flights) == ['Brisbane', 'Los Angeles', 'New Orleans'] as Set SpringOne2GX - 41
  • 42.
    Jaxen XPath @Grab('jaxen#jaxen;1.1.1') import org.jaxen.dom.DOMXPath import groovy.xml.DOMBuilder def trip = DOMBuilder.parse(Flights.reader).documentElement assert new DOMXPath('flight/to/text()'). stringValueOf(trip) == 'Los Angeles' © ASERT 2006-2009 assert new DOMXPath('flight[2]/@hours'). stringValueOf(trip) == '4' def flights = new DOMXPath('flight').selectNodes(trip) def hoursAsInt = { n -> new DOMXPath('@hours').numberValueOf(n) } assert flights.collect(hoursAsInt).sum() == 17 assert Flights.getCities(flights) == ['Brisbane', 'Los Angeles', 'New Orleans'] as Set SpringOne2GX - 42
  • 43.
    JSR 225 -XQJ import net.sf.saxon.xqj.SaxonXQDataSource import javax.xml.xquery.XQSequence XQSequence.metaClass.collect = { Closure c -> def items = [] while (delegate.next()) items += c(delegate) items } def asString = { seq -> seq.getItemAsString(null) } © ASERT 2006-2009 def hourAttr = { it.item.node.getAttribute('hours') as int } def flights = "document { ${Flights.XML} }" def exp = new SaxonXQDataSource().connection.createExpression() def seq = exp.executeQuery("$flights/trip/flight/to/text()") assert seq.collect(asString) == ['Los Angeles', 'New Orleans'] seq = exp.executeQuery("$flights/trip/flight") assert seq.collect(hourAttr).sum() == 17 SpringOne2GX - 43
  • 44.
    XSLT... import static javax.xml.transform.TransformerFactory.newInstance as xsltFactory import javax.xml.transform.stream.* def xslt = ''' <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/trip"> <html> <body> <h1>Flights</h1> <ul> <xsl:apply-templates select="flight"/> </ul> © ASERT 2006-2009 </body> </html> </xsl:template> <xsl:template match="flight"> <li> <xsl:value-of select="from"/> => <xsl:value-of select="to"/> </li> </xsl:template> </xsl:stylesheet> '''.trim() def transformer = xsltFactory().newTransformer( new StreamSource(new StringReader(xslt))) transformer.transform(new StreamSource(Flights.reader), new StreamResult(System.out)) SpringOne2GX - 44
  • 45.
    ...XSLT import static javax.xml.transform.TransformerFactory.newInstance as xsltFactory import javax.xml.transform.stream.* def xslt = ''' <html> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/trip"> <body> <html> <h1>Flights</h1> <body> <ul> <h1>Flights</h1> <li>Brisbane =&gt; Los Angeles</li> <ul> <li>Los Angeles =&gt; New Orleans</li> </ul> <xsl:apply-templates select="flight"/> </ul> </body> © ASERT 2006-2009 </body> </html> </html> </xsl:template> <xsl:template match="flight"> <li> <xsl:value-of select="from"/> => <xsl:value-of select="to"/> </li> </xsl:template> </xsl:stylesheet> '''.trim() def transformer = xsltFactory().newTransformer( new StreamSource(new StringReader(xslt))) transformer.transform(new StreamSource(Flights.reader), new StreamResult(System.out)) SpringOne2GX - 45
  • 46.
    MarkupBuilder import groovy.xml.MarkupBuilder def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.flights { flight(hours:13) { © ASERT 2006-2009 from('Brisbane') to('Los Angeles') } flight(hours:4) { from('Los Angeles') to('New Orleans') } } println writer SpringOne2GX - 46
  • 47.
    StreamingMarkupBuilder import groovy.xml.StreamingMarkupBuilder def writer = new StreamingMarkupBuilder().bind { flights { flight(hours: 13) { from('Brisbane') © ASERT 2006-2009 to('Los Angeles') } flight(hours: 4) { from('Los Angeles') to('New Orleans') } } } println writer SpringOne2GX - 47
  • 48.
    DOMBuilder import groovy.xml.* def builder = DOMBuilder.newInstance() def root = builder.flights { flight(hours: 13) { from('Brisbane') © ASERT 2006-2009 to('Los Angeles') } flight(hours: 4) { from('Los Angeles') to('New Orleans') } } new XmlNodePrinter().print(root) SpringOne2GX - 48
  • 49.
    MarkupBuilder with Namespaces import groovy.xml.MarkupBuilder def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.'rdf:description'( 'xmlns:bibterm': "http://www.book-stuff.com/terms/", 'xmlns:dc': "http://purl.org/dc/elements/1.0/", 'xmlns:rdf': "http://www.w3.org/1999/02/22-rdf-syntax- © ASERT 2006-2009 ns#") { 'bibterm:book' { 'dc:title'('Groovy in Action') 'bibterm:year'('2007') } <rdf:description xmlns:dc='http://purl.org/dc/elements/1.0/' } xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' println writer xmlns:bibterm='http://www.book-stuff.com/terms/'> <bibterm:book> <dc:title>Groovy in Action</dc:title> <bibterm:year>2007</bibterm:year> </bibterm:book> </rdf:description> SpringOne2GX - 49
  • 50.
    StreamingMarkupBuilder with Namespaces import groovy.xml.* XmlUtil.serialize(new StreamingMarkupBuilder().bind { mkp.declareNamespace( bibterm: "http://www.book-stuff.com/terms/", dc: "http://purl.org/dc/elements/1.0/", rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#") 'rdf:description' { © ASERT 2006-2009 'bibterm:book' { 'dc:title'('Groovy in Action') 'bibterm:year'('2007') } } <rdf:description xmlns:dc='http://purl.org/dc/elements/1.0/' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' }, System.out) xmlns:bibterm='http://www.book-stuff.com/terms/'> <bibterm:book> <dc:title>Groovy in Action</dc:title> <bibterm:year>2007</bibterm:year> </bibterm:book> </rdf:description> SpringOne2GX - 50
  • 51.
    DOMBuilder with Namespaces import groovy.xml.DOMBuilder def b = DOMBuilder.newInstance() def root = b.'rdf:description'( 'xmlns:bibterm': "http://www.book-stuff.com/terms/", 'xmlns:dc': "http://purl.org/dc/elements/1.0/", 'xmlns:rdf': "http://www.w3.org/1999/02/22-rdf-syntax-ns#") { 'bibterm:book' { 'dc:title'('Groovy in Action') © ASERT 2006-2009 'bibterm:year'('2007') } } new XmlNodePrinter().print(root) <rdf:description xmlns:dc='http://purl.org/dc/elements/1.0/' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:bibterm='http://www.book-stuff.com/terms/'> <bibterm:book> <dc:title>Groovy in Action</dc:title> <bibterm:year>2007</bibterm:year> </bibterm:book> </rdf:description> SpringOne2GX - 51
  • 52.
    DOMBuilder with NamespaceBuilder import groovy.xml.* def b = NamespaceBuilder.newInstance(DOMBuilder.newInstance()) b.namespace('http://www.book-stuff.com/terms/', 'bibterm') b.namespace('http://purl.org/dc/elements/1.0/', 'dc') b.namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf') def root = b.'rdf:description' { 'bibterm:book' { 'dc:title'('Groovy in Action') © ASERT 2006-2009 'bibterm:year'('2007') } } new XmlNodePrinter().print(root) <?xml version="1.0" encoding="UTF-8"?> <rdf:description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <bibterm:book xmlns:bibterm="http://www.book-stuff.com/terms/"> <dc:title xmlns:dc="http://purl.org/dc/elements/1.0/">Groovy in Action</dc:title> <bibterm:year>2007</bibterm:year> </bibterm:book> </rdf:description> SpringOne2GX - 52
  • 53.
    StaxBuilder import javax.xml.stream.XMLOutputFactory import groovy.xml.StaxBuilder def factory = XMLOutputFactory.newInstance() def writer = new StringWriter() def xmlwriter = factory.createXMLStreamWriter(writer) def builder = new StaxBuilder(xmlwriter) builder.flights { © ASERT 2006-2009 flight(hours: 13) { from('Brisbane') to('Los Angeles') } flight(hours: 4) { from('Los Angeles') to('New Orleans') } } println writer SpringOne2GX - 53
  • 54.
    StaxBuilder for non-XML(JSON) @Grab('org.codehaus.jettison#jettison;1.1') import org.codehaus.jettison.mapped.* import groovy.xml.StaxBuilder def writer = new StringWriter() def con = new MappedNamespaceConvention() def mappedWriter = new MappedXMLStreamWriter(con, writer) def builder = new StaxBuilder(mappedWriter) © ASERT 2006-2009 builder.flights { flight(hours: 13) { from('Brisbane') {"flights":{"flight":[ to('Los Angeles') { "@hours":"13", } "from":"Brisbane", flight(hours: 4) { "to":"Los Angeles" }, from('Los Angeles') { "@hours":"4", to('New Orleans') "from":"Los Angeles", } "to":"New Orleans" } } ]}} println writer SpringOne2GX - 54
  • 55.
    Updating XML <shopping> <shopping> <category type="groceries"> <category type="groceries"> <item>Luxury Chocolate</item> <item>Chocolate</item> <item>Luxury Coffee</item> <item>Coffee</item> </category> </category> <category type="supplies"> <category type="supplies"> <item>Paper</item> <item>Paper</item> <item quantity="6" © ASERT 2006-2009 <item quantity="4">Pens</item> when="Urgent">Pens</item> </category> </category> <category type="present"> <category type="present"> <item when="Aug 10"> <item>Mum's Birthday</item> Kathryn's Birthday <item when="Oct 15"> </item> Monica's Birthday </category> </item> </shopping> </category> </shopping> SpringOne2GX - 55
  • 56.
    Updating with XmlParser ... def root = new XmlParser().parseText(Shopping.XML) // modify groceries: luxury items please root.category.find{ it.@type == 'groceries' }.item.each{ g -> g.value = 'Luxury ' + g.text() } // modify supplies: we need extra pens now root.category.find{ © ASERT 2006-2009 it.@type == 'supplies' }.item.findAll{ it.text() == 'Pens' }.each{ p -> p.@quantity = p.@quantity.toInteger() + 2 p.@when = 'Urgent' } // modify presents: August has come and gone def presents = root.category.find{ it.@type == 'present' } presents.children().clear() presents.appendNode('item', "Mum's Birthday") presents.appendNode('item', [when:'Oct 15'], "Monica's Birthday") ... SpringOne2GX - 56
  • 57.
    Updating with XmlSlurper ... // modify groceries: luxury items please def groceries = root.category.find{ it.@type == 'groceries' } (0..<groceries.item.size()).each { groceries.item[it] = 'Luxury ' + groceries.item[it] } // modify supplies: we need extra pens now root.category.find{ it.@type == 'supplies' © ASERT 2006-2009 }.item.findAll{ it.text() == 'Pens' }.each { p -> p.@quantity = (p.@quantity.toInteger() + 2).toString() p.@when = 'Urgent' } // modify presents: August has come and gone root.category.find{ it.@type == 'present' }.replaceNode{ node -> category(type:'present'){ item("Mum's Birthday") item("Monica's Birthday", when:'Oct 15') } } ... SpringOne2GX - 57
  • 58.
    Updating with DOMCategory use(DOMCategory) { // modify groceries: luxury items please def groceries = root.category.find{ it.'@type' == 'groceries' }.item groceries.each { g -> g.value = 'Luxury ' + g.text() } // modify supplies: we need extra pens now def supplies = root.category.find{ it.'@type' == 'supplies' }.item supplies.findAll{ it.text() == 'Pens' }.each { p -> © ASERT 2006-2009 p['@quantity'] = p.'@quantity'.toInteger() + 2 p['@when'] = 'Urgent' } // modify presents: August has come and gone def presents = root.category.find{ it.'@type' == 'present' } presents.item.each { presents.removeChild(it) } presents.appendNode('item', "Mum's Birthday") presents.appendNode('item', [when:'Oct 15'], "Monica's Birthday") ... } SpringOne2GX - 58
  • 59.
    Validating against aDTD... def xml = ''' <!DOCTYPE records [ <!ELEMENT car (country,record)> <!ATTLIST car make NMTOKEN #REQUIRED name CDATA #REQUIRED year NMTOKEN #REQUIRED > <!ELEMENT country (#PCDATA)> <!ELEMENT record (#PCDATA)> © ASERT 2006-2009 <!ATTLIST record type NMTOKEN #REQUIRED> <!ELEMENT records (car+)> ]> <records> <car name="HSV Maloo" make="Holden" year="2006"> <country>Australia</country> <record type="speed">Production Pickup Truck with speed of 271kph</record> </car> ... </records> '''.trim() SpringOne2GX - 59
  • 60.
    ...Validating against aDTD def validating = true def namespaceAware = false new XmlParser(validating, namespaceAware).parseText(xml) © ASERT 2006-2009 new XmlSlurper(validating, namespaceAware).parseText(xml) import groovy.xml.DOMBuilder DOMBuilder.parse(new StringReader(xml), validating, namespaceAware) SpringOne2GX - 60
  • 61.
    Validating against aW3C Schema... def xsd = ''' <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:element name="records"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" ref="car"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="car"> <xs:complexType> <xs:sequence> © ASERT 2006-2009 <xs:element ref="country"/> <xs:element ref="record"/> </xs:sequence> <xs:attribute name="make" use="required" type="xs:NCName"/> <xs:attribute name="name" use="required"/> <xs:attribute name="year" use="required" type="xs:integer"/> </xs:complexType> </xs:element> <xs:element name="country" type="xs:string"/> <xs:element name="record"> <xs:complexType mixed="true"> <xs:attribute name="type" use="required" type="xs:NCName"/> </xs:complexType> </xs:element> </xs:schema> '''.trim() SpringOne2GX - 61
  • 62.
    ...Validating against aW3C Schema import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI import javax.xml.transform.stream.StreamSource import javax.xml.validation.SchemaFactory def factory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI) // println factory.isSchemaLanguageSupported(W3C_XML_SCHEMA_NS_URI) © ASERT 2006-2009 def schema = factory.newSchema(new StreamSource(new StringReader(xsd))) def validator = schema.newValidator() validator.validate( new StreamSource(new StringReader(XmlExamples.CAR_RECORDS))) SpringOne2GX - 62
  • 63.
    Validating against aRelaxNG Schema... def rng = ''' <grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> <start> <ref name="records"/> </start> <define name="car"> <element name="car"> <attribute name="make"> <data type="token"/> ... </attribute> <define name="record"> <attribute name="name"> © ASERT 2006-2009 <element name="record"> <text/> <attribute name="type"> </attribute> <data type="token"/> <attribute name="year"> </attribute> <data type="integer"/> <text/> </attribute> </element> <ref name="country"/> </define> <ref name="record"/> <define name="records"> </element> <element name="records"> </define> <oneOrMore> <define name="country"> <ref name="car"/> <element name="country"> </oneOrMore> <text/> </element> </element> </define> </define> </grammar> ... '''.trim() SpringOne2GX - 63
  • 64.
    ...Validating against aRelaxNG Schema // require isorelax.jar, isorelax-jaxp-bridge.jar // require one of Jing, MSV, ... import static javax.xml.XMLConstants.RELAXNG_NS_URI import javax.xml.transform.stream.StreamSource import javax.xml.validation.SchemaFactory def factory = SchemaFactory.newInstance(RELAXNG_NS_URI) © ASERT 2006-2009 def schema = factory.newSchema(new StreamSource(new StringReader(rng))) def validator = schema.newValidator() validator.validate(new StreamSource(new StringReader(CAR_RECORDS))) SpringOne2GX - 64
  • 65.
    WEB SERVICES SpringOne 2GX 2009. All rights reserved. Do not distribute without permission.
  • 66.
    Groovy and WebServices • SOAP Web Services – GroovySOAP using XFire for Java 1.4 – GroovyWS using CXF for Java 1.5+ – JAXB out of the box for Java 6 – CXF, Axis2, Spring Web Services © ASERT 2006-2009 • RESTful Options – Restlet.org, RESTlet DSL, roll your own – JAX-RS: Jersey, CXF, JBoss RESTeasy • Frameworks layered upon SOA – Synapse, Tuscany, ServiceMix SpringOne2GX - 66
  • 67.
    GroovySOAP class MathService { • XFire based double add(double a, double b) { a + b • Java 1.4 } double square(double c) { c * c } } import groovy.net.soap.SoapServer © ASERT 2006-2009 def server = new SoapServer('localhost', 6789) server.setNode('MathService') server.start() import groovy.net.soap.SoapClient def url = 'http://localhost:6789/MathServiceInterface?wsdl' def math = new SoapClient(url) assert math.add(1.0, 2.0) == 3.0 assert math.square(3.0) == 9.0 SpringOne2GX - 67
  • 68.
    GroovyWS class MathService { • CXF based double add(double a, double b) { a + b • Java 1.5+ } double square(double c) { c * c } } import groovyx.net.ws.WSServer © ASERT 2006-2009 def server = new WSServer() server.setNode MathService.name, "http://localhost:6980/MathService" import groovyx.net.ws.WSClient def url = "http://localhost:6980/MathService?wsdl" def proxy = new WSClient(url, this.class.classLoader) def result = proxy.add(1.0d, 2.0d) assert result == 3.0d result = proxy.square(3.0d) assert result == 9.0d SpringOne2GX - 68
  • 69.
    JAXB Server import javax.xml.ws.Endpoint import javax.jws.WebService import javax.jws.soap.SOAPBinding import javax.jws.WebMethod @WebService(name="Echo", serviceName="EchoService", targetNamespace="http://jaxws.asert.com") @SOAPBinding(style=SOAPBinding.Style.RPC) © ASERT 2006-2009 class EchoImpl { @WebMethod(operationName = "echo") String echo(String message) { println "Received: $message" "nYou said: " + message } } Endpoint.publish("http://localhost:8080/Echo", new EchoImpl()) println 'EchoService published and running ...' SpringOne2GX - 69
  • 70.
    JAXB Client • JAXB Client import javax.xml.namespace.QName import com.asert.jaxws.EchoService def url = new URL("http://localhost:8080/Echo?wsdl") def qname = new QName("http://jaxws.asert.com", "EchoService") def echoServer = new EchoService(url, qname).echoPort © ASERT 2006-2009 println echoServer.echo("Today is ${new Date()}") • Build instructions wsimport -d ../build -p com.asert.jaxws http://localhost:8080/Echo?wsdl SpringOne2GX - 70
  • 71.
    Raw CXF • Apache CXF helps you build and develop services. You can use frontend programming APIs, like JAX-WS and support is provided for SOAP, XML/HTTP, RESTful HTTP, ... over HTTP, JMS, JBI, ... – Follow instructions for Java but there is also © ASERT 2006-2009 some special things you can do with Groovy SpringOne2GX - 71
  • 72.
    Axis2 • Apache Axis is a comprehensive implementation of SOAP – Follow the instructions for Java but use Groovy instead and precompile – Use GroovyShell to call script at runtime • Another article: © ASERT 2006-2009 – http://www.developer.com/services/article.ph p/10928_3570031_2 SpringOne2GX - 72
  • 73.
    RESTful options • Several Options – Already supported in discussed frameworks, e.g. CXF – Groovy Restlet DSL http://docs.codehaus.org/display/GROOVY/GroovyRestlet – Jersey © ASERT 2006-2009 http://wikis.sun.com/display/Jersey/Main – restlet.org http://www.restlet.org SpringOne2GX - 73
  • 74.
    restlet.org importorg.restlet.* import org.restlet.data.* class MailboxResource extends Restlet { void handle(Request request, Response response) { switch (request.method) { case Method.GET: handleGet(request, response) break case Method.PUT: handlePut(request, response) © ASERT 2006-2009 break case Method.POST: handlePost(request, response) break default: // The request method is not allowed; set an error status response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED) response.setAllowedMethods([Method.GET, Method.PUT, Method.POST] as Set) } } void handleGet(request, response) { response.setEntity("Hello, world!", MediaType.TEXT_PLAIN) } // ... SpringOne2GX - 74
  • 75.
    GroovyRestlet DSL builder.component { current.servers.add(protocol.HTTP, 8182) application(uri: "") { router { def guard = guard(uri: "/docs", scheme: challengeScheme.HTTP_BASIC, realm: "Restlet Tutorials") guard.secrets.put("scott", "tiger".toCharArray()) guard.next = directory(root: "", autoAttach: false) restlet(uri: "/users/{user}", handle: {req, resp -> resp.setEntity("Account of user "${req.attributes.get('user')}"", mediaType.TEXT_PLAIN) © ASERT 2006-2009 }) restlet(uri: "/users/{user}/orders", handle: {req, resp -> resp.setEntity("Orders or user "${req.attributes.get('user')}"", mediaType.TEXT_PLAIN) }) restlet(uri: "/users/{user}/orders/{order}", handle: {req, resp -> def attrs = req.attributes def message = "Order "${attrs.get('order')}" for User "${attrs.get('user')}"" resp.setEntity(message, mediaType.TEXT_PLAIN) }) } } }.start() Source: http://docs.codehaus.org/display/GROOVY/GroovyRestlet SpringOne2GX - 75
  • 76.
    Jersey... package com.asert import javax.ws.rs.GET import javax.ws.rs.Path import javax.ws.rs.Produces import static com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory.* @Path ("/helloworld") class HelloWorldResource { @GET @Produces("text/plain") © ASERT 2006-2009 String getPlainMessage() { "Hello World" } } def baseUri = "http://localhost:9998/" def initParams = ["com.sun.jersey.config.property.packages": "com.asert"] println """ Starting grizzly with Jersey... App WADL available at ${baseUri}application.wadl App available at ${baseUri}helloworld """ create(baseUri, initParams) SpringOne2GX - 76
  • 77.
    ...Jersey... import javax.ws.rs.PathParam import javax.xml.bind.annotation.* class PrettyXml { static print(node) { def writer = new StringWriter() new XmlNodePrinter(new PrintWriter(writer)).print(node) writer.toString() } } © ASERT 2006-2009 @XmlRootElement @XmlAccessorType (XmlAccessType.FIELD) class FlightInfoBean { @XmlAttribute String hours @XmlElement String from, to static populate(num) { def trip = new XmlParser().parse(Flights.reader) def f = trip.flight[num as int] new FlightInfoBean(hours:f.@hours, from:f.from.text(), to:f.to.text()) } } SpringOne2GX - 77
  • 78.
    ...Jersey @Path("/flight/xml/{flightnum}") class FlightInfoXml { @GET @Produces("text/xml") String getXmlMessage(@PathParam('flightnum') String num) { def trip = new XmlParser().parse(Flights.reader) PrettyXml.print(trip.flight[num as int]) } } @Path("/flight/json/{flightnum}") © ASERT 2006-2009 class FlightInfoJson { @GET @Produces("application/json") FlightInfoBean getJsonMessage(@PathParam('flightnum') String num) { FlightInfoBean.populate(num) } } @Path("/flight/atom/{flightnum}") class FlightInfoAtom { @GET @Produces("application/atom") FlightInfoBean getAtomMessage(@PathParam('flightnum') String num) { FlightInfoBean.populate(num) } } SpringOne2GX - 78
  • 79.
    Jersey output > curl http://localhost:9998/helloworld Hello World > curl http://localhost:9998/flight/xml/1 <flight hours="4"> <from> Los Angeles </from> <to> New Orleans © ASERT 2006-2009 </to> </flight> > curl http://localhost:9998/flight/json/1 {"@hours":"4","from":"Los Angeles","to":"New Orleans"} > curl http://localhost:9998/flight/atom/1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <flightInfoBean hours="4"> <from>Los Angeles</from> <to>New Orleans</to> </flightInfoBean> SpringOne2GX - 79
  • 80.
    Synapse • Apache Synapse is a simple, lightweight and high performance Enterprise Service Bus (ESB) with support for XML, Web services, binary and text formats – Groovy scripting, endpoints, Synapse DSL – https://svn.apache.org/repos/asf/synapse/ © ASERT 2006-2009 trunk/java/src/site/resources/presentations/ makingsoagroovyfremantle.pdf SpringOne2GX - 80
  • 81.
    ServiceMix • ServiceMix is an open source Enterprise Service Bus (ESB) combining Service Oriented Architecture (SOA), Event Driven Architecture (EDA) and Java Business Integration (JBI) functionality – You can use ServiceMix Scripting © ASERT 2006-2009 – You can use legacy ScriptComponent and GroovyComponent – You can write Groovy JBI components SpringOne2GX - 81
  • 82.
    Tuscany... • Tuscany embodies Service Component Architecture (SCA) which defines a simple, service-based model for construction, assembly and deployment of a network of services (existing and new ones) that are defined in a language-neutral way.” © ASERT 2006-2009 – You can define your services using Groovy either using Java mechanisms or Scripting integration SpringOne2GX - 82
  • 83.
    ...Tuscany... <composite ...> <component name="CalculatorServiceComponent" .../> <component name="AddServiceComponent" .../> <component name="SubtractServiceComponent"> <tuscany:implementation.java class="calculator.SubtractServiceImpl"/> </component> <component name="MultiplyServiceComponent"> © ASERT 2006-2009 <tuscany:implementation.script language="groovy"> def multiply(n1, n2) { using groovyc Compile your Groovy } (with*or without annotations) then n1 n2 </tuscany:implementation.script> Need treat just like normal Java. </component> groovy jar in your runtime classpath. <component name="DivideServiceComponent"> <tuscany:implementation.script script="calculator/DivideServiceImpl.groovy"/> </component> </composite> SpringOne2GX - 83
  • 84.
    ...Tuscany <composite ...> With Groovy scripts either <component name="CalculatorServiceComponent" .../> <component name="AddServiceComponent" .../> files – no embedded or in <component name="SubtractServiceComponent"> compilation necessary. <tuscany:implementation.java class="calculator.SubtractServiceImpl"/> </component> <component name="MultiplyServiceComponent"> © ASERT 2006-2009 <tuscany:implementation.script language="groovy"> def multiply(n1, n2) { n1 * n2 } </tuscany:implementation.script> </component> <component name="DivideServiceComponent"> <tuscany:implementation.script script="calculator/DivideServiceImpl.groovy"/> </component> </composite> SpringOne2GX - 84
  • 85.
    More Information: onthe web • Web sites – http://groovy.codehaus.org – http://grails.codehaus.org – http://pleac.sourceforge.net/pleac_groovy (many examples) – http://www.asert.com.au/training/java/GV110.htm (workshop) • Mailing list for users – user@groovy.codehaus.org © ASERT 2006-2009 • Information portals – http://www.aboutgroovy.org – http://www.groovyblogs.org • Documentation (1000+ pages) – Getting Started Guide, User Guide, Developer Guide, Testing Guide, Cookbook Examples, Advanced Usage Guide • Books – Several to choose from ... SpringOne2GX - 85
  • 86.
    More Information: Groovyin Action © ASERT 2006-2009 SpringOne2GX - 86