Soyons
RESTful
avec
RESTX !
by @xavierhanin
http://slid.es/xavierhanin/restx
/me
Xavier Hanin
Lead Architect / Developer
Bordeaux Agency Manager
@ 4SH
Community
Creator of BordeauxJUG (2008)
Frequent speaker (Devoxx BE, Devoxx FR, ApacheCon, JavaZone, BordeauxJUG, ...)
Teacher @ Institut Polytechnique de Bordeaux (since 2004)
Winner of CodeStory S02
Open Source
Creator of Apache Ivy (2004)
Creator of Voxxr.in (2012)
Many side projects / contributions
wicket-contrib-push, xooki, xooctory, twitter4j, ...
Creator of RESTX (2013)
Web Development
Servlet, JSP, Velocity, Freemarker, JSTL, JSF, RichFaces, ADF Faces, Wicket, GWT, Struts, Spring MVC, Restlet, Play! Framework, SimpleFramework, PHP, HTML5, CSS3, JS, NodeJS, Dojo, ExtJS, jQuery, AngularJS
Motivation

Agenda
- REST
- RESTX
REST
Elements
Resources
Resources Identifiers
Representations
Constraints
Client Server
Stateless
Cacheable
Uniform Interface
Layered System
Code On Demand (optional)
Resource
Any information that can be named can be a resource - Roy Fielding
A person
The list of persons living in Bordeaux
Today's weather in La Rochelle
Resource Identifier
URI = Uniform Resource Identifier
^
|
URL = Uniform Resource Locator
eg
http://restx.io/modules
http://en.wikipedia.org/wiki/Main_Page
Representation
REST components perform actions on a resource by using a representation to capture the current or intended state of that resource and transferring that representation between components - Roy Fielding
Representation
JSON
{
"name":"Acme Inc",
"address": {
"body":"111, av du truc",
"zipCode":"33700",
"city":"Merignac"
},
"_id":"51eba5766bc8e48ffeaacc89"
}
XML
<company name="Acme Inc" _id="51eba5766bc8e48ffeaacc89">
<address>
<body>111, av du truc</body>
<zipcode>33700</zipcode>
<city>Merignac</city>
</address>
</company>
Uniform interface
HTTP Methods
GET
HEAD
OPTIONS
PUT
POST
DELETE
TRACE
CONNECT
GET
get a resource state representation
GET /companies/51ebab006bc8e48ffeaacc93
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:39 GMT Content-Type:application/json; charset=UTF-8 {
"name":"ACME Inc", "address":{ "body":"1122, pooder st",
"zipCode":"12345 CA",
"city":"Palo Alto"}, "_id":"51ebab006bc8e48ffeaacc93" }
HEAD
get a resource metadata
eg
HEAD /companies/51ebab006bc8e48ffeaacc93
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Content-Type:application/json; charset=UTF-8
OPTIONS
request for information about the communication options available
eg
Used for CORS (Cross Origin Resource Sharing)
OPTIONS /companies/51ebab006bc8e48ffeaacc93
Origin: http://foo.example
Access-Control-Request-Method: PUT
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: PUT, POST, GET, OPTIONS
PUT
put a resource state representation
PUT /companies/51ebab006bc8e48ffeaacc93
{ "name":"ACME Inc",
"address":{
"body":"1122, pooder st",
"zipCode":"12345 CA",
"city":"Palo Alto"},
"_id":"51ebab006bc8e48ffeaacc93"}
HTTP/1.1 200 OK
{ "name":"ACME Inc",
"address":{
"body":"1122, pooder st",
"zipCode":"12345 CA",
"city":"Palo Alto"},
"_id":"51ebab006bc8e48ffeaacc93"}
DELETE
deletes a resource
eg
DELETE /companies/51ebab006bc8e48ffeaacc93
HTTP/1.1 204 No Content
POST
posts a new resource state representation
"request that the origin server accept the entity enclosed in the request as a new subordinate of the resource"
POST /companies
{ "name":"ACME Inc",
"address":{
"body":"1122, pooder st",
"zipCode":"12345 CA",
"city":"Palo Alto"} }
HTTP/1.1 201 Created
{ "name":"ACME Inc",
"address":{
"body":"1122, pooder st",
"zipCode":"12345 CA",
"city":"Palo Alto"},
"_id":"51ebab006bc8e48ffeaacc93"}
Uniform Interface
200 OK
201 Created
202 Accepted
204 No Content
301 Moved Permanently
303 See Other
304 Not Modified
...
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
500 Internal Server Error
...
Stateless
such that each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client. - Roy Fielding
RESTX
RESTX
Java lightweight REST framework
Open Source
Apache License
RESTX History
pre history : 2011 framework jewas
4SH only
very low level
Netty based / included HTTP connector
no annotation
RESTX HISTORY
january 2013
starting a new project @ 4SH
AngularJS / REST / MongoDB
focus on server startup time
spring mvc + jetty too slow (2s)
RESTX History
May 2013
First open source version (0.2.5)
restx.io web site
0.2.6
June ~ July 2013 -> 0.2.7, 0.2.8
September 2013
-> 0.2.9 - first conferences
November 2013 -> 0.30
January 2014 -> 0.31
Getting Started
$ curl -s http://restx.io/install.sh | sh $ restx ========================================================== == WELCOME TO RESTX SHELL - 0.31 - type `help` for help ========================================================== restx> app new Welcome to RESTX APP bootstrap! This command will ask you a few questions to generate your app. For any question you can get help by answering '??' App name? MyApp [...] Congratulations! - Your app is now ready in `~/projects/myapp`
As a Library
Apache Maven:
<dependency> <groupId>io.restx</groupId> <artifactId>restx-core</artifactId> <version>0.31<version> </dependency>
Apache Ivy:
<dependency org="io.restx" name="restx-core" rev="0.31" />
+ a few additional steps:
First Resource
@Component @RestxResource
public class HelloResource {
@PermitAll @GET("/message")
public MyMessage sayHello(String who) {
return new MyMessage().setMessage(String.format(
"hello %s, it's %s",
who, DateTime.now().toString("HH:mm:ss")));
}
}
$ curl "http://localhost:8080/api/message?who=restx" {"message":"hello restx, it's 15:53:23"}
Fast Startup
No classpath scanning
Track startup time
Empty app starts in ~ 500ms
App with ~100 routes ~60 entity classes
+ mongoDB connection
starts in ~1.2s
Benefits
Better development cycles
Cloud ready: better warmup requests time
Better for integration tests
no reflection / proxies
Use code generation instead
Based on Annotation Processing
built in Javac
BENEFITS - Knows your code
Parameters names, Parameterized types, ...
@GET("/cities")
public Iterable<City> findCities(Optional<String> name) {
if (name.isPresent()) {
return cities.get().find("{name: #}",
name.get()).as(City.class);
} else {
return cities.get().find().as(City.class);
}
}
@GET("/cities/{oid}")
public Optional<City> findCityById(String oid) {
return Optional.fromNullable(cities.get().findOne(new ObjectId(oid)).as(City.class));
}
@PUT("/cities/{oid}")
public City updateCity(String oid, City city) {
checkEquals("oid", oid, "city.key", city.getKey());
cities.get().save(city);
return city;
}
Benefits - See the code
new StdEntityRoute("default#HelloResource#sayHello",
mapper, new StdRouteMatcher("GET", "/message")) {
@Override
protected Optional doRoute(
RestxRequest request,
RestxRouteMatch match) throws IOException {
securityManager.check(request, open());
return Optional.of(resource.sayHello(
/* [QUERY] who */ checkPresent(request.getQueryParam("who"),
"query param who is required")
));
}
}
Benefits - Impact Analysis

