We all love REST. Its provider, platform, and language are neutral; its easy to debug, implement and access; and it provides a reliable back of your cloud, browser, mobile and desktop applications.
Java developers can use libraries that support JAX-RS , such as RESTEasy , to get a REST server running in just a few minutes. Then, using JAX-RS clients , these JAX-RS REST servers can be called from Java client applications with just a few lines of code.
But, despite the fact that GWT has much in common with Java, calling REST services from GWT can be painful. Using the RequestBuilder class involves specifying the correct HTTP method, encoding your URL, and then either decoding the response or creating Overlay to represent the data sent by the REST server. This may not be a big overhead for calling one or two REST methods, but it works a lot when integrating GWT with the more complex REST service.
Here is Errai . Errai is a JBoss project that, among other things, implements JAX -RS as part of the GWT . Theoretically, this means that you can share your JAX-RS interface between Java and GWT projects by providing a single source that defines the functionality of your REST server.
Calling an Errai REST server involves only a few simple steps. First, you need a JAX-RS REST interface. This is the JAX-RS annotated Java interface that defines the methods that your REST server will provide. This interface can be shared between Java and GWT projects.
@Path("customers") public interface CustomerService { @GET @Produces("application/json") public List<Customer> listAllCustomers(); @POST @Consumes("application/json") @Produces("text/plain") public long createCustomer(Customer customer); }
The REST interface is then injected into your GWT client class.
@Inject private Caller<CustomerService> customerService;
A response handler is defined.
RemoteCallback<Long> callback = new RemoteCallback<Long>() { public void callback(Long id) { Window.alert("Customer created with ID: " + id); } };
And finally, the REST method is called.
customerService.call(callback).listAllCustomers();
Pretty simple, huh?
In this example, you might think that Errai will provide you with a solution for your current JAX-RS infrastructure, but unfortunately this simple example does not address some of the complications that you will probably see when you try to combine your GWT database and Java REST . Here are some of the errors to consider when using Errai and JAX-RS.
You need to implement CORS
Typically, when you deploy the GWT JAX-RS client, you will debug your GWT application on an external REST server. This will not work if you do not implement CORS , because by default the browser hosting the GWT application will not allow your JavaScript code to contact a server that does not work in the same domain. In fact, you can even run the REST server on your local development computer and still encounter these cross-domain access issues, as calls between different ports are also denied.
If you use RESTEasy, the CORS implementation can be done in two ways. The first is done using the MessageBodyInterceptors interface. You provide a write () method and annotate your class with @Provider and @ServerInterceptor annotations. The write () method is then used to add the "Access-Control-Allow-Origin" header to respond to any simple requests ("simple" requests do not set custom headers, and the request body uses only plain text).
The second method handles CORS pre-validation requests (for HTTP request methods that can cause side effects for user data - in particular, for non-GET HTTP methods or for using POST with certain MIME types). These requests use the HTTP OPTIONS method and expect to get the headers "Access-Control-Allow-Origin", "Access-Control-Allow-Methods" and "Access-Control-Allow-Headers" in the response. This is shown in the handleCORSRequest () method below.
Note
The REST interface below allows any and all CORS requests that may not be suitable from a security point of view. However, it is unreasonable to assume that preventing or restricting CORS at this level will provide any degree of security, for example, setting up a proxy server so that these requests on behalf of the client are quite trivial.
@Path("/1") @Provider @ServerInterceptor public class RESTv1 implements RESTInterfaceV1, MessageBodyWriterInterceptor { @Override public void write(final MessageBodyWriterContext context) throws IOException, WebApplicationException { context.getHeaders().add(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*"); context.proceed(); } @OPTIONS @Path("/{path:.*}") public Response handleCORSRequest(@HeaderParam(RESTInterfaceV1.ACCESS_CONTROL_REQUEST_METHOD) final String requestMethod, @HeaderParam(RESTInterfaceV1.ACCESS_CONTROL_REQUEST_HEADERS) final String requestHeaders) { final ResponseBuilder retValue = Response.ok(); if (requestHeaders != null) retValue.header(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders); if (requestMethod != null) retValue.header(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_METHODS, requestMethod); retValue.header(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*"); return retValue.build(); } }
Using these two methods, any call to your REST server will provide appropriate answers to resolve cross-lookup requests.
You need to accept and respond with simple POJO
The introduction illustrates the simple REST interface that Long responded to. Both Java and JAX-RS GWT implementations know how to serialize and deserialize primitives and simple classes, such as java.util collections.
In the real world, your REST interface will respond with more complex objects. Different implementations may collide here.
For starters, JAX-RS and Errai use different annotations to customize the sorting of objects between JSON and Java objects. Errai has annotations like @MapsTo and @Portable, while RESTEasy (or Jackson, the JSON marshaller ) uses annotations like @JsonIgnore and @JsonSerialize. These annotations are mutually exclusive: the GWT will complain about Jackson's annotations, and Jackson cannot use Errai annotations.
A simple solution is to use a set of simple POJOs with your leisure interface. Simple, I mean classes that have no-args constructors, and have only getter and setter methods that are directly related to the properties that will be present in the JSON object as it passes through the wire. Simple POJOs can be combined with both Errai and Jackson with default settings, eliminating the need to manipulate incompatible annotations.
Errai and Jackson also contain names for the resulting properties in the JSON string from different places. Jackson will use the getter and setter method names, while Errai will use the instance variable names. Therefore, make sure your instance names and getter / setter methods match exactly. This is normal:
public class Test { private int count; public int getCount() {return count;} public void setCount(int count) {this.count = count;} }
This will cause problems:
public class Test { private int myCount; public int getCount() {return myCount;} public void setCount(int count) {this.myCount = count;} }
Secondly, it is tempting to add additional methods to these REST data objects to implement some business functions. However, if you do this, it will not take long before you try to use a class that is not supported by GWT, and you may be surprised that GWT does not support : date formatting, cloning arrays, converting strings to bytes []. .. The list goes on. Therefore, itβs best to stick to the basics in REST data objects and implement any business logic completely outside the inheritance tree of REST data objects using something like composition or component design.
Note
Without the @Portable annotation, you will need to manually list any classes used by Errai when invoking the REST interface in the ErraiApp.properties file .
Note
You also want to stay away from Maps. See this error for more details.
Note
You cannot use nested parameterized types in the object hierarchy returned by your JSON server. See this error and this forum post for more details.
Note
Errai has problems with the byte []. Use a list instead. See this forum for more details.
You need to debug firefox
When it comes to sending large amounts of data through the REST interface using GWT, you will have to debug your application using Firefox. In my own experience, encoding even a small file in bytes [] and sending it over the network caused all kinds of errors in Chrome. Many different exceptions will be thrown because the JSON encoder will try to process the corrupted data; exceptions that do not occur in the compiled version of the GWT application or when debugging in Firefox.
Unfortunately, Google did not manage to update their Firefox GWT plugins with the new Mozillas release cycles, but you can often find an unofficial Alan Leung release on the GWT Google Groups forums. This link contains the version of the GWT plugin for Firefox 12, and this link has a version for Firefox 13.
You need to use Errai 2.1 or later.
Only Errai 2.1 or later will create Jackson compatible JSON, which is necessary if you are trying to integrate GWT with RESTEasy. Jackson Markelling can be enabled using
RestClient.setJacksonMarshallingActive(true);
or
<script type="text/javascript"> erraiJaxRsJacksonMarshallingActive = true; </script>
You need to create separate JAX-RS interfaces for advanced features.
If your JAX-RS REST interface returns additional objects, for example ATOM (or, moreover, imports classes such as org.jboss.resteasy.plugins.providers.atom.Feed), you need to split the REST interface into two Java interfaces, because Errai is not aware of these objects and classes are probably not in a state that can be easily imported into GWT.
One interface may contain your plain old JSON and XML methods, while another may contain ATOM methods. This way you can avoid having to refer to an interface with unknown classes in your GWT application.
Note
Errai only supports JSON mashalling at this point , although in the future you will be able to define custom marshalers.