Skip to content

Instantly share code, notes, and snippets.

@danbev
Last active December 11, 2015 20:28
Show Gist options
  • Save danbev/4655294 to your computer and use it in GitHub Desktop.
Save danbev/4655294 to your computer and use it in GitHub Desktop.
What's new in AeroGear Controller 1.0.0M8

What's new in AeroGear Controller M8

With the 1.0.0.M8 release of AeroGear-Controller we have added the following features:

  • Pagination support
  • Consuming/Producing custom media types

Pagination in AeroGear Controller

Pagination is the ability to return a limited number of elements in response to a call. The number of elements returned is referred to as a page. In AeroGear controller the strategy used for implementing pagination is using a offset/limit:

  • offset
    The offset of the first element that should be included in the returned collection. Default value is 0.
  • limit
    The number of elements that should be returned. Default value is 10.

Let's look at an example of what a Route with an endpoint that supports pagination looks like:

route()
      .from("/cars")
      .on(RequestMethod.GET)
      .produces(JSON)
      .to(Cars.class).findCarsBy(param(PaginationInfo.class), param("color"));

PaginationInfo is used to avoid having to add the params for offset and limit which would otherwise have been required:

.to(Cars.class).findCarsBy(param("offset"), param("limit"), param("color"));

This might not look too bad, but imaging that you need to specify several query parameters in addition to color in the example above, and it can get pretty messy. So, instead we use the PaginationInfo type which it will be populated with the request values which are then available to the target method. This information can then be used by the endpoint target method to return a paged collection of data.

The target endpoint method is annotated and this is where pagination can be configured:

@Paginated 
public List<Car> findCarsBy(PaginationInfo paginationInfo, String color) {
    return getCars(paginationInfo.getOffset(), color, paginationInfo.getLimit());
}

@Paginated has options that can be overridden. For example, you can specify that you want to use different names of the query parameters than the default offset, limit, and that you'd like to use custom headers instead of Web Linking.
The source for findCarsBy and the other examples in this post are part of aerogear-controller-demo.

Related to pagination is also the ability to navigate, to get to the next/previous page. By default Web Linking is used to provide links to the next and previous pages as appropriate. Example of a HTTP response with Web Linking:

HTTP/1.1 200 OK
Link: 
<http://controller-aerogear.rhcloud.com/aerogear-controller-demo/cars?offset=0&color=red&limit=5>; rel="previous",  
<http://controller-aerogear.rhcloud.com/aerogear-controller-demo/cars?offset=10&color=red&limit=5>; rel="next"  
Content-Type: application/json;charset=UTF-8
Content-Length: 200
[
      {"color":"red","brand":"Mazda","id":41},
      {"color":"red","brand":"Mini","id":48},
      {"color":"red","brand":"Nissan","id":55},
      {"color":"red","brand":"Opel","id":62},
      {"color":"red","brand":"Scoda","id":69}
]

Web Linking can be disabled in favor of the perhaps simpler option of custom headers.

Consuming/Producing custom media types

This support enables specifying that a route can consume a custom media type, or produce a custom media type, or both.
For example:

route()
      .from("/cars")
      .on(RequestMethod.POST)
      .consumes(CUSTOM_MEDIA_TYPE)
      .produces(CUSTOM_MEDIA_TYPE)
      .to(Cars.class).save(param(Car.class));

The above example declares that this route will be able to consume and produce a custom media type. You can specify multiple media types for both the consumes and the produces method. The client uses the HTTP Accept header to specify what type it wants the server to respond with.

Adding a Custom consumer

A consumer is added to the system by adding a class that implements the Consumer interface. This will be picked up by the CDI container and registered for that specific content type.

Adding a Custom Producer/Responder

Adding a producer/responder currently differs from adding a consumer. The reason for this is that we discovered the need to be able to respond to the same media type in different ways.
For example, a client may request that it Accept: text/html. But the route could choose to respond with either a dynamically generated JSP, or a static HTML page.

So, let's take a look at what is required to implement a custom media type responder that returns data in a custom format to the caller, as opposed to forwarding the responds to a view:

public class CustomResponder extends AbstractRestResponder {
    
    public static final MediaType CUSTOM_MEDIA_TYPE = new MediaType("application/custom", CustomResponder.class);

    public CustomResponder() {
        super(CUSTOM_MEDIA_TYPE);
    }

    public void writeResponse(Object entity, RouteContext routeContext) 
        throws Exception {
        HttpServletResponse response = routeContext.getResponse();
        response.getWriter().write("CustomResponder returned: " + entity);
    }

}

If you also implement Responder directly if you have the need to forward to a view instead of responding of if you simply prefer to implement an interface or can't extend.

We would be more than happy to answer any questions you might have, and encourage you to try the demo on OpenShift. Links to source, mailing list, issue tracker etc, can be found in the Resources section below.

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment