CRUD through HTTP is a good step forward to using resources and becoming RESTful, another step further into it is to make use of hypermedia based services and this gem allows you to do it really fast.
You can read the article on using the web for real which gives an introduction to hypermedia/aware resources.
1. Easy —> writing hypermedia aware resource based clients
2. Easy —> hypermedia aware resource based services
3. Small → it’s not a bloated solution with a huge list of APIs
4. HATEOAS —> clients you are unaware of will not bother if you change your URIs
5. HATEOAS —> services that you consume will not affect your software whenever they change part of their flow or URIs
Restfulie is the first API which tries to somehow implement Jim Webber and Ian Robinson opinion on how RESTFul systems use hypermedia
as the way to lead your client’s path through a business process.
Therefore Restfulie is unique both in its feature set when compared to both Spring and JAX-RS based implementations, and its implementation: looking for simple code and favoring conventions over manual configurations when creating hypermedia aware system.
According to Richardson Maturity Model , systems are only to be called RESTFul if they support this kind of state flow transition through hypermedia content contained within resources representations:
<order> <product>basic rails course</product> <product>RESTful training</product> <atom:link rel="refresh" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/> <atom:link rel="update" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/> <atom:link rel="pay" href="http://www.caelum.com.br/orders/1/pay" xmlns:atom="http://www.w3.org/2005/Atom"/> <atom:link rel="cancel" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/> </order>
If you are to implement a 3rd level (restful) service, Restfulie is the way to go.
In its Java version, Restfulie uses by default but allows overriding:
- VRaptor:http://www.vraptor.org as the server-side controller
- XStream:“http://xstream.codehaus.org” as its serialization library
- java.net api for http requests
- Spring IoC for dependency injection
XStream is the most famous java serialization tool around with support both to json and xml while VRaptor (as Rails) supplies a reverse URI lookup system upon its controller which provides a way to identify URI’s from well defined transitions.
Restfulie comes into different flavors, both java and ruby versions are available to use.
The client side code allows you to hide http-protocol specifics if required, while allowing you to re-configure it when needed.
Example on accessing a resource and its services through the restfulie API:
Order order = new Order(); // place the order order = service("http://www.caelum.com.br/order").post(order); // cancels it resource(order).getTransition("cancel").execute();
This is a simple example how to make your state changes available to your resource consumers:
public class Order implements StateResource { public List<Transition> getFollowingTransitions(Restfulie control) { if (status.equals("unpaid")) { control.transition("latest").uses(OrderingController.class).get(this); control.transition("cancel").uses(OrderingController.class).cancel(this); } return control.getTransitions(); } }
Start downloading all data : the client jars, server jars and both server side and client side application.
You can download a sample client and server side application on the same link, those will be helpful for you too understand how to use Restfulie.
In order to use Restfulie in your client side app, simply add all required jars to your classpath.
Download vraptor’s blank project and add all server side jars to your application’s classpath.
The entry point for Restfulie’s api is the Restfulie class. It’s basic usage is through the resource method which, given an URI, will allow
you to retrieve a resource or post to a resource controller:
Order order = Restfulie.resource("http://www.caelum.com.br/orders/1").get(); Client client = new Client(); Restfulie.resource("http://www.caelum.com.br/clients").post(client);
Due to the nature of the entry point and the java bytecode, Restfulie is still unable to allow the user to make the http verb even more transparent.
As seen earlier, as soon as you have aquired an object through the use of the restfulie api, you can invoke its transitions:
Order order = Restfulie.resource("http://www.caelum.com.br/orders/1").get(); resource(order).getTransition("cancel").execute();
The resource method can be statically imported from the Restfulie class.
Restfulie uses XStream behind the scenes, therefore all XStream related annotations are supported by default when using it.
The following example shows how to alias a type:
@XStreamAlias("order") public class Order { }
More info on how to configure XStream through the use of annotations can be “found in its website”:“http://xstream.codehaus.org”.
By default, Restfulie serializes all primitive, String and enum types. In order to serialize child elements, one has pre-configure Restfulie. This is
the typical usage-pattern applications will face while using restfulie:
Resources resources = Restfulie.resources(); resources.configure(Order.class).include("items"); // the configuration step is completed, so lets use it now: resources.entryAt("http://www.caelum.com.br/clients").post(new Client());
The entire serialization process can be configured either through the Resources interface’s methods or using *XStream*’s explicit configuration.
One can access all possible transitions for an object by invoking a resource’s getTransitions method:
List<Transition> transitions = resource(order).getTransitions();
By default, restfulie uses a well known table of defaults for http verb detection according to the rel element:
- destroy, cancel and delete send a DELETE request
- update sends a PUT request
- refresh, reload, show, latest sends a GET request
- other methods sends a POST request
If you want to use a custom http verb in order to send your request, you can do it:
payment = resource(order).getTransition("pay").method(HttpMethod.PUT).executeAndRetrieve(payment);
If you need to send some information to the server, this can be done by passing an argument to the execute method, which will be serialized and sent as the request body’s content:
payment = resource(order).getTransition("pay").method(HttpMethod.PUT).executeAndRetrieve(payment);
Once you have found the entry point you want to use (retrieving a resource or creating one), the javadoc api is a resourcefull place for more info.
The default way to use Restfulie is to define the getFollowingTransitions method in your resource. The method receives a Restfulie instance (server side version)
which allows you to dsl-like create transitions. In order to do that, given a Restfulie object, invoke the transition method with your rel name and the relative
controller action:
public List<Transition> getFollowingTransitions(Restfulie control) { control.transition("delete").uses(OrderingController.class).cancel(this); return control.getTransitions(); }
Note that both the OrderingController class with its cancel method are web methods made available through the use of vraptor:
@Resource public OrderingController { @Delete @Path("/order/{order.id}") @Transition public void cancel(Order order) { order = database.getOrder(order.getId()); order.cancel(); status.ok(); } }
Now you need to set up your application package in web.xml. This is the only configuration required:
<context-param> <param-name>br.com.caelum.vraptor.packages</param-name> <param-value>br.com.caelum.vraptor.rest,com.your.app.package.without.leading.whitespace</param-value> </context-param>
By using the @Transition to annotate your method, Restfulie will automatically load the order from the database and check for either 404 (object not found), 405 (method not allowed), 409 (conflict: transition is not allowed for this resource’s state).
This is one of the advantages of using Restfulie over other level 2 Rest frameworks. By supporting hypermedia content and handling transitions out of the box, Restfulie creates a new layer capable
of helping the server to deal with unexpected states.
1. Create your model (i.e. Order)
@XStreamAlias(“order”)
public class Order {private String id;
private Location location;
@XStreamImplicit
private List items;private String status;
private Payment payment;public enum Location {
takeAway, drinkIn
};// add id setter
// add desired getters}
@XStreamAlias(“item”)
public class Item {
enum Coffee {latte, cappuccino, espresso};
enum Milk {skim, semi, whole};
enum Size {small, medium, large};private Coffee name;
private int quantity;
private Milk milk;
private Size size;// getters and setters
}
2. Usually the getFollowingTransitions method would check the resource state in order to coordinate which transitions can be executed:
So add the following_transitions method returning an array of possible transitions:
public class Order implements StateResource { public List<Transition> getFollowingTransitions(Restfulie control) { if (status.equals("unpaid")) { control.transition("latest").uses(OrderingController.class).get(this); control.transition("cancel").uses(OrderingController.class).cancel(this); control.transition("pay").uses(OrderingController.class).pay(this,null); } return control.getTransitions(); } }
3. Create your retrieval method:
@Get @Path("/order/{order.id}") public void get(Order order) { order = database.getOrder(order.getId()); result.use(xml()).from(order).include("items").serialize(); }
You are ready to go. Create a new order and access it through your /order/id path.
The best way to start is to download the sample application and go through the OrderingController and Order classes.
While JAX-RS will deserialize your request body into your method argument and require you to retrieve extra URI information through the requested URI, Restfulie accepts one
core parameter (based on its alias) and extra parameters to be extracted through the URI itself:
@Post @Path("/order/{order.id}/pay") @Consumes("application/xml") @Transition public void pay(Order order, Payment payment) { order = database.getOrder(order.getId()); order.pay(payment); status.ok(); }
If you are looking for or want to help, let us know at the mailing list:
http://groups.google.com/group/restfulie
VRaptor’s website also contain its own mailing list which can be used to get help on implementing controller’s.
Restfulie was created and is maintained within Caelum by
Projetct Founder
- Guilherme Silveira ( email ) – twitter:http://www.twitter.com/guilhermecaelum
Contributors
- Lucas Cavalcanti (email) – twitter:http://www.twitter.com/lucascs
- Adriano Almeida (email) – twitter:http://www.twitter.com/adrianoalmeida7
You can see an application’s source code here, both client and server side were implemented using restfulie:
There is a portuguese tutorial on the server-side support and a blog post on the entire ecosystem in english
- refactor order, receipt and paymento to have methods as order.pay
- rename getHref to getUri
- allow vraptor to map two different methods to the same path and verb in different states
- if transition does not exist, getTransition should throw an exception?
- support custom namespaces
- if all deserialized objects return an enhanced type, it should be able to do resource(order).getResponseCode??
- how to survive errors as 500 when expecting an entity?
- allows pure String/byte array client side post and server side retrieval
- customize the rel name
- public cruise build
- routes.uriFor(OrderingController.class).get(order); status.created(routes.getUri()); should become status.created(order);
- release 0.4
- rel prepend suffix as http://iansrobinson.com/resources/link-relations/preceding
- automatically generate uri for this rel with its transition description
- pure href definition of link
- get entry point support
- Set the correct media type instead of application/xml
- full support to extended json
- allow servers to define transitions by accessing other systems
- allow servers to define a state method instead of internal variable
- controller filtering and methods
- english tutorial
- when receiving a 201 + content, it should believe the content
- when receiving a 201 without any content, it should allow to redirect or not
- client side should allow withTimeStamp, withETag, withAuth
- is there is an etag, use it by default (maybe NOT use it by default)… modified since and so on (header tags)
- server side maybe allow hypermedia controls or not
Users are encourajed to contribute with extra implementations for each layer (i.e. spring mvc implementation for the controller layer).
Check the license file