Contenu connexe Similaire à Oop2008 RESTful services with GWT and Apache CXF (20) Plus de Adrian Trenaman (11) Oop2008 RESTful services with GWT and Apache CXF1. Building and consuming RESTful
JSON services with Apache CXF
and Google Web Toolkit
Adrian Trenaman,
Distinguished Consultant,
IONA Technologies
services@iona.com 1
2. Introduction
Google Web Toolkit provides an excellent toolkit for RESTful AJAX
clients.
… but doesn’t provide a compelling solution for the server side.
Apache CXF provides a framework for building RESTful servers.
GWT and CXF together offer a compelling solution.
Agenda:
GWT overview
A RESTful “Hello, World” (in time honoured tradition)
A document-oriented RESTful service
This is a technical session;
This is a technical session;
Convention-based services expect to learn enough about
expect to learn enough about
Using JSON payload GWT and CXF to go out and
GWT and CXF to go out and
build your own solutions.
build your own solutions.
GWT client techniques
© 2008 IONA Technologies 2
4. GWT – overview
GWT allows you to build dynamic web applications in Java…
• Your Java is compiled into cross-browser Javascript.
Key components: JRE emulation library, web UI library, java-to-js
compiler, and a hosted web browser for debugging.
© 2008 IONA Technologies 4
5. GWT – overview (cont’)
GWT is available as open-source under Apache Public License
(APL) 2.0.
• Website: http://code.google.com/webtoolkit
Benefits of GWT:
• Small application footprint (~100Kb)
• Excellent GUI performance
• Faster time-to-development: design, develop, debug, refactor in Java using your
favourite IDE, then deploy as JS.
© 2008 IONA Technologies 5
6. GWT and RPC
GWT provides an RPC approach based on serialized Java over
HTTP.
Host a GWT service in a servlet engine – the GWT servlet will handle
serialization of parameters, return values and exceptions.
Easy to use, but limits the reusability of services.
<<browser>> <<servlet-engine>>
: :Tomcat
<<gwt servlet>>
<<gwt servlet>>
<<gwt>>
<<gwt>> :ServiceImpl
:ServiceImpl
:Client
:Client
Serialised Java
Objects over HTTP
© 2008 IONA Technologies 6
7. GWT and AJAX
GWT supports AJAX-style services
• Use asynchronous HTTP requests to transmit text-based payload such as XML
or JSON.
• Apache CXF (Fuse) can provide the server-side solution.
Rapidly produce JSON and XML services for GWT clients.
<<browser>> <<servlet-engine>>
: :Tomcat
<<cxf servant>>
<<cxf servant>>
<<gwt>>
<<gwt>> :ServiceImpl
:ServiceImpl
:Client
:Client CXF
JSON or XML over HTTP
© 2008 IONA Technologies 7
9. RESTful services with FUSE
ContactsService.java
ContactsService.java
Approach: @WebService
@WebService
Annotate your Java service. interface ContactsService {
interface ContactsService {
Deploy - in Spring Framework, @Get
@Get
Tomcat, J2EE, or standalone. @HttpResource(location=“/contacts/id={id}”)
@HttpResource(location=“/contacts/id={id}”)
Consume – most likely from public Contact getContact(int id);
public Contact getContact(int id);
AJAX clients. }
}
http://frodo:9000/contacts/id=123 Go >
<ns4:Contact> <<server>>
frodo:
<firstName>Ade</firstName>
<lastName>Trenaman</lastName>
<<fuse-service>>
<<fuse-service>>
<company>IONA Technologies</company> :ContactsService
:ContactsService
<title>Principal Consultant</title> GET 9000
HTTP
<email>adrian.trenaman@iona.com</email>
<mobile>+353-86-6051026</mobile>
© 2008 IONA Technologies 9
10. RESTful services with FUSE (cont’)
No formal interface definition language (WSDL, IDL) is used.
However, XSD is often used for data definition.
A service’s “parameters” are passed via payload and URL, e.g.:
http://localhost:9000/contacts/id=007
FUSE supports XML and JSON payloads
Services make use of a natural mapping from HTTP verbs to CRUD
operations.
POST: Create a new item of data on the service.
GET: Retrieve data from the service.
PUT: Update data on the service.
DELETE: Delete data from services.
© 2008 IONA Technologies 10
12. Hello World
A simple “Hello, World” application:
A client performs a HTTP GET using a URL like:
http://localhost:9000/hw/sayHello/user=ade&msg=hello
The server receives the parameters and returns a string value.
Steps:
Write a simple Java bean to match the URI template: in this case, to hold the
user and msg parameters.
Write your business logic in Java
Use a browser, or tools such as curl or wget, to perform a HTTP GET.
© 2008 IONA Technologies 12
13. Parameters bean…
Input parameters for the sayHello operation can be modelled
easily as a Java bean.
Parameters map to bean fields with appropriate getters and setters.
URI: http://localhost:9000/hw/sayHello/user=ade&msg=hello
Java bean:
public class SayHello {
private String user;
private String msg;
public String getMsg() { … }
public void setMsg(String message) {…}
public String getUser() {…}
public void setUser(String user) {…}
}
© 2008 IONA Technologies 13
14. Business Logic
Annotate the class
Annotate the class
as aaweb service.
as web service.
@WebService
This method will
This method will
public class HelloWorld { respond to aaHTTP
respond to HTTP
GET…
GET…
… at this
… at this
@Get location!
location!
@HttpResource(
location="/sayHello/user={user}&msg={msg}“
)
public String sayHello(SayHello params) {
System.out.println(params.getUser() + " said '" +
params.getMsg() + "'");
return "Thanks for your message!";
} Everything
Everything
else is just else is just
} plain old Java.
plain old Java.
© 2008 IONA Technologies 14
15. Deploying your service
You can deploy your service into a Spring container, Tomcat, a
J2EE application server, or run as a Java mainline.
Boiler plate code:
String URL = "http://localhost:9000/hw/";
JaxWsServerFactoryBean svc = new JaxWsServerFactoryBean();
svc.setBindingId(HttpBindingFactory.HTTP_BINDING_ID);
svc.setAddress(URL);
svc.setServiceBean(new HelloWorld());
svc.getServiceFactory().setWrapped(false);
svc.create();
System.out.println(“Listening on " + URL);
© 2008 IONA Technologies 15
16. Testing your service
You can test your REST services by simply pointing a browser at
the URL.
This will implicitly perform a GET on your service.
Alternatively, you can use command-line tools like wget or curl
curl –X GET
“http://localhost:9000/hw/sayHello/user=ade&msg=hello”
The output should be:
<ns2:sayHelloResponse xmlns:ns2="http://wrapped.helloworld/">
Thanks for your message!
</ns2:sayHelloResponse> Namespace derived
Namespace derived
from Java package.
from Java package.
© 2008 IONA Technologies 16
17. “Hello, world”: lessons learnt
The “Hello, World” example shows how expose a Java method as a
RESTful service
… with the use of just a few annotations.
By default the response payload is returned as XML; later we’ll see how to
easily change this to JSON.
This approach works well for algorithmic services.
Like “calculate my tax”.
The next section focuses on more resource-based service.
Like “retrieve customer details for customer 007654321”, or “create new
customer”.
© 2008 IONA Technologies 17
19. Resource-based service
Consider a resource such as “Customer contact details”.
Perhaps you have this modelled as a Java class.
Alternatively, you might have this modelled as an XML Schema.
If so, then you can easily generate an equivalent Java class using the JAX-B support
provided by FUSE.
You want a RESTful service providing CRUD operations for your
document:
Create, retrieve, update, delete customer contacts.
FUSE supports this via the use of URI templates (as before), along
with @Post, @Get, @Post, @Delete annotations.
FUSE will marshal the payload (if any) into your parameters.
© 2008 IONA Technologies 19
20. RESTful HTTP verbs
You can use the full palette of HTTP verbs:
Retrieve Create
POST http://.../contacts/id=123
GET http://.../contacts
Delete
GET http://.../contacts/id=123
DELETE http://.../contacts/id=123
GET http://.../contacts/name=ade Update
PUT http://.../contacts/id=123
© 2008 IONA Technologies 20
21. RESTful HTTP verbs
However: client technologies may only support GET and POST.
Tip: design your service for the lowest common denominator.
Retrieve
Create
POST GET http://.../contacts/create
GET http://.../contacts
Delete
GET http://.../contacts/id=123 DELETE GET
http://.../contacts/del/id=123
Update
GET http://.../contacts/name=ade
PUT POST http://.../contacts/id=123
© 2008 IONA Technologies 21
22. Contact service: interface
Defining an interface for the ContactService:
@WebService
public interface ContactService
{
@Get
@HttpResource(location="/contacts/create")
public Contact createContact() throws CannotCreate;
@Get
@HttpResource(location="/contacts/delete/id={id}")
public void removeContact(RemoveContact params);
© 2008 IONA Technologies 22
23. Contacts service: interface (cont’)
@Get
@HttpResource(location="/contacts")
public Contacts getContacts();
@Get
@HttpResource(location="/contacts/id={id}")
public Contact getContact(GetContact gc) throws NotFound;
@Get
@HttpResource( location=
"/contacts/firstName={firstName}&lastName={lastName}“
)
public Contacts findContacts(FindContactsByName params);
© 2008 IONA Technologies 23
24. Contacts service: interface (cont’)
@Post
@HttpResource(location="/contacts/{id}")
public void updateContact(Contact params);
© 2008 IONA Technologies 24
26. RESTful services by convention
FUSE can introspect a Java class and deploy as a RESTful service.
You don’t have to provide annotations.
FUSE adopts a number of conventions to give an intuitive deployment.
The introspection process must determine:
What HTTP verb to use – POST, PUT, GET or DELETE?
What URL context should the method relate to?
Examples:
removePerson(long id) DELETE /people/{id}
updatePerson(long id) PUT /people/{id}
© 2008 IONA Technologies 26
27. Conventions
Conventions are intuitive; best learnt by example.
Method: Collection<Contact> getContacts()
Maps to: GET /contacts.
Method: Contact getContact(long id)
Maps to: GET /contacts/{id}.
Note the use of a pluralizer in constructing the URI template.
Method: void updateContact(long id, Contact c)
Maps to: PUT /contacts/{id}.
The contact information is transferred as XML payload.
© 2008 IONA Technologies 27
28. Conventions (cont’)
Method: void removeContact(long id)
Maps to: DELETE /contacts/{id}
Methods that begin with remove or delete are mapped to HTTP DELETE.
Method: void createContact(Contact c)
Maps to: POST /contacts
Methods that begin with create or add are mapped to HTTP POST.
The contact information is transferred as XML payload.
© 2008 IONA Technologies 28
30. JSON - JavaScript Object Notation
The RESTful services shown previously return data formatted as
XML.
FUSE can generate XML from JAX-B annotated classes, and classes
without annotations (applying suitable conventions).
Alternative payload formats, such as JSON, may be preferred for
RESTful services.
JSON (JavaScript Object Notation) is a simple name-value format that
facilitates rapid marshalling and unmarshalling:
See http://www.json.org.
May be preferred by web developers writing Javascript clients.
FUSE supports JSON by replacing the XML marshaller with a JSON
marshaller.
© 2008 IONA Technologies 30
31. Badgerfish vs. mapped notation
Recall: XML elements are typically qualified with their namespace.
<getContactResponse xmlns="http://easyrest/">
<ns2:Contact xmlns:ns2="http://www.iona.com/demo/contact"
id="123">
<firstName>Joe</firstName>
<lastName>Blogs</lastName>
<company>IONA Technologies </company>
<title>Consultant </title>
<email> joe.blogs@iona.com </email>
<mobile>+353-1234567</mobile>
<phone>+353-1234567</phone>
</ns2:Contact>
</getContactResponse>
© 2008 IONA Technologies 31
32. Badgerfish vs. mapped notation (cont’)
When marshalling as JSON, the XML namespaces can be inserted
in a mangled form, using “Badgerfish” notation.
{"getContactResponse":{
"@xmlns":{"$":"http://easyrest/"},
"ns2:Contact":{
"@xmlns":{"ns2":"http://www.iona.com/demo/contact"},
"@id":"123",
"firstName":{"$":"Joe"}, "lastName":{"$":"Blogs"},
"company":{"$":"IONA Technologies"},
"title":{"$":" Consultant"},
"email":{"$":"joe.blogs@iona.com"},
"mobile":{"$":"+353-1234567"},
"phone":{"$":"+353-1234567"}
}
}}
© 2008 IONA Technologies 32
33. Badgerfish vs. mapped notation (cont’)
Some prefer to perform an explicit namespace mapping:
{"easyrest.getContactResponse":{
"contact.Contact":{
"@id":"123",
"firstName":"Joe",
"lastName":"Blogs",
"company":"IONA Technologies",
"title":"Consultant",
"email":"joe.blogs@iona.com",
"mobile":"+353-1234567",
"phone":"+353-1234567"
}
}}
© 2008 IONA Technologies 33
34. Configuring an endpoint for JSON
FUSE supports both Badgerfish and mapped approaches; to use
Badgerfish:
Map <String, Object> properties = …;
BadgerFishXMLInputFactory xif = new
BadgerFishXMLInputFactory();
properties.put(XMLInputFactory.class.getName(), xif);
BadgerFishXMLOutputFactory xof = new
BadgerFishXMLOutputFactory();
properties.put(XMLOutputFactory.class.getName(), xof);
endpoint.setProperties(properties);
© 2008 IONA Technologies 34
35. Configuring an endpoint for JSON (cont’)
For mapped JSON:
HashMap<String, String> nstojns = …;
nstojns.put("http://www.iona.com/demo/contact", "contact");
nstojns.put("http://easyrest/", "easyrest");
MappedXMLInputFactory xif = new
MappedXMLInputFactory(nstojns);
properties.put(XMLInputFactory.class.getName(), xif);
MappedXMLOutputFactory xof = new
MappedXMLOutputFactory(nstojns);
properties.put(XMLOutputFactory.class.getName(), xof);
endpoint.setProperties(properties);
© 2008 IONA Technologies 35
36. Aside: HTTP content-type
When returning JSON payload, the HTTP Content-Type header
should be set to something other than text/xml
After all, the payload isn’t XML, it’s JSON.
Use the properties map to set the content-type:
properties.put("Content-Type", "text/plain");
Could also set to application/json if preferred.
© 2008 IONA Technologies 36
38. GWT and AJAX – boilerplate Java code
RequestBuilder builder =
new RequestBuilder(RequestBuilder.GET, "http://...");
try {
Request req = builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
}
public void onResponseReceived(Request request,
Response response) {
}
});
}
catch (RequestException e) {
}
© 2008 IONA Technologies 38
39. GWT and AJAX – boilerplate Java code - notes
The RequestBuilder approach is favoured.
• An alternative approach, using the raw HTTPRequest class, is internally-
focussed and may be deprecated.
RequestBuilder only supports HTTP GET and POST.
•DELETE and PUT and other verbs not supported, due to a bug in Safari
implementation of the XMLHTTPRequest.
© 2008 IONA Technologies 39
40. GWT support for JSON
GWT provides support for creating and parsing JSON payload.
• See classes in package com.google.gwt.json.client.
public String toJSON() {
JSONObject obj = new JSONObject();
obj.put("firstName", new JSONString(“Ade”));
obj.put("lastName", new JSONString(“Trenaman”));
JSONObject contact = new JSONObject();
contact.put("er.Contact", obj);
return contact.toString();
}
{"er.Contact":{"firstName":"Ade","lastName":"Trenaman"}}
{"er.Contact":{"firstName":"Ade","lastName":"Trenaman"}}
© 2008 IONA Technologies 40
41. GWT support for JSON (cont’)
A similar API exists for parsing JSON.
JSONValue jsonValue = JSONParser.parse(responseText);
JSONObject obj = jsonValue.isObject();
JSONValue contact = obj.get("er.Contact");
JSONValue firstName = contact.isObject().get("firstName");
© 2008 IONA Technologies 41
42. Summary
© 2008 IONA Technologies 42
43. Summary
Use CXF (Fuse) to provide JSON services for AJAX clients.
Native support for RESTful JSON and XML payloads using explicit or
implied conventions.
CXF first implementation of JSR-311 – Java API for RESTful Web
Services.
Use GWT to build dynamic, flexible browser-based clients.
Full API for JSON and AJAX interactions.
Drawbacks:
GWT’s JSON API makes you do a lot of the hard work for marshalling and
unmarshalling.
• Perhaps leverage JSNI?
© 2008 IONA Technologies 43
44. Resources
Google Web Toolkit:
Go to http://code.google.com/webtoolkit
CXF, Fuse Service Framework and IONA:
Learn more: http://open.iona.com/
Download: http://open.iona.com/downloads/
Browse demos:
samples/restful_http_binding
samples/restful_dispatch
Contribute!
Need help?
Visit the forums on Apache and open.iona.com.
Contact us: training@iona.com
© 2008 IONA Technologies 44