dbevenius

Blog

View My GitHub Profile

AeroGear SimplePush Server 0.8.0

This is the first community release of AeroGear's SimplePush Server.

So what is a SimplePush Server?

It's a server side implementation of the SimplePush Protocol specification from Mozilla. The SimplePush protocol describes a JavaScript client API and a protocol which allows backend applications to send notification messages to their web applications.

Firefox OS v1.1 uses SimplePush as its Push Notification technology. Firefox OS Push Notifications are designed for one thing – waking up apps. They do not deal with data, desktop notifications or anything else, since there are other Web APIs that provide them. From the very beginning SimplePush was designed to explicitly not carry any payload. Instead a version number is sent to the client. Based on that version number the client can perfom an action, e.g. refresh a view of data.

AeroGear SimplePush Server

Mozilla provides a server implementation based on the Go programming language and AeroGear's JavaScript client will work against that implementation with out any problems. We wanted to provide another server option both for security (some companies would like more control over the push environment) and as a way to set up completely internal push networks. For example, a company with an internal app with a need for push notifications could deploy a SimplePush server on their internal network/VPN and have push messaging capability to their web apps without having to contact an outside server.

AeroGear SimplePush is a Java implementation of the server side part of the specification and two version are provided, one based on Netty, and one based on Vert.x.

The SimplePush specification states that secure WebSockets should be used as the transport protocol but for web applications this is not always possible. Older browsers versions may lack WebSocket support, or issues with restrictive proxies in combination with WebSockets can lead to situations where the communication will simply not work. To counter this AeroGear's SimplePush server uses SockJS to provide a fallback strategy. SockJS provides a client side and server side protocol and provides fallback handling in situations where a WebSocket connection cannot be made. AeroGear uses Netty's upcoming SockJS support which takes care of the SockJS handling.

If you feel like trying things out we will shortly be providing an OpenShift cartridge which will enable you to create a new application on OpenShift that contains both AeroGear SimplePush Server and AeroGear UnifiedPush server.
You can also try out AeroGear SimplePush Server by building yourself. The deployment options are to run standalone or embedded in AS7/WildFly for the Netty based version and standalone for the Vert.x version. Details of getting this up and running can be found in the modules.

For more information about the SimplePush protocol and how it works please refer to Mozilla's specification and AeroGear SimplePush protocol documentation.

If you have any questions, problems, or comments the please post to our mailing list.

References

AeroGear Controller 1.0.0

We are pleased to announce the availablity of AeroGear Controller 1.0.0 which is a very lean model view controller written in Java. It focuses on the routing of HTTP requests to plain Java Object endpoints, and the handling of the results of these invocations. The results can either be forwarded to a view, or returned in the format requested by the caller.

Below is a list of some of the features that are available in 1.0.0:

What follows is a description of the pagination feature.Details about all the features can be found in the user guide.

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:

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 PaginationInfo type which will be populated with values from the request 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 would be 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://server/cars?offset=0&color=red&limit=5>; rel="previous",  
<http://server/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.

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


What's new in AeroGear Controller 1.0.0.M8

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

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 an offset/limit:

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 default Web Linking (more about this shortly).

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 as mentioned above using the @Paginated annotation.

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


What's new in AeroGear Controller 1.0.0.M1

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

Error Routes

You can now configure a route as an error handler for a type of exception:

route()
       .on(YourException.class)
       .to(ExceptionHandler.class).errorPage(); 

You can specify multiple exceptions if needed:

route()
       .on(YourException.class, SomeOtherException.class)
       .to(ExceptionHandler.class).errorPage();

If you'd like to log the exception in the error route, you can specify that the target method, errorPage() above, should take a parameter:

route()
       .on(YourException.class, SomeOtherException.class)
       .to(ExceptionHandler.class).errorPage(param(Exception.class));

As mentioned previously in this document a view for ExceptionHandler would in this case be located in:

/WEB-INF/pages/ExceptionHandler/errorPage.jsp   

The exception is made available to the jsp page:

${requestScope['org.jboss.aerogear.controller.exception']}

Cross Origin Resource Sharing (CORS) Support

CORS is supported by default but can be disabled or configured differently by implementing a CDI Producer:

@Produces
public CorsConfiguration demoConfig() {
    return CorsConfig.enableCorsSupport()
                      .anyOrigin()
                      .enableCookies()
                      .exposeHeaders("header1")
                      .maxAge(20)
                      .enableAllRequestMethods()
                      .validRequestHeaders("header1");
    }

To disable CORS support

@Produces
public CorsConfiguration demoConfig() {
    return CorsConfig.disableCorsSupport();
}

RESTful endpoints

In previous versions of AeroGear-Controller there was only support for forwarding requests to views, in a model-view-controller style. With version M7 there is partial support for RESTful endpoints.

For example, you can now specify that a Route produces a response:

route()
       .from("/cars")
       .roles("admin")
       .on(RequestMethod.POST)
       .produces(MediaType.JSON.toString(), MediaType.HTML.toString())
       .to(Home.class).save(param(Car.class));

In the above example, a POST to this route can respond with either JSON or HTML (forwarding to a view) depending on the HTTP Accept Header. Also, this route will only be accessible to a user belonging to the admin group.

As mentioned RESTful support is only partially implemented as we still need to be able to consume other formats, like JSON for example. This will be added with CR1 and full support will exist in 1.0.0.

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.

What's next?

Resources