Groovy Web Services
How can we make both RESTful and JAX-WS web services development easier using Groovy?
»
Groovy Web Services
Ken Kousen
Kousen IT, Inc.
ken.kousen@kousenit.com
http://www.kousenit.com
RESTful Web Services
URL based: "GETful"
JAX-WS Web Services
Holy code generation, Batman
blog: http://kousenit.wordpress.com
twitter: @kenkousen
Example: Google Chart API
http://chart.apis.google.com/chart?
plus lots of parameters
Addressable Resources
GET --> find
POST --> insert
PUT --> update
DELETE --> delete
params tell the story (rather than path)
base?k1=v1&k2=v2...
Amazon Product API
def queryStrng = params.collect { k,v -> "$k=$v" }.join('&')
def url = "${baseUrl}?${queryString}"
Build URL with Groovy
Parse XML Results
def response = new XmlSlurper().parse(url)
def item = response.Items.Item
book.title = item.ItemAttributes.Title
RESTful Tools
JAX-RS (JSR 311)
"Jersey"
Restlets
Grails
Annotations for REST
+ WADL
Part of Java EE 6
Jersey
Client stubs
Server
Skeleton
SIB/SEI
Top Down
Bottom Up
JAX-WS
WSDL
Grails
"/product/$id"(controller:"product", parseRequest:true){
action = [GET:"show", PUT:"update", DELETE:"delete", POST:"save"]
}
import grails.converters.*
class ProductController {
def show = {
if(params.id && Product.exists(params.id)) {
def p = Product.findByName(params.id)
render p as XML
} else {
def all = Product.list()
render all as XML
}
}
}
URL Mappings
Converters
Content Negotiation
import grails.converters.*
class BookController {
def books
def list = {
this.books = Book.list()
withFormat {
html bookList:books
js { render "alert('hello')" }
xml { render books as XML }
}
}
}
Making Java Groovy
O'Reilly, 2010 (?)
ISBN 0978739299
http://chart.apis.google.com/chart?&cht=bhs&chs=800x360&chd=t:10,15,25,40,65,100,-15,-30,-60,-90,5,10&chm=tInitial+publication,000000,0,0,12|tFavorable+mention+in+GroovyMag,000000,0,1,12|tJolt+award+nomination,000000,0,2,12|tWitty+repartee+on+Letterman,000000,0,3,12|tProduct+placement+in+Harry+Potter+movie,000000,0,4,12|tOprah%27s+Book+Club,000000,0,5,12|tDrunken+rant+on+Larry+King+Live,000000,0,6,12|tBug+found+in+book+source+code,000000,0,7,12|tInformercial+with+ShamWow+guy,000000,0,8,12|tScandalous+YouTube+video%28s%29,000000,0,9,12|tTearful+apology+on+The+View,000000,0,10,12|tO%27Reilly+%27Behind+the+Text%27+Special,000000,0,11,12&chds=-95,140&chtt=Anticipated+Impact+on+%22Making+Java+Groovy%22+Book+Sales&chco=0000ff|0000ff|0000ff|0000ff|0000ff|0000ff|ff0000|ff0000|ff0000|ff0000|0000ff|0000ff
def url = 'http://chart.apis.google.com/chart?'
def data = ['Initial publication':10,'Favorable mention in GroovyMag':15,
'Jolt award nomination':25,'Witty repartee on Letterman':40,
'Product placement in Harry Potter movie':65,"Oprah's Book Club":100,
'Drunken rant on Larry King Live':-15,'Bug found in book source code':-30,
'Informercial with ShamWow guy':-60,'Scandalous YouTube video(s)':-90,
'Tearful apology on The View':5,"O'Reilly 'Behind the Text' Special":10
]
// For bar chart, format is;
// label,label_color,data_set,point_index,label_size
// Would use a collect here instead, but need an index
def transformed = []
data.keySet().eachWithIndex { val, i ->
transformed << "t${URLEncoder.encode(val,'UTF-8')},000000,0,$i,12"
}
// Blue for positive, red for negative
def colors = data.values().collect() {
(it < 0 ? 'ff0000' : '0000ff')
}.join('|')
def title = 'Anticipated Impact on "Making Java Groovy" Book Sales'
def map = [
'cht' : 'bhs',
'chs' : '800x360',
'chd' : 't:'+ data.values().join(','),
'chm' : transformed.join('|'),
'chds': '-95,140',
'chtt': URLEncoder.encode(title,'UTF-8'),
'chco': colors
]
map.each { key, value ->
url += "&${key}=${value}"
}
println url
Groovy Client + JAX-WS Stubs
wsimport <options> <wsdl_loc>
Example: ISBN Service
http://webservices.daehosting.com/services/isbnservice.wso?WSDL
wsimport -keep -p mjg <wsdl_url>
ISBNServiceSOAPType service =
new ISBNService().getISBNServiceSOAP()
boolean ok = service.isValidISBN10("0978739299")
Invoke with Groovy (or Java)
wsimport
wsgen
WSDL --> Java
Java --> WSDL
Built into Java SE 6
apt
JAX-WS Tools
apt
annotation processing tool
works with Java source only
No Groovy
wsimport
generates Java stubs from WSDL
wsgen
generates WSDL
(and artifacts)
from bytecodes
Example: Potter's Potions, Ltd.
@WebService
public interface Wizard {
@WebMethod
@WebResult(name="potion")
Potion brewPotion(@WebParam(name="ingredients")
Ingredient[] elements);
@WebMethod
@WebResult(name="when")
Date sendOwl(@WebParam(name="message")
String message);
}
Java SEI
Groovy + POJOs
POJO, not POGO
JAX-WS --> JAXB
--> POGO properties
--> getMetaClass()
--> problems
@WebService
interface Wizard {
@WebMethod
@WebResult(name="potion")
Potion brewPotion(@WebParam(name="ingredients")
Ingredient[] elements);
@WebMethod
@WebResult(name="when")
Date sendOwl(@WebParam(name="message")
String message);
}
Groovy SEI + POJOs
Looks almost identical
but Java Potions and Ingredients
Implementations
wsgen works from SIBs
(Service Implementation Beans)
Java SIB
@WebService(endpointInterface="Wizard")
public class Weasley implements Wizard {
public Potion brewPotion(Ingredient[] elements) {
Potion p = new Potion();
p.setName("veritiserum");
p.setEffect("makes you tell the truth");
return p;
}
public Date sendOwl(String message) {
return new Date();
}
}
Groovy SIB
@WebService(endpointInterface="Wizard")
class Granger implements Wizard {
Potion brewPotion(Ingredient[] elements) {
return new Potion(name:'polyjuice',
effect:'change appearance');
}
Date sendOwl(String message) {
return new Date()
}
}
Annoying wsgen Issue
Working from SIB puts SIB class name in WSDL for service and port
Enter Groovy @Delegate
@WebService(endpointInterface="Wizard")
class HogwartsWizard implements Wizard {
@Delegate Wizard wizard
}
Wizard wiz = new HogwartsWizard(
wizard:new Granger())
Endpoint.publish('http://.../potions', wiz)
println 'Ready to receive requests...'
Groovy @Delegate
Publish Service
Methods forward to delegate
Handlers
Intercept SOAP Messages
Process using SOAP with Attachments API for Java (SAAJ)
Ugly, awkward in Java
Simple, elegant in Groovy
so much for interface / implemenation split
parsley
rosemary
thyme
SAAJ
Uses for Handlers
Cross-cutting concerns
(Do-it-yourself AOP)
Logging, security, encryption, routing, etc.
or, app-specific concerns...
JAX-WS 2.0, including JAXB 2.0
JAX-WS 2.1+ available from Metro
Wizard w = new HogwartsWizardService().getHogwartsWizardPort()
def ingredients = []
ingredients << new Ingredient(name:'Sopophorous bean',amount:1)
Potion p = w.brewPotion(ingredients)
println "$p.name: $p.effect"
Potions Client
Still Want POGOs?
Use field access rather than properties
Special @XMLAccessor annotation
@XmlAccessorType(XmlAccessType.FIELD)
class Potion {
String name
String effect
}
POGO
Groovy SEI
Use @WebMethod
or all public methods exposed
including invokeMethod(), etc
Conclusions
XML always easier with Groovy
Freely intermix with Java
JAX-WS tools work with Groovy
Do-It-Yourself WS
Generate XML with MarkupBuilder
Parse or Slurp XML
Baseball Data
Online XML game data
http://gd2.mlb.com/components/game/mlb
boxscores for all games on year, month, day
Data in XML format
def getGame(away,home,year) {
def box = new XmlParser.parse(...)
def awayName = box.@away_fname
...
// add extracted data to a POGO
}
builder.games {
games.each { g ->
game(
outcome:"$g.away $g.aScore, ...",
lat:g.stadium.latitude,
lng:g.stadium.longitude
)
}
}
Parse XML Data
Generate XML Response
Related Groovy Projects
HTTPBuilder
GroovyWS
HTTPBuilder
Built on Apache HttpClient library
Automatic parsing based on content type
Uses Groovy builder syntax
GroovyWS
Publish and access web services
Uses Apache CXF
Note: both version 0.5
caveat emptor
docs are a bit "thin"
vs
http://.../product/42Popular presentations
30 Things About Me
Brooke Ahrens on
All About Brooke Ahrens- I used this presentation as an icebreaker to introduce myself to my classes this year.
Future-Proof Your Education
Maria Andersen on
How do you prepare for uncertain career paths where technical knowledge doubles every two years? You pay attention to the skills that surround the content: ...
More popular prezis in Explore>