oop2008 restful services with gwt and apache cxf

44
1 Building and consuming RESTful JSON services with Apache CXF and Google Web Toolkit Adrian Trenaman, Distinguished Consultant, IONA Technologies [email protected]

Upload: adrian-trenaman

Post on 29-Nov-2014

2.947 views

Category:

Technology


0 download

DESCRIPTION

A paper I presented at OOP 2008 in Munich.

TRANSCRIPT

Page 1: Oop2008 RESTful services with GWT and Apache CXF

1

Building and consuming RESTful JSON services with Apache CXF and Google Web Toolkit

Adrian Trenaman, Distinguished Consultant, IONA [email protected]

Page 2: Oop2008 RESTful services with GWT and Apache CXF

2© 2008 IONA Technologies

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

Convention-based services

Using JSON payload

GWT client techniques

This is a technical session;

expect to learn enough about

GWT and CXF to go out and build your own solutions.

This is a technical session;

expect to learn enough about

GWT and CXF to go out and build your own solutions.

Page 3: Oop2008 RESTful services with GWT and Apache CXF

3© 2008 IONA Technologies

Google Web Toolkit (GWT) – a brief overview

Page 4: Oop2008 RESTful services with GWT and Apache CXF

4© 2008 IONA Technologies

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-jscompiler, and a hosted web browser for debugging.

Page 5: Oop2008 RESTful services with GWT and Apache CXF

5© 2008 IONA Technologies

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.

Page 6: Oop2008 RESTful services with GWT and Apache CXF

6© 2008 IONA Technologies

<<browser>>:

<<servlet-engine>>:Tomcat

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.

<<gwt servlet>>:ServiceImpl

<<gwt servlet>>:ServiceImpl<<gwt>>

:Client

<<gwt>>:Client

Serialised Java

Objects over HTTP

Page 7: Oop2008 RESTful services with GWT and Apache CXF

7© 2008 IONA Technologies

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>>:ServiceImpl

<<cxf servant>>:ServiceImpl<<gwt>>

:Client

<<gwt>>:Client

JSON or XML over HTTPCXF

Page 8: Oop2008 RESTful services with GWT and Apache CXF

8© 2008 IONA Technologies

RESTful JSON Services with

Fuse Service Framework / Apache CXF

Page 9: Oop2008 RESTful services with GWT and Apache CXF

9© 2008 IONA Technologies

RESTful services with FUSE

Approach:

Annotate your Java service.

Deploy - in Spring Framework,

Tomcat, J2EE, or standalone.

Consume – most likely from AJAX clients.

ContactsService.javaContactsService.java

@WebService

interface ContactsService {

@Get

@HttpResource(location=“/contacts/id={id}”)

public Contact getContact(int id);

}

@WebService

interface ContactsService {

@Get

@HttpResource(location=“/contacts/id={id}”)

public Contact getContact(int id);

}

http://frodo:9000/contacts/id=123 Go >

<ns4:Contact>

<firstName>Ade</firstName>

<lastName>Trenaman</lastName>

<company>IONA Technologies</company>

<title>Principal Consultant</title>

<email>[email protected]</email>

<mobile>+353-86-6051026</mobile>

<<server>>frodo:

<<fuse-service>>:ContactsService

<<fuse-service>>:ContactsService

HTTP 9000GET

Page 10: Oop2008 RESTful services with GWT and Apache CXF

10© 2008 IONA Technologies

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.

Page 11: Oop2008 RESTful services with GWT and Apache CXF

11© 2008 IONA Technologies

A RESTful “Hello, World”

Page 12: Oop2008 RESTful services with GWT and Apache CXF

12© 2008 IONA Technologies

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.

Page 13: Oop2008 RESTful services with GWT and Apache CXF

13© 2008 IONA Technologies

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) {…}

}

Page 14: Oop2008 RESTful services with GWT and Apache CXF

14© 2008 IONA Technologies

Business Logic

@WebService

public class HelloWorld {

@Get

@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!";

}

}

Annotate the class as a web service.

Annotate the class as a web service.

This method will respond to a HTTP GET…

This method will respond to a HTTP GET…

… at this location!

… at this location!

Everything else is just plain old Java.

Everything else is just plain old Java.

Page 15: Oop2008 RESTful services with GWT and Apache CXF

15© 2008 IONA Technologies

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);

Page 16: Oop2008 RESTful services with GWT and Apache CXF

16© 2008 IONA Technologies

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 from Java package.

Namespace derived from Java package.

Page 17: Oop2008 RESTful services with GWT and Apache CXF

17© 2008 IONA Technologies

“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”.

Page 18: Oop2008 RESTful services with GWT and Apache CXF

18© 2008 IONA Technologies

A RESTful resource-based service.

Page 19: Oop2008 RESTful services with GWT and Apache CXF

19© 2008 IONA Technologies

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.

Page 20: Oop2008 RESTful services with GWT and Apache CXF

20© 2008 IONA Technologies

Update

Delete

CreateRetrieve

RESTful HTTP verbs

You can use the full palette of HTTP verbs:

GET http://.../contacts

GET http://.../contacts/id=123

PUT http://.../contacts/id=123

POST http://.../contacts/id=123

DELETE http://.../contacts/id=123

GET http://.../contacts/name=ade

Page 21: Oop2008 RESTful services with GWT and Apache CXF

21© 2008 IONA Technologies

RESTful HTTP verbs

However: client technologies may only support GET and POST.

Tip: design your service for the lowest common denominator.

Update

Delete

CreateRetrieve

GET http://.../contacts

GET http://.../contacts/id=123

PUT POST http://.../contacts/id=123

POST GET http://.../contacts/create

DELETE GEThttp://.../contacts/del/id=123

GET http://.../contacts/name=ade

Page 22: Oop2008 RESTful services with GWT and Apache CXF

22© 2008 IONA Technologies

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);

Page 23: Oop2008 RESTful services with GWT and Apache CXF

23© 2008 IONA Technologies

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);

Page 24: Oop2008 RESTful services with GWT and Apache CXF

24© 2008 IONA Technologies

Contacts service: interface (cont’)

@Post

@HttpResource(location="/contacts/{id}")

public void updateContact(Contact params);

Page 25: Oop2008 RESTful services with GWT and Apache CXF

25© 2008 IONA Technologies

Convention-based RESTful-services

Page 26: Oop2008 RESTful services with GWT and Apache CXF

26© 2008 IONA Technologies

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}

Page 27: Oop2008 RESTful services with GWT and Apache CXF

27© 2008 IONA Technologies

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.

Page 28: Oop2008 RESTful services with GWT and Apache CXF

28© 2008 IONA Technologies

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.

Page 29: Oop2008 RESTful services with GWT and Apache CXF

29© 2008 IONA Technologies

Support for JSON

Page 30: Oop2008 RESTful services with GWT and Apache CXF

30© 2008 IONA Technologies

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.

Page 31: Oop2008 RESTful services with GWT and Apache CXF

31© 2008 IONA Technologies

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> [email protected] </email>

<mobile>+353-1234567</mobile>

<phone>+353-1234567</phone>

</ns2:Contact>

</getContactResponse>

Page 32: Oop2008 RESTful services with GWT and Apache CXF

32© 2008 IONA Technologies

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":{"$":"[email protected]"},

"mobile":{"$":"+353-1234567"},

"phone":{"$":"+353-1234567"}

}

}}

Page 33: Oop2008 RESTful services with GWT and Apache CXF

33© 2008 IONA Technologies

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":"[email protected]",

"mobile":"+353-1234567",

"phone":"+353-1234567"

}

}}

Page 34: Oop2008 RESTful services with GWT and Apache CXF

34© 2008 IONA Technologies

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);

Page 35: Oop2008 RESTful services with GWT and Apache CXF

35© 2008 IONA Technologies

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);

Page 36: Oop2008 RESTful services with GWT and Apache CXF

36© 2008 IONA Technologies

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.

Page 37: Oop2008 RESTful services with GWT and Apache CXF

37© 2008 IONA Technologies

GWT client code

Page 38: Oop2008 RESTful services with GWT and Apache CXF

38© 2008 IONA Technologies

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) {

}

Page 39: Oop2008 RESTful services with GWT and Apache CXF

39© 2008 IONA Technologies

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.

Page 40: Oop2008 RESTful services with GWT and Apache CXF

40© 2008 IONA Technologies

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"}}

Page 41: Oop2008 RESTful services with GWT and Apache CXF

41© 2008 IONA Technologies

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");

Page 42: Oop2008 RESTful services with GWT and Apache CXF

42© 2008 IONA Technologies

Summary

Page 43: Oop2008 RESTful services with GWT and Apache CXF

43© 2008 IONA Technologies

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?

Page 44: Oop2008 RESTful services with GWT and Apache CXF

44© 2008 IONA Technologies

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: [email protected]