XML and Web Services with Groovy
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

XML and Web Services with Groovy

  • 5,580 views
Uploaded on

Dr Paul King's presentation slides on \'XML and Web Services with Groovy\'

Dr Paul King's presentation slides on \'XML and Web Services with Groovy\'

More in: Technology , Education
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
5,580
On Slideshare
5,559
From Embeds
21
Number of Embeds
6

Actions

Shares
Downloads
281
Comments
0
Likes
12

Embeds 21

http://www.slideshare.net 9
http://www.techgig.com 7
http://www.linkedin.com 2
http://codecamp.pbwiki.com 1
http://techgig.in 1
http://timesjobs.techgig.com 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. XML and Web Services © ASERT 2006-2008 with Groovy Dr Paul King paulk@asert.com.au ASERT, Australia codecamp 2008 - 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-2008 Groovy = Java – boiler plate code + optional dynamic typing + closures + domain specific languages + builders + metaprogramming codecamp 2008 - 2
  • 3. Growing Acceptance • A slow and steady start but now gaining in momentum, maturity and mindshare © ASERT 2006-2008 codecamp 2008 - 3
  • 4. Why Groovy? • Minimal learning curve • Compiles to bytecode • Java object model & integration • Annotations • Optional static typing © ASERT 2006-2008 • Both run-time and compile-time metaprogramming codecamp 2008 - 4
  • 5. Fun Groovy trends © ASERT 2006-2008 codecamp 2008 - 5
  • 6. 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-2008 • Creating XML – Special Groovy support: MarkupBuilder and StreamingMarkupBuilder – Or again, enhanced syntax for your current favorites codecamp 2008 - 6
  • 7. ... 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-2008 • Verifying XML – Also DTD, W3C XML Schema, Relax NG in a similar fashion to Java mechanisms for these features codecamp 2008 - 7
  • 8. ... 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-2008 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 codecamp 2008 - 8
  • 9. An Xml Example ... import groovy.xml.dom.DOMCategory class Flights { static final String XML = ''' <trip> <flight hours=quot;13quot;> <from>Brisbane</from> © ASERT 2006-2008 <to>Los Angeles</to> </flight> <flight hours=quot;4quot;> <from>Los Angeles</from> <to>New Orleans</to> </flight> </trip> ''' ... codecamp 2008 - 9
  • 10. ... An Xml Example ... static final Reader getReader() { new StringReader(XML) } static final Set getCities(flights) { Set cities = [] © ASERT 2006-2008 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. codecamp 2008 - 10
  • 11. 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-2008 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 codecamp 2008 - 11
  • 12. 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-2008 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 codecamp 2008 - 12
  • 13. 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-2008 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 codecamp 2008 - 13
  • 14. ...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-2008 def longFlights = trip.flight.findAll(moreThanFiveHours) def longLaxFlights = longFlights.findAll(arrivingLax) def longOzLaxFlights = longLaxFlights.findAll(departingOz) assert longOzLaxFlights.@hours == '13' codecamp 2008 - 14
  • 15. ...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-2008 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 havecodecamp 2008 - 15 to care
  • 16. A Namespace Example class Books { static final String XML = ''' <rdf:rdf xmlns:bibterm=quot;http://www.book-stuff.com/terms/quot; xmlns:dc=quot;http://purl.org/dc/elements/1.0/quot; xmlns:rdf=quot;http://www.w3.org/1999/02/22-rdf-syntax-ns#quot;> <rdf:description rdf:about=quot;http://www.book-stuff.com/bibquot;> <bibterm:book rdf:parseType=quot;Resourcequot;> <bibterm:year>2007</bibterm:year> <dc:title>Groovy in Action</dc:title> © ASERT 2006-2008 <bibterm:author rdf:parseType=quot;Resourcequot;> <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> ''' ... codecamp 2008 - 16
  • 17. 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-2008 • 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' codecamp 2008 - 17
  • 18. 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-2008 assert b.'dc:title' == 'Groovy in Action' assert b.'bibterm:year' == '2007' codecamp 2008 - 18
  • 19. 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-2008 ['trip', 'flight', 'flight', 'from', 'to', 'from', 'to'] assert trip.'**'.from*.text() == ['Brisbane', 'Los Angeles'] codecamp 2008 - 19
  • 20. What about non-XML? def neko = new org.cyberneko.html.parsers.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') } data.each { println it } © ASERT 2006-2008 def neko = new org.cyberneko.html.parsers.SAXParser() 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.codehaus.org/apidocs/index.html /faq.html /groovy-jdk.html ... codecamp 2008 - 20
  • 21. 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-2008 assert dest.firstChild.nodeValue == 'Los Angeles' assert flights.item(1).getAttribute('hours') == '4' assert Flights.getCities(flights) == ['Brisbane', 'Los Angeles', 'New Orleans'] as Set codecamp 2008 - 21
  • 22. 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-2008 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' codecamp 2008 - 22
  • 23. DOMCategory import groovy.xml.DOMBuilder import groovy.xml.dom.DOMCategory def doc = DOMBuilder.parse(Flights.reader) def trip = doc.documentElement © ASERT 2006-2008 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 } codecamp 2008 - 23
  • 24. DOM4J import org.dom4j.io.SAXReader def trip = new SAXReader().read(Flights.reader).rootElement assert trip.elements()[0].elementText('to') == 'Los Angeles' © ASERT 2006-2008 assert trip.elements()[1].attributeValue('hours') == '4' codecamp 2008 - 24
  • 25. JDOM import org.jdom.input.SAXBuilder def b = new SAXBuilder() def trip = b.build(Flights.reader).rootElement © ASERT 2006-2008 assert trip.children[0].getChildText('to') == 'Los Angeles' assert trip.children[1].getAttribute('hours').value == '4' codecamp 2008 - 25
  • 26. XOM import nu.xom.Builder def doc = new Builder().build(Flights.reader) def flights = doc.rootElement.childElements © ASERT 2006-2008 assert flights.get(0).getFirstChildElement('to').value == 'Los Angeles' assert flights.get(1).getAttribute('hours').value == '4' codecamp 2008 - 26
  • 27. 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-2008 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' codecamp 2008 - 27
  • 28. 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-2008 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' codecamp 2008 - 28
  • 29. 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-2008 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 codecamp 2008 - 29
  • 30. 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-2008 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 } codecamp 2008 - 30
  • 31. Xalan XPath import static org.apache.xpath.XPathAPI.* import groovy.xml.DOMBuilder def trip = DOMBuilder.parse(Flights.reader).documentElement assert eval(trip, 'flight/to/text()').str() == 'Los Angeles' assert eval(trip, 'flight[2]/@hours').str() == '4' © ASERT 2006-2008 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 codecamp 2008 - 31
  • 32. Jaxen XPath 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' assert new DOMXPath('flight[2]/@hours'). stringValueOf(trip) == '4' © ASERT 2006-2008 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 codecamp 2008 - 32
  • 33. 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-2008 def hourAttr = { it.item.node.getAttribute('hours') as int } def flights = quot;document { ${Flights.XML} }quot; def exp = new SaxonXQDataSource().connection.createExpression() def seq = exp.executeQuery(quot;$flights/trip/flight/to/text()quot;) assert seq.collect(asString) == ['Los Angeles', 'New Orleans'] seq = exp.executeQuery(quot;$flights/trip/flightquot;) assert seq.collect(hourAttr).sum() == 17 codecamp 2008 - 33
  • 34. XSLT... import static javax.xml.transform.TransformerFactory.newInstance as xsltFactory import javax.xml.transform.stream.* def xslt = ''' <xsl:stylesheet xmlns:xsl=quot;http://www.w3.org/1999/XSL/Transformquot; version=quot;1.0quot;> <xsl:template match=quot;/tripquot;> <html> <body> <h1>Flights</h1> <ul> <xsl:apply-templates select=quot;flightquot;/> </ul> © ASERT 2006-2008 </body> </html> </xsl:template> <xsl:template match=quot;flightquot;> <li> <xsl:value-of select=quot;fromquot;/> => <xsl:value-of select=quot;toquot;/> </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)) codecamp 2008 - 34
  • 35. ...XSLT import static javax.xml.transform.TransformerFactory.newInstance as xsltFactory import javax.xml.transform.stream.* def xslt = ''' <html> <xsl:stylesheet xmlns:xsl=quot;http://www.w3.org/1999/XSL/Transformquot; version=quot;1.0quot;> <xsl:template match=quot;/tripquot;> <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=quot;flightquot;/> </ul> </body> © ASERT 2006-2008 </body> </html> </html> </xsl:template> <xsl:template match=quot;flightquot;> <li> <xsl:value-of select=quot;fromquot;/> => <xsl:value-of select=quot;toquot;/> </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)) codecamp 2008 - 35
  • 36. MarkupBuilder import groovy.xml.MarkupBuilder def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.flights { flight(hours:13) { © ASERT 2006-2008 from('Brisbane') to('Los Angeles') } flight(hours:4) { from('Los Angeles') to('New Orleans') } } println writer codecamp 2008 - 36
  • 37. StreamingMarkupBuilder import groovy.xml.StreamingMarkupBuilder def writer = new StreamingMarkupBuilder().bind { flights { flight(hours: 13) { from('Brisbane') © ASERT 2006-2008 to('Los Angeles') } flight(hours: 4) { from('Los Angeles') to('New Orleans') } } } println writer codecamp 2008 - 37
  • 38. DOMBuilder import groovy.xml.* def builder = DOMBuilder.newInstance() def root = builder.flights { flight(hours: 13) { from('Brisbane') © ASERT 2006-2008 to('Los Angeles') } flight(hours: 4) { from('Los Angeles') to('New Orleans') } } new XmlNodePrinter().print(root) codecamp 2008 - 38
  • 39. MarkupBuilder with Namespaces import groovy.xml.MarkupBuilder def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.'rdf:description'( 'xmlns:bibterm': quot;http://www.book-stuff.com/terms/quot;, 'xmlns:dc': quot;http://purl.org/dc/elements/1.0/quot;, 'xmlns:rdf': quot;http://www.w3.org/1999/02/22-rdf-syntax- © ASERT 2006-2008 ns#quot;) { '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> codecamp 2008 - 39
  • 40. StreamingMarkupBuilder with Namespaces import groovy.xml.* XmlUtil.serialize(new StreamingMarkupBuilder().bind { mkp.declareNamespace( bibterm: quot;http://www.book-stuff.com/terms/quot;, dc: quot;http://purl.org/dc/elements/1.0/quot;, rdf: quot;http://www.w3.org/1999/02/22-rdf-syntax-ns#quot;) 'rdf:description' { © ASERT 2006-2008 '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> codecamp 2008 - 40
  • 41. DOMBuilder with Namespaces import groovy.xml.DOMBuilder def b = DOMBuilder.newInstance() def root = b.'rdf:description'( 'xmlns:bibterm': quot;http://www.book-stuff.com/terms/quot;, 'xmlns:dc': quot;http://purl.org/dc/elements/1.0/quot;, 'xmlns:rdf': quot;http://www.w3.org/1999/02/22-rdf-syntax-ns#quot;) { 'bibterm:book' { 'dc:title'('Groovy in Action') © ASERT 2006-2008 '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> codecamp 2008 - 41
  • 42. 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-2008 'bibterm:year'('2007') } } new XmlNodePrinter().print(root) <?xml version=quot;1.0quot; encoding=quot;UTF-8quot;?> <rdf:description xmlns:rdf=quot;http://www.w3.org/1999/02/22-rdf-syntax-ns#quot;> <bibterm:book xmlns:bibterm=quot;http://www.book-stuff.com/terms/quot;> <dc:title xmlns:dc=quot;http://purl.org/dc/elements/1.0/quot;>Groovy in Action</dc:title> <bibterm:year>2007</bibterm:year> </bibterm:book> </rdf:description> codecamp 2008 - 42
  • 43. 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-2008 flight(hours: 13) { from('Brisbane') to('Los Angeles') } flight(hours: 4) { from('Los Angeles') to('New Orleans') } } println writer codecamp 2008 - 43
  • 44. StaxBuilder for non-XML import groovy.xml.StaxBuilder import org.codehaus.jettison.mapped.* @Grab(group='org.codehaus.jettison', module='jettison', version='1.0.1') def writer = new StringWriter() def con = new MappedNamespaceConvention() def mappedWriter = new MappedXMLStreamWriter(con, writer) def builder = new StaxBuilder(mappedWriter) © ASERT 2006-2008 builder.flights { flight(hours: 13) { from('Brisbane') {quot;flightsquot;:{quot;flightquot;:[ to('Los Angeles') { quot;@hoursquot;:quot;13quot;, } quot;fromquot;:quot;Brisbanequot;, flight(hours: 4) { quot;toquot;:quot;Los Angelesquot; }, from('Los Angeles') { quot;@hoursquot;:quot;4quot;, to('New Orleans') quot;fromquot;:quot;Los Angelesquot;, } quot;toquot;:quot;New Orleansquot; } } ]}} println writer codecamp 2008 - 44
  • 45. Updating XML <shopping> <shopping> <category type=quot;groceriesquot;> <category type=quot;groceriesquot;> <item>Luxury Chocolate</item> <item>Chocolate</item> <item>Luxury Coffee</item> <item>Coffee</item> </category> </category> <category type=quot;suppliesquot;> <category type=quot;suppliesquot;> <item>Paper</item> <item>Paper</item> <item quantity=quot;6quot; © ASERT 2006-2008 <item quantity=quot;4quot;>Pens</item> when=quot;Urgentquot;>Pens</item> </category> </category> <category type=quot;presentquot;> <category type=quot;presentquot;> <item when=quot;Aug 10quot;> <item>Mum's Birthday</item> Kathryn's Birthday <item when=quot;Oct 15quot;> </item> Monica's Birthday </category> </item> </shopping> </category> </shopping> codecamp 2008 - 45
  • 46. Updating with XmlParser def root = new XmlParser().parseText(input) // modify groceries: quality items please def groceries = root.category.findAll{ it.@type == 'groceries' }.item groceries.each { g -> g.value = 'Luxury ' + g.text() } // modify supplies: we need extra pens © ASERT 2006-2008 def supplies = root.category.findAll{ it.@type == 'supplies' }.item supplies.findAll{ it.text() == 'Pens' }.each { s -> s.@quantity = s.@quantity.toInteger() + 2 s.@when = 'Urgent' } // modify presents: August has come and gone def presentCategory = root.category.find{ it.@type == 'present' } presentCategory.children().clear() presentCategory.appendNode('item', quot;Mum's Birthdayquot;) presentCategory.appendNode('item', [when:'Oct 15'], quot;Monica's Birthdayquot;) codecamp 2008 - 46
  • 47. Updating with XmlSlurper def root = new XmlSlurper().parseText(input) // modify groceries: quality 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 def pens = root.category.find{ it.@type == 'supplies' © ASERT 2006-2008 }.item.findAll{ it.text() == 'Pens' } pens.each { p -> p.@quantity = (p.@quantity.toInteger() + 2).toString() p.@when = 'Urgent' } // modify presents: August has come and gone def presents = root.category.find{ it.@type == 'present' } presents.replaceNode{ node -> category(type:'present'){ item(quot;Mum's Birthdayquot;) item(quot;Monica's Birthdayquot;, when:'Oct 15') } } codecamp 2008 - 47
  • 48. Updating with DOMCategory use(DOMCategory) { def categories = DOMBuilder.parse(input).documentElement.category // modify groceries: quality items please def groceries = categories.findAll{ it.'@type' == 'groceries' }[0].item groceries.each { g -> g.value = 'Luxury ' + g.text() } // modify supplies: we need extra pens def supplies = categories.findAll{ it.'@type' == 'supplies' }[0].item © ASERT 2006-2008 supplies.findAll{ it.text() == 'Pens' }.each { s -> s['@quantity'] = s.'@quantity'.toInteger() + 2 s['@when'] = 'Urgent' } // modify presents: August has come and gone def presents = categories.find{ it.'@type' == 'present' } presents.item.each { presents.removeChild(it) } presents.appendNode('item', quot;Mum's Birthdayquot;) presents.appendNode('item', [when:'Oct 15'], quot;Monica's Birthdayquot;) } codecamp 2008 - 48
  • 49. 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-2008 • RESTful Options – Restlet.org, RESTlet DSL, roll your own – JAX-RS: Jersey, CXF, JBoss RESTeasy • Frameworks layered upon SOA – Synapse, Tuscany, ServiceMix codecamp 2008 - 49
  • 50. 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-2008 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 codecamp 2008 - 50
  • 51. 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-2008 def server = new WSServer() server.setNode MathService.name, quot;http://localhost:6980/MathServicequot; import groovyx.net.ws.WSClient def url = quot;http://localhost:6980/MathService?wsdlquot; 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 codecamp 2008 - 51
  • 52. JAXB Server import javax.xml.ws.Endpoint import javax.jws.WebService import javax.jws.soap.SOAPBinding import javax.jws.WebMethod @WebService(name=quot;Echoquot;, serviceName=quot;EchoServicequot;, targetNamespace=quot;http://jaxws.asert.comquot;) @SOAPBinding(style=SOAPBinding.Style.RPC) © ASERT 2006-2008 class EchoImpl { @WebMethod(operationName = quot;echoquot;) String echo(String message) { println quot;Received: $messagequot; quot;nYou said: quot; + message } } Endpoint.publish(quot;http://localhost:8080/Echoquot;, new EchoImpl()) println 'EchoService published and running ...' codecamp 2008 - 52
  • 53. JAXB Client • JAXB Client import javax.xml.namespace.QName import com.asert.jaxws.EchoService def url = new URL(quot;http://localhost:8080/Echo?wsdlquot;) def qname = new QName(quot;http://jaxws.asert.comquot;, quot;EchoServicequot;) def echoServer = new EchoService(url, qname).echoPort © ASERT 2006-2008 println echoServer.echo(quot;Today is ${new Date()}quot;) • Build instructions wsimport -d ../build -p com.asert.jaxws http://localhost:8080/Echo?wsdl codecamp 2008 - 53
  • 54. 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-2008 some special things you can do with Groovy codecamp 2008 - 54
  • 55. 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-2008 – http://www.developer.com/services/article.ph p/10928_3570031_2 codecamp 2008 - 55
  • 56. 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-2008 http://wikis.sun.com/display/Jersey/Main – restlet.org http://www.restlet.org codecamp 2008 - 56
  • 57. 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-2008 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(quot;Hello, world!quot;, MediaType.TEXT_PLAIN) } // ... codecamp 2008 - 57
  • 58. GroovyRestlet DSL builder.component { current.servers.add(protocol.HTTP, 8182) application(uri: quot;quot;) { router { def guard = guard(uri: quot;/docsquot;, scheme: challengeScheme.HTTP_BASIC, realm: quot;Restlet Tutorialsquot;) guard.secrets.put(quot;scottquot;, quot;tigerquot;.toCharArray()) guard.next = directory(root: quot;quot;, autoAttach: false) restlet(uri: quot;/users/{user}quot;, handle: {req, resp -> resp.setEntity(quot;Account of user quot;${req.attributes.get('user')}quot;quot;, mediaType.TEXT_PLAIN) © ASERT 2006-2008 }) restlet(uri: quot;/users/{user}/ordersquot;, handle: {req, resp -> resp.setEntity(quot;Orders or user quot;${req.attributes.get('user')}quot;quot;, mediaType.TEXT_PLAIN) }) restlet(uri: quot;/users/{user}/orders/{order}quot;, handle: {req, resp -> def attrs = req.attributes def message = quot;Order quot;${attrs.get('order')}quot; for User quot;${attrs.get('user')}quot;quot; resp.setEntity(message, mediaType.TEXT_PLAIN) }) } } }.start() Source: http://docs.codehaus.org/display/GROOVY/GroovyRestlet codecamp 2008 - 58
  • 59. 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 (quot;/helloworldquot;) class HelloWorldResource { @GET @Produces(quot;text/plainquot;) © ASERT 2006-2008 String getPlainMessage() { quot;Hello Worldquot; } } def baseUri = quot;http://localhost:9998/quot; def initParams = [quot;com.sun.jersey.config.property.packagesquot;: quot;com.asertquot;] println quot;quot;quot; Starting grizzly with Jersey... App WADL available at ${baseUri}application.wadl App available at ${baseUri}helloworld quot;quot;quot; create(baseUri, initParams) codecamp 2008 - 59
  • 60. ...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-2008 @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()) } } codecamp 2008 - 60
  • 61. ...Jersey @Path(quot;/flight/xml/{flightnum}quot;) class FlightInfoXml { @GET @Produces(quot;text/xmlquot;) String getXmlMessage(@PathParam('flightnum') String num) { def trip = new XmlParser().parse(Flights.reader) PrettyXml.print(trip.flight[num as int]) } } @Path(quot;/flight/json/{flightnum}quot;) © ASERT 2006-2008 class FlightInfoJson { @GET @Produces(quot;application/jsonquot;) FlightInfoBean getJsonMessage(@PathParam('flightnum') String num) { FlightInfoBean.populate(num) } } @Path(quot;/flight/atom/{flightnum}quot;) class FlightInfoAtom { @GET @Produces(quot;application/atomquot;) FlightInfoBean getAtomMessage(@PathParam('flightnum') String num) { FlightInfoBean.populate(num) } } codecamp 2008 - 61
  • 62. Jersey output > curl http://localhost:9998/helloworld Hello World > curl http://localhost:9998/flight/xml/1 <flight hours=quot;4quot;> <from> Los Angeles </from> <to> New Orleans © ASERT 2006-2008 </to> </flight> > curl http://localhost:9998/flight/json/1 {quot;@hoursquot;:quot;4quot;,quot;fromquot;:quot;Los Angelesquot;,quot;toquot;:quot;New Orleansquot;} > curl http://localhost:9998/flight/atom/1 <?xml version=quot;1.0quot; encoding=quot;UTF-8quot; standalone=quot;yesquot;?> <flightInfoBean hours=quot;4quot;> <from>Los Angeles</from> <to>New Orleans</to> </flightInfoBean> codecamp 2008 - 62
  • 63. 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-2008 trunk/java/src/site/resources/presentations/ makingsoagroovyfremantle.pdf codecamp 2008 - 63
  • 64. 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-2008 – You can use legacy ScriptComponent and GroovyComponent – You can write Groovy JBI components codecamp 2008 - 64
  • 65. 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-2008 – You can define your services using Groovy either using Java mechanisms or Scripting integration codecamp 2008 - 65
  • 66. ...Tuscany... <composite ...> <component name=quot;CalculatorServiceComponentquot; .../> <component name=quot;AddServiceComponentquot; .../> <component name=quot;SubtractServiceComponentquot;> <tuscany:implementation.java class=quot;calculator.SubtractServiceImplquot;/> </component> <component name=quot;MultiplyServiceComponentquot;> © ASERT 2006-2008 <tuscany:implementation.script language=quot;groovyquot;> 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=quot;DivideServiceComponentquot;> <tuscany:implementation.script script=quot;calculator/DivideServiceImpl.groovyquot;/> </component> </composite> codecamp 2008 - 66
  • 67. ...Tuscany <composite ...> With Groovy scripts either <component name=quot;CalculatorServiceComponentquot; .../> <component name=quot;AddServiceComponentquot; .../> files – no embedded or in <component name=quot;SubtractServiceComponentquot;> compilation necessary. <tuscany:implementation.java class=quot;calculator.SubtractServiceImplquot;/> </component> <component name=quot;MultiplyServiceComponentquot;> © ASERT 2006-2008 <tuscany:implementation.script language=quot;groovyquot;> def multiply(n1, n2) { n1 * n2 } </tuscany:implementation.script> </component> <component name=quot;DivideServiceComponentquot;> <tuscany:implementation.script script=quot;calculator/DivideServiceImpl.groovyquot;/> </component> </composite> codecamp 2008 - 67
  • 68. 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-2008 • 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 ... codecamp 2008 - 68
  • 69. More Information: Groovy in Action © ASERT 2006-2008 codecamp 2008 - 69