Saturday, December 8, 2012

Thoughts on Spring Surf

Spring Surf is a View composition solution originally developed as part of Alfresco projects but which has recently been spun off into its own project (technlogies mentioned were previously s/Spring/Alfresco/).  I've been working with Surf on a project for the past 6 months and have grown familiar with many of its intricacies

What it is

Spring Surf  is probably best known for its use within Alfresco Share.  Surf provides a skeleton around Spring Webscripts which are effectively bundles of convention driven MVC functionality made to work within Alfresco.  Surf is largely notable for its very verbose XML based view definitions where the various components are customizable in multiple locations.  Additionally Surf creates a rich model which represents the assorted objects which play a role in the View and can be used while rendering for some view specific logic.

My Hopes

I adopted Spring Surf for a project because we use Alfresco and I was looking for a replacement solution that would keep stakeholders from desiring that some stale legacy code was carried forward.  The verbosity of Surf seemed to promise higher levels of flexibility, and combined with Alfresco that could allow for a greater amount of runtime control and publishing of a wide range of View updates through Alfresco Web forms.  The splitting off of Surf into a Spring project was promising as it indicated that Alfresco was interested in creating a richer ecosystem of libraries that were less coupled to their architecture.

DevCon

 I recently went to Alfresco DevCon 2012 primarily to get a feel for the future direction of the project so that any of my work would not diverge too strongly (particularly since most documentation is still focused on version 3 while 4 appeared to be a major shift).  Alfresco is understandably focused on enhancing its role as an ECM which co-exists with other solutions and in particular becoming the cloud compatible ECM.  Solutions which are not directly concerned with document management are increasingly being handled outside of Alfresco using CMIS and entirely different technologies (like Drupal).

My Realities(not necessarily anyone else's)

The biggest single hurdle in working with Surf was the lack of documentation.  Although it had been split off, the Spring pages were seemingly given no attention and there was effectively no information about using the framework in non-Alfresco contexts.  After DevCon it became painfully clear that this is due to Surf not being useful outside of Alfresco, particularly Alfresco Share.

Being focused on being an ECM Alfresco is in the role of allowing their solutions to be able to be worked with, but it is not their role to develop external solutions.  The splitting of Share into a more distinct client application was necessary to become more Cloud-y.  I can only suppose that shifting Webscripts and Surf to the Spring umbrella was done in the hopes that they would gain traction outside of Alfresco (which they haven't).  Alfresco provides a powerful platform for the repository and for the Share client, but on the client side the result is that you must be on top of the platform to reap the benefits.  The idea of further modularization into a library or loosely coupled framework is not on their horizon.

Surf is too cumbersome to compete with other similar solutions.  There is virtually no tooling to help with the creation of Surf files.  The one possible exception is the now seemingly abandoned Spring Roo plugin, which from my eyes is on the wrong end of the development tools: a possible benefit of having the view defined using Surf's approach would be to transfer greater power to a non-engineer (non Roo user).

The imagined flexibility didn't pan out as the model of the View is created entirely at start-up.  This makes sense and there are ways to work around it, but ultimately it does not provide any out of the box flexibility beyond any other view composition approaches.

Webscripts still have promise (and are used effectively in another department at my office), but unless they are communicating with Alfresco they could also be replaced by a far simpler and lighter solution.

Ultimately Spring Surf only makes sense as Alfresco Surf and used in Alfresco Share.  The entire structure makes perfect sense when viewed through the lens of a larger platform which allows for a consistent programming model when dealing with repository nodes and a View which represents the interface to that repository.  Outside of that context, however, the complexity it introduces provides no benefit over slight modifications of far, far simpler alternatives.

If you are building a solution that should sensibly be tied to the Alfresco platform, then using Surf to customize Share and optionally working with an Alfresco partner makes a lot of sense.  If you're looking for a more general purpose solution and may have even thought something like Spring == a solution that can help for a variety of problems: keep looking.

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.