Saturday, December 8, 2012

Transparent REST client code for your services using Spring 3 and RESTEasy

The advantages to creating REST services has been re-iterated enough to be skipped here.  I will quickly mention my personal most practical favorite is that it allows the I/O of the service to be visible enough that the endpoints can be easily debugged in isolation using curl.  One of the big dangers is that the simplicity of creating client code can lead to issues like duplication and inconsistency as on- off code is thrown together.  Ironically the simplicity that is the strength of REST can become its weakness as the need for structure is so dramatically reduced.  Most JAX-RS implementations (I think) provide support for a client package where you can be left with the best of both worlds: a simple REST service and a means to automatically generate
structured client code.

RESTEasy is being used because the target environment is running JBoss servers.  Alternative JAX-RS implementations such as Jersey and CXF should offer similar mechanisms, though they were not researched thoroughly.

Background

Basic Objectives

This solution assumes that both a service and a client package for that service are being developed.  There is nothing to prevent the client solution from being used by itself, but it would likely not be justifiable considering the relative complexity of the produced system and the associated dependencies.

The basic goals are as follows:
  • The service should be a simple, standard REST service
  • Java client could should be able to transparently access the service
  • The API information should be DRY between systems
  • It should be easier to code than alternatives

The simple client alternative

More often than not it seems as though REST clients are created directly using HttpClient or (in Spring) using the RestTemplate.  This a very simple and straightforward approach.  Additionally...it is quite likely the most viable alternative when accessing a third party service that does not provide a specific client package.  In particular this would make sense if the service is implemented in another language (and I would probably recommend using a language that allows for faster development unless there's a reason for Java.

Possible Issues

(All of these could be avoided of course)
  • The API information (including DTOs) is likely to be duplicated in the client and the server
  • Management of the connection and serialization concerns may need to be addressed (and therefore likely to be buggy)
  • The connection may be implemented outside of a properly defined layer and interface

RESTEasy Proxy Client in Spring

RESTEasy provides a proxy client that can be used to connect to a defined service interface.  The documentation demonstrates how it can be used, but (as it is framework agnostic) does not provide integration information.  Using Spring 3 programmatic configuration allows for seamless automatic creation of a service layer which can be used to access the remote REST API.  This keeps the definition of the service consolidated between client and server and therefor eases maintenance.  Additionally it allows the client system to call the proxy and be provided with an object without concern of where it is coming from, and allowing the RESTEasy code to properly handle the details.

Implementation

The Interface

The JAX-RS information should be provided in the form of an Interface if that is not normally practiced.  The JAX-RS Interface is what drives everything, it should be packaged in a location that is accessible to both the client and the server (as should any DTOs), within the client module would work as a default.  Simple example:

@Path(ExampleResource.PATH)
public interface ExampleResource {
  public static final String PATH = "/collection/";

  @GET
  @Path("/{id}")
  @Produces(Mediatype.APPLICATION_JSON)
  Example findOne(@PathParam("id") Long id);

}

The Server

The server should provide the endpoint that implements the created Interface.  This is standard JAX-RS behavior.  It could be done directly on top of an existing service, I personally like to keep it in a controller layer to ensure that it can remain resource oriented while the services may be more message oriented.

The Client (the actual significant part)

Now the fun way to bridge the gap left in the RESTEasy docs of how to tie those client proxies in to your system.  Using Spring 3 annotations for programmatic configuration you can add the following to a re-usable client module:

@Configuration
public class RestClientConfiguration {
  @Value("${restServiceRoot}")
  private String restServiceRoot;

  public ServiceBeanConfiguration() {
    RegisterBuiltin.register(
      ResteasyProviderFactory.getInstance());
  }

  @Bean
  public ExampleResource exampleResourceClientService() {
    return ProxyFactory.create(ExampleResource.class, restServiceRoot);
  }
}

This creates a new Spring bean named "exampleResourceClientService" (the name of the method or specified as a value for @Bean) which can then just be injected as needed (ideally using the Interface) and called like Example example = exampleResourceClientService.findOne(id);.  Additionally the above assumes you have a property named restServiceRoot which points to the server (such as "http://myservice.example.com/rest").

Conclusion

Following the above you should be able to create a client jar that can be included in any project and scanned to get an easily maintained client package for very little work.  Additional services can be created by adding new methods to the configuration class, and further customizations can be easily applied.  You end up with a nice simple REST API but with the kind of client proxy that is normally associated with heavier communication technologies.

No comments :

Post a Comment