JBoss.orgCommunity Documentation
JAX-RS (Java API for RESTful Web Services) is a Java EE standard (JSR-311) for implementing REST-based Web services in Java. Errai JAX-RS brings this standard to the browser and simplifies the integration of REST-based services in GWT client applications. Errai can generate proxies based on JAX-RS interfaces which will handle all the underlying communication and serialization logic. All that's left to do is to invoke a Java method.
Errai's JAX-RS support consists of the following:
A client-side API to communicate with JAX-RS endpoints
A code generator that runs at your project's build time, providing proxy implementations for each JAX-RS resource interfaces visible within the GWT module
Errai IoC and CDI providers that allow you to
@Inject
instances of {{Caller<T>} (the same API used in Errai RPC)}
Integration with either Errai Marshalling or Jackson to translate request and response data between Java object and a string-based wire format
If you want to get started right away with a working Errai JAX-RS CRUD application, you can use our jaxrs-quickstart Maven archetype. See the Quickstart Guide for details. If you want to set up a project from scratch see the next chapter.
To use Errai JAX-RS, you must include it on the compile-time classpath. If you are using Maven for your build, add this dependency:
<dependency>
<groupId>org.jboss.errai</groupId>
<artifactId>errai-jaxrs-client</artifactId>
<version>${errai.version}</version>
<scope>provided</scope>
</dependency>
or if you are not using Maven for dependency management, add
errai-jaxrs-client-version.jar
to your classpath.
If you intend to use Errai's JSON format on the wire you will need to add Errai's JAX-RS JSON provider to your classpath and make sure it gets deployed to the server.
<dependency>
<groupId>org.jboss.errai</groupId>
<artifactId>errai-jaxrs-provider</artifactId>
<version>${errai.version}</version>
</dependency>
or manually add
errai-jaxrs-provider-version.jar
in case you're not using Maven. If your REST service returns Jackson generated JSON you do not need the errai-jaxrs-provider (see
Configuration
) .
Once you have Errai JAX-RS on your classpath, ensure your application inherits the GWT module as well. Add this line to your application's
*.gwt.xml
file:
<inherits name="org.jboss.errai.enterprise.Jaxrs"/>
Errai's JAX-RS support consists mostly of features that make the client side easier and more reliable to maintain. You will need to use an existing third-party JAX-RS implementation on the server side. All Java EE 6 application servers include such a module out-of-the-box. If you are developing an application that you intend to deploy on a plain servlet container, you will have to choose a JAX-RS implementation (for example, RestEasy) and configure it properly in your web.xml.
Alternatively, you could keep your REST resource layer in a completely separate web application hosted on the same server (perhaps build an Errai JAX-RS client against an existing REST service you developed previously). In this case, you could factor out the shared JAX-RS interface into a shared library, leaving the implementation in the non-Errai application.
Finally, you can take advantage of the cross-origin resource sharing (CoRS) feature in modern browsers and use Errai JAX-RS to send requests to a third-party server. The third-party server would have to be configured to allow cross-domain requests. In this case, you would write a JAX-RS-Annotated interface describing the remote REST resources, but you would not create an implementation of that interface.
Errai JAX-RS works by leveraging standard Java interfaces that bear JAX-RS annotations. You will also want these interfaces visible to server-side code so that your JAX-RS resource classes can implement them (and inherit the annotations). This keeps the whole setup typesafe, and reduces duplication to the bare minimum. The natural solution, then is to put the JAX-RS interfaces under the client.shared package within your GWT module:
project
src
main
java
com.mycompany.myapp
MyApp.gwt.xml [the app's GWT module]
com.mycompany.myapp.client.local
MyAppClientStuff.java [code that @Injects Caller<MyAppRestResource>]
com.mycompany.myapp.client.shared
CustomerService.java [the JAX-RS interface]
com.mycompany.myapp.server
CustomerServiceImpl.java [the server-side JAX-RS resource implementation]
The contents of the server-side files would be as follows:
Example 7.1. CustomerService.java
@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 above interface is visible both to server-side code and to client-side code. It is used by client-side code to describe the available operations, their parameter types, and their return types. If you use your IDE's refactoring tools to modify this interface, both the server-side and client-side code will be updated automatically.
Example 7.2. CustomerServiceImpl.java
public class CustomerServiceImpl implements CustomerService {
@Override
public List<Customer> listAllCustomers() {
// Use a database API to look up all customers in back-end data store
// Return the resulting list
}
@Override
public long createCustomer(Customer customer) {
// Store new Customer instance in back-end data store
}
}
The above class implements the shared interface. Since it performs database and/or filesystem operations to manipulate the persistent data store, it is not GWT translatable, and it's therefore kept in a package that is not part of the GWT module.
Note that all JAX-RS annotations (
@Path
,
@GET
,
@Consumes
, and so on) can be inherited from the interface. You do not need to repeat these annotations in your resource implementation classes.
This section assumes you have already set up the CustomerService JAX-RS endpoint as described in the previous section.
To create a request on the client, all that needs to be done is to invoke
RestClient.create()
, thereby providing the JAX-RS interface, a response callback and to invoke the corresponding interface method:
Example 7.3. App.java
...
Button create = new Button("Create", new ClickHandler() {
public void onClick(ClickEvent clickEvent) {
Customer customer = new Customer(firstName, lastName, postalCode);
RestClient.create(CustomerService.class, callback).createCustomer(customer);
}
});
...
For details on the callback mechanism see Handling Responses .
Injectable proxies can be used as an alternative to calling
RestClient.create()
.
@Inject
private Caller<CustomerService> customerService;
To create a request, the callback objects need to be provided to the
call
method before the corresponding interface method is invoked.
customerService.call(callback).listAllCustomers();
To use caller injection, your application needs to inherit the Errai IOC GWT module. To do this, just add this line to your application's
*.gwt.xml
file and make sure it comes
after
the Errai JAX-RS module (see
Getting Started
):
<inherits name="org.jboss.errai.ioc.Container"/>
The JAX-RS interfaces need to be visible to the GWT compiler and must therefore reside within the client packages (e.g. client.shared).
An instance of Errai's
RemoteCallback<T>
has to be passed to the
RestClient.create()
call, which will provide access to the JAX-RS resource method's result.
T
is the return type of the JAX-RS resource method. In the example below it's just a
Long
representing a customer ID, but it can be any serializable type (see
Chapter 5, Marshalling
).
RemoteCallback<Long> callback = new RemoteCallback<Long>() {
public void callback(Long id) {
Window.alert("Customer created with ID: " + id);
}
};
A special case of this
RemoteCallback
is the
ResponseCallback
which can be used as an alternative. It provides access to the
Response
object representing the underlying HTTP response. This is useful when more details of the HTTP response are needed, such as headers and the status code. The
ResponseCallback
can also be used for JAX-RS interface methods that return a
javax.ws.rs.core.Response
type. In this case, the
MarshallingWrapper
class can be used to manually demarshall the response body to an entity of the desired type.
ResponseCallback callback = new ResponseCallback() {
public void callback(Response response) {
Window.alert("HTTP status code: " + response.getStatusCode());
Window.alert("HTTP response body: " + response.getText());
}
};
For handling errors, Errai's error callback mechanism can be reused and an instance of
ErrorCallback
can optionally be passed to the
RestClient.create()
call. In case of an HTTP error, the
ResponseException
provides access to the
Response
object. All other
Throwables
indicate a communication problem.
ErrorCallback errorCallback = new RestErrorCallback() {
public boolean error(Request request, Throwable throwable) {
try {
throw throwable;
}
catch (ResponseException e) {
Response response = e.getResponse();
// process unexpected response
response.getStatusCode();
}
catch (Throwable t) {
// process unexpected error (e.g. a network problem)
}
return false;
}
};
Client-side remote call interceptors provide the ability to manipulate or bypass the request before it's being sent. This is useful for implementing crosscutting concerns like caching or security features e.g:
avoiding the request when the data is cached locally
adding special HTTP headers or parameters to the request
To have a JAX-RS remote call intercepted, either an interface method or the remote interface type has to be annotated with
@InterceptedCall
. If the type is annotated, all interface methods will be intercepted.
@Path("customers")
public interface CustomerService {
@GET
@Path("/{id}")
@Produces("application/json")
@InterceptedCall(MyCacheInterceptor.class)
public Customer retrieveCustomerById(@PathParam("id") long id);
}
Note that an ordered list of interceptors can be used for specifying an interceptor chain e.g.
@InterceptedCall({MyCacheInterceptor.class, MySecurityInterceptor.class})
public Customer retrieveCustomerById(@PathParam("id") long id);
Implementing an interceptor is easy:
public class MyCacheInterceptor implements RestClientInterceptor {
@Override
public void aroundInvoke(final RestCallContext context) {
RequestBuilder builder = context.getRequestBuilder();
builder.setHeader("headerName", "value");
context.proceed();
}
}
The
RestCallContext
passed to the
aroundInvoke
method provides access to the context of the intercepted JAX-RS (REST) remote call. It allows to read and write the parameter values provided at the call site and provides read/write access to the
RequestBuilder
instance which has the URL, HTTP headers and parameters set.
Calling
proceed
executes the next interceptor in the chain or the actual remote call if all interceptors have already been executed. If access to the result of the (asynchronous) remote call is needed in the interceptor, one of the overloaded versions of
proceed
accepting a
RemoteCallback
has to be used instead.
The result of the remote call can be manipulated by calling
RestCallContext.setResult()
.
Not calling
proceed
in the interceptor bypasses the actual remote call, passing
RestCallContext.getResult()
to the
RemoteCallBack
provided at the call site.
Errai's JSON format will be used to serialize/deserialize your custom types. See Chapter 5, Marshalling for details.
Alternatively, a Jackson compatible JSON format can be used on the wire. See Configuration for details on how to enable Jackson marshalling.
All paths specified using the
@Path
annotation on JAX-RS interfaces are by definition relative paths. Therefore, by default, it is assumed that the JAX-RS endpoints can be found at the specified paths relative to the GWT client application's context path.
To configure a relative or absolute root path, the following JavaScript variable can be set in either
the host HTML page
<script type="text/javascript">
erraiJaxRsApplicationRoot = "/MyJaxRsEndpointPath";
</script>
or by using a JSNI method:
private native void setMyJaxRsAppRoot(String path) /*-{
$wnd.erraiJaxRsApplicationRoot = path;
}-*/;
or by simply invoking:
RestClient.setApplicationRoot("/MyJaxRsEndpointPath");
The root path will be prepended to all paths specified on the JAX-RS interfaces. It serves as the base URL for all requests sent from the client.
The following options are available for activating Jackson marshalling on the client. Note that this is a client-side configuration, the JAX-RS endpoint is assumed to already return a Jackson representation (Jackson is supported by all JAX-RS implementations). The
errai-jaxrs-provider.jar
does not have to be deployed on the server in this case!
<script type="text/javascript">
erraiJaxRsJacksonMarshallingActive = true;
</script>
or by using a JSNI method:
private native void setJacksonMarshallingActive(boolean active) /*-{
$wnd.erraiJaxRsJacksonMarshallingActive = active;
}-*/;
or by simply invoking:
RestClient.setJacksonMarshallingActive(true);