Benefits - Easy Debug
Benefits - More efficient
Pay the price at build time, not runtime
Less CPU cycles
=>
Faster
Cheaper, especially for the cloud "pay for what you use"
Lightweight

NO BRAINER FEATURES
Bundled with best libraries
Google Guava
Rely on Optional<> to mark parameters as optional
Joda Time
Until its standardised in JSR 310
No brainer features
JSON Mapping
Done with Jackson
"High-performance JSON processor"
Very flexible
High performance
Supports BSON
Configured to support JodaTime & Guava
NO BRAINER FEATURES
Validation
Done with Hibernate Validator
All incoming objects are validated
@Size(min = 4)
private String name;
NO BRAINER FEATURES
Dependency Injection
With (almost) no classpath scanning
Type safe (based on code generation)
Used to discover plugins
@Component
public class MyComponent {
public MyComponent(MyDependency dependency) {
...
}
}
NO BRAINER FEATURES
Security
Stateless security
Secured by default
@Component @RestxResource
public class HelloResource {
@PermitAll @GET("/message")
public MyMessage sayHello(String who) {
return new MyMessage().setMessage(String.format(
"hello %s, it's %s",
who, DateTime.now().toString("HH:mm:ss")));
}
}
NO BRAINER FEATURES
Security (2)
@Component @RestxResource
public class HelloResource {
@RolesAllowed("admin") @GET("/message")
public MyMessage sayHello(String who) {
return new MyMessage().setMessage(String.format(
"hello %s, it's %s",
who, DateTime.now().toString("HH:mm:ss")));
}
}
Developer Friendly
DEMO
Developer Friendly
Easy Setup
Fast startup
Auto compile
Play well with your IDE
Nice error reporting on JSON mapping errors
API Docs
Monitor
Factory UI
Easy debugging
Enterprise Ready
- Pure Java
-
Available in Maven Central
-
Easily build with Maven / Ant+Ivy
- Test with JUnit
- Develop in your preferred IDE
-
Deploy in any servlet 2.5 container with Java 7
-
Easily embeddable (it's just a servlet)
-
Enterprise friendly Open Source License (ASL)
Cloud Ready
Deploy on
Any Java PAAS
-
Cloudbees
-
Heroku
-
Google App Engine
- JElastic
- CloudFoundry
- ...
Cloud Ready
Deploy on
Any IAAS
- AWS EC2
- Google Compute Engine
- OVH public cloud
- your private cloud
- ...
Cloud demo
Cloudbees
$ restx app new [...] $ mvn package $ bees app:deploy target/*.war -a myaccount/myapp
RESTX w/ MongoDB
a module based on jongo
@Component @RestxResource
public class CompanyResource {
private final JongoCollection companies;
public CompanyResource(
@Named("companies") JongoCollection companies) {
this.companies = companies;
}
@GET("/companies")
public Iterable<Company> findCompanies() {
return companies.get().find().as(Company.class);
}
}
RESTX W/ MONGODB
- uses Jackson for Mapping
- symmetry between REST API and storage
- can use views mechanism to include/exclude fields
- tight integration with RESTX
- support for RESTX specs
- recording (capture states of collections used)
- replay (setup DB for tests, fetch collections with data fixtures)
- super easy to use
RESTX Specs
Specify your API behavior in format close to HTTP:
title: should say hello given: - time: 2013-03-31T14:33:18.272+02:00 wts: - when: GET message?who=xavier then: |
200 OK
{"message":"hello xavier, it's 14:33:18"}
RESTX Specs
This can be used for:
- integration tests
- examples in API documentation
- REST API mocking
RESTX Specs
can easily be recorded

capture:
- system state (time, DB state, ...)
-
request
- response
Thank you !
Questions?
Links
Copy of restx
By xavierhanin
Copy of restx
Soyons RESTful avec RESTX !
- 1,752