XML and Web Services with Groovy

4,591 views
4,474 views

Published on

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

Published in: Technology, Education
0 Comments
12 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,591
On SlideShare
0
From Embeds
0
Number of Embeds
30
Actions
Shares
0
Downloads
289
Comments
0
Likes
12
Embeds 0
No embeds

No notes for slide

XML and Web Services with Groovy

  1. 1. XML and Web Services © ASERT 2006-2008 with Groovy Dr Paul King paulk@asert.com.au ASERT, Australia codecamp 2008 - 1
  2. 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. 3. Growing Acceptance • A slow and steady start but now gaining in momentum, maturity and mindshare © ASERT 2006-2008 codecamp 2008 - 3
  4. 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. 5. Fun Groovy trends © ASERT 2006-2008 codecamp 2008 - 5
  6. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 69. More Information: Groovy in Action © ASERT 2006-2008 codecamp 2008 - 69

×