Search This Blog

Loading...

Sunday, April 20, 2014

jersey/jaxrs 2.X example using spring JavaConfig and spring security

Introduction


I had blogged previously on using jersey/JAX-RS 2.0 when it was in a pre-release. Since then Jersey/JAX-RS 2.0 has released and undergone a few versions. As I have recently been working with the jersey 2.7-spring integration and spring security and figured I'd share an example.

Requirements


As is quite standard with me, I like to 'manufacture' requirements. The application being developed is a Notes application that allows one to perform CRUD operations for simple notes. So, for the Web Service being created, I have the following requirements:
  1. No XML- You get type safety during refactoring
  2. Spring Dependency Injection with Java Config. The official jersey/spring3 example is very nice but does not demonstrate Java Config usage.
  3. Jersey Should manage Resource classes an have Spring service objects injected into them
  4. Security should be enabled via Spring Security. Only users with the ROLE 'notesuser' should be able to create a note.

The Project

Look Ma, no XML

This example will use Servlet 3.X specification, so we eliminate web.xml. There are many ways one can eliminate XML using Servlet 3.X, but this example focuses on the usage of javax.servlet.ServletContainerInitializer. With a ServletContainerInitializer one can programatically register Servlets, Filters and Listeners thus giving you type safety that can be useful during refactoring. As we are in Spring Land, rather than utilize ServletContainerInitializer, an extension of Spring's WebApplicationInitializer is used as shown below:
@Order(Ordered.HIGHEST_PRECEDENCE) 
public class NotesApplicationInitializer implements WebApplicationInitializer {

  private static final String SPRING_SECURITY_FILTER_NAME = "springSecurityFilterChain";
  
  @Override public void onStartup(ServletContext ctx) throws ServletException {
    // Listeners
    ctx.addListener(ContextLoaderListener.class);

    ctx.setInitParameter(ContextLoader.CONTEXT_CLASS_PARAM,
      AnnotationConfigWebApplicationContext.class.getName());
    ctx.setInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, SpringConfig.class.getName());

    // Register Spring Security Filter chain
    ctx.addFilter(SPRING_SECURITY_FILTER_NAME, DelegatingFilterProxy.class)
        .addMappingForUrlPatterns(
          EnumSet.<DispatcherType> of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, "/*");

    // Register Jersey 2.0 servlet
    ServletRegistration.Dynamic servletRegistration = ctx.addServlet("Notes",
      ServletContainer.class.getName());
    
    servletRegistration.addMapping("/*");
    servletRegistration.setLoadOnStartup(1);
    servletRegistration.setInitParameter("javax.ws.rs.Application", NotesApplication.class.getName());
  }
}
The WebApplicationInitializer does the following:
  1. Sets up a Spring ContextLoaderListener
  2. Tells the Context loader the location of the Spring Java Config that is to be used for Services
  3. Registers the Spring Security Filter chain
  4. Registers the Jersey Servlet Container by providing it the NotesApplication as the class to use for Resource and Provider management
Of note is the fact that the NotesApplicationInitializer has been set to highest precedence as that will ensure that it is executed before any other WebApplicationInitializer provided by accompanying jars. If for example, the SpringWebApplicationInitializer from jerseyspring3.jar gets loaded, then it attempts to find a spring  applicationContext.xml and will fail as our example does not use spring xml style bean definitions.

Spring Dependency Injection

In conjunction with the registration of the Spring Java Config in the WebApplicationInitializer shown above, the Java Config enables the Services and Spring Security filter chain via the following:
@Configuration
@EnableNotesService
@EnableNotesSecurity
public class SpringConfig {
}
I have not delved into the details of the @Enable annotations but they are in line with Spring's style to import dependencies. For the scope of this example, you can assume that @EnableNotesSecurity results in the importing of the notes Java services and @EnableNotesSecurity imports the security configuration.

Jersey Should Manage the Resource Classes

I wanted all my JAX-RS resources managed by Jersey and not Spring. I did not want to annotate my JAX-RS classes with @Component + Classpath-scanning and/or have them defined in a Java Config which would then result in Spring managing them. All the Resources and relevant providers are registered with Jersey by extending the javax.ws.rs.core.Application class as shown below:
public class NotesApplication extends Application {
  
  @Override
  public Set<Class<?>> getClasses() {
    return ImmutableSet.<Class<?>>of(NotesResource.class, 
      HealthResource.class,
      NoteNotFoundExceptionMapper.class, LoggingFilter.class, AccessDeniedExeptionMapper.class);
  }
}
Note that the 'service' java classes, such as NotesService.java (managed by Spring) are made available via dependency injection into the NotesResource via Jersey's Spring-HK2 bridge.

Security Should be enabled Via Spring Security

For this example,I have very simple Spring Security filter chain set up that ensures a POST to create a Note can only be done by a user who has the ROLE 'notesuser' provided in a HTTP header while making the call. It is trivial, but hey, this is an example :-) The goal was to demonstrate the use of a @PreAuthorize annotation on the create a Note and how it can be made to work when Jersey manages the resource and not spring. The Notes resource create method looks like the following:
@Path("/notes")
@Produces({ MediaType.APPLICATION_XML })
@Consumes({ MediaType.APPLICATION_XML })
public class NotesResource {
  // Jersey object injection
  private final UriInfo uriInfo;
  
  // Spring object injection
  private final NotesService notesService;
  
  // Note that UriInfo is obtained from Jersey but the NotesService is a spring dependency
  @Inject
  public NotesResource(@Context UriInfo uriInfo, NotesService notesService) {
    this.uriInfo = uriInfo;
    this.notesService = notesService;
  }
  
  @POST
  @Loggable
  @PreAuthorize("hasRole('notesuser')") // Pre Authorize role that allows only notesuser to create the note
  public Response create(Note note) {
    LOG.debug("Creating a note:" + note);
    NoteResult result = createNote(note);
    
    return Response.created(result.getLocation()).entity(result).build();
  }
  .....
}
As the NotesResource is managed by Jersey, the only way I could figure to enforce the @PreAuthorize annotation was via AspectJ weaving of the resource class. The same is accomplished by adding the aspectJ maven plugin which is used during the build process to enhance the JAX-RS Resource classes:
 <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.6</version>
    <configuration>
    ...
    <sources>
      <source>
        <basedir>${basedir}/src/main/java/com/welflex/notes/rest/resource</basedir>
        <includes>
           <include>**/*Resource.java</include>
        </includes>
      </source>
    </sources>
    ....
    <aspectLibraries>
      <aspectLibrary>
        <groupId>org.springframework.security</groupId>
          <artifactId>spring-security-aspects</artifactId>
      </aspectLibrary>
   </aspectLibraries>
   </configuration>
  <executions>
    <execution>
      <goals>       
        <goal>compile</goal>
      </goals>
    </execution>
  <executions>
  <dependencies>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.7.4</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjtools</artifactId>
      <version>1.7.4</version>
    </dependency>
  </dependencies>
<plugin>

Running the Example


The example is provided as a maven project. I am utilizing the concept of a maven 'bom' for the first time for managing the related jersey dependencies. Execute a 'mvn install' to see the example in action or execute a 'mvn jetty:run' on the web project to start the service. The example itself can be downloaded from here.

Client

The client will send the 'role' as a header parameter as shown below for creating a note:
  public NoteResult create(Note note, String role) {
    return webServiceClient.target(UriBuilder.fromUri(baseUri).path("/notes").build())
        .request(MediaType.APPLICATION_XML)
        .header("role", role)
        .post(Entity.entity(note, MediaType.APPLICATION_XML_TYPE), NoteResult.class);
  }

Integration Test

Integration test starts up a web container and executes the client operations. The following shows the spring-security role being honored:
  @Test(expected = ForbiddenException.class)
  public void roleAbsent() {
    Note note = new Note();
    note.setUserId("sacharya");
    note.setContent("Something to say");
    client.create(note, "Fake Role"); // Should throw a JAX-RS ForbiddenException
    fail("Should not have created a note as the role provided is not supported");
  }
Running the equivalent of the above with the role of 'notesuser' will allow the creation of the note. The life-cycle integration test in the attached example demonstrates the same succeeding. The example should be devoid of web.xml. If anyone coming across this blog has integrated Jersey/Spring/Spring-security in a different way, please share, would love to hear. That's all folks! Enjoy!

Thursday, February 21, 2013

Netflix Hystrix - It's Hysterical...err Hystrixcal?

Introduction:


In any distributed architecture, failure of services are inevitable. These could be due to network related issues, bugs in code, unexpected loads, failure of dependencies, what have you? When failure of a single service in a network starts affecting the availability of other services, then that service could become a single point of failure for the entire dependency graph associated with that service.  Hystrix is a library developed and subsequently outsourced by Netflix that is geared toward making a distributed architecture resilient while addressing some of the above concerns. 
Hystrix function as described by the creators "Hystrix is a library designed to control the interactions between these distributed services providing greater latency and fault tolerance. Hystrix does this by isolating points of access between the services, stopping cascading failures across them, and providing fallback options, all of which improve the system's overall resiliency."
I could talk more about Hystrix but reading their excellent WIKI is definitely the more DRY route. My goal in this BLOG is simply to have fun while providing you an example of Hystrix usage to play around with :-)


Fictional Case Study


Developers of company Acme had developed a GreetingService and had also provided a client for consuming the service. The client developed looked like:

public class GreetingJerseyClient implements GreetingClient {
  private final String baseUri;
  private final Client client;

  public GreetingJerseyClient(String baseUri) {
    this.baseUri = baseUri;
    client = ClientFactory.newClient();
  }

  /**
   * @return a Greeting in the specific language, for example "Hello" or "Hola"
   */
  @Override
  public String getGreeting(String languageCode) {
    ....
  }

  /**
   * @return a Greeting for the individual in the specfied language, for example "Hello Sanjay", "Hola Sanjay".
   * @throws InvalidNameException if null was provided as the language code.
   */
  @Override
  public String greet(String name, String languageCode) throws InvalidNameException {
    ...
  }
}

This GreetingService was a core service that was utilized by all their web applications and in some cases consumed by other services as well. The GreetingService itself was a beast that which on load tests showed requests could take upto 1 second to respond at times (probably full GC's kicking in). Accordingly the developers configured the read-timeout for the connections to be 1 second so that any request taking longer than 1 second would time out. Upon deployment all looked good for sometime but then they started encountering problems where the service started degrading, i.e., it was not the occasional request that took a second to respond but all requests in a particular time window that exhibited latency or failure. This resulted in the application container threads all blocking leading to a denial of service.

Bosses were mad, stake holders took to narcotics, stock values plummeted.


A postmortem of the incident was held. Cause of the problem was not singular but multiple things that happened, a perfect storm if you may, a bug was introduced that increased response times, their load balancer ran into unexpected issues, a data center technician spilled his drink on some servers of the cluster..phew. Results of the postmortem were:
  • To provide a default greeting for the getGreeting() call in the event the Greeting service were experiencing degradation.
  • Deploy the Greeting Service in a cloud provider and pay them only for usage ($$$$$) in the event of failure of their existent measly data center. Only the greet(name, languageCode) call would access that.
  • Provide a transparent means of switching to the cloud service in the event of degradation of the in house Greeting Service and switch back when the in home Greeting Service is responsive again.
  • Be able to track and monitor service degradation so the network operations folk could react quickly.
Tasked with the above, the development team decided that Hystrix would help address the above concern really well. As changing the API was not an option, the developers decided to provide a HystrixGreetingClient that would wrap the service calls with a HystrixCommand.

The following is the enhancement they made to provide a default greeting for the greet() call in the event of service degradation:
public class HystrixGreetingClient implements GreetingClient {
  private final GreetingClient client;

  public HystrixGreetingClient(GreetingClient client) {
  }

  @Override
  public String greet() {
    return new GetGreetingHystrixCommand().execute();
  }

 // Default Greeting Agreed Upon
  public static final String DEFAULT_GREETING = "Hello!";

  class GetGreetingHystrixCommand extends HystrixCommand<String> {

    public GetGreetingHystrixCommand() {
      super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GreetingClient"));
    }

    @Override
    protected String run() throws Exception {
    // Attempt to obtain greeting from the service
      return client.getGreeting();
    }

    @Override
    protected String getFallback() {
      // Fallback provides a default greeting
      return DEFAULT_GREETING;
    }
  }
}

The GetGreetingCommand is a HystrixCommand  which will invoke the fallback greeting if Hystrix detected service degration.  For the greet(name, languageCode) operation, as the goal was to invoke the cloud deployed Greeting Service in the event of service degradation of their in-house version, the developers provided a fallback client that could be invoked as shown below:
public class HystrixGreetingClient implements GreetingClient {
  private final GreetingClient primary;
  private final GreetingClient fallback;

  /**
   * @param primary The primary client which communicates with the in-house data center
   * @param fallback The fallback client which communicates with the $$$$$ cloud provider
   */
  public HystrixGreetingClient(GreetingClient primary, GreetingClient fallback) {
  }

  @Override
  public String greet(String languageCode) {
    ....
  }
 
  @Override
  public String greet(String name, String languageCode) {
    try {
      return new GreetHystrixCommand(name, languageCode).execute();
    }
    catch (HystrixBadRequestException e) {
      if (e.getCause() instanceof InvalidNameException) {
        throw InvalidNameException.class.cast(e.getCause());
      }
      throw e;
    }
  }

  class GreetHystrixCommand extends HystrixCommand<String> {
    private final String name;
    private final String languageCode;

    public GreetHystrixCommand(String name, String languageCode) {
      super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GreetingClient"))
        .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionIsolationThreadTimeoutInMilliseconds(500)));
      this.name = name;
      this.languageCode = languageCode;
    }

    @Override
    protected String run() throws Exception {
      try {
        // Attempt to invoke the primary service
        return primary.greet(name, languageCode);
      }
      catch (InvalidNameException e) {
        // Throw a HystrixBadRequestException as this is not a network or service issue
        throw new HystrixBadRequestException("Invalid Name", e);
      }
    }

    @Override
    protected String getFallback() {
      // If primary failed or was short circuited, invoke the secondary
      return new GreetHystrixFallbackCommand().execute();
    }

    /**
     * Fallback command designed to talk to the cloud provider service
     */
    class GreetHystrixFallbackCommand extends HystrixCommand<String> {

      public GreetHystrixFallbackCommand() {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GreetingClient"));
      }
      
      /**
       * There is no fallback for this call. Fail fast.
       */
      @Override
      protected String run() throws Exception {
        try {
          return fallback.greet(name, languageCode);
        }
        catch (InvalidNameException e) {
          // This is a user or programming error, not a network or service problem
          // so throw a HystrixBadRequestException
          throw new HystrixBadRequestException("Invalid Name", e);
        }
        catch (RuntimeException e) {
        // Demonstrating that all other exceptions are thrown
          throw e;
        }
      }
    }
  }
}
The call to greet(name, language) if experiencing service degradation will result in the fallback cloud provider equivalent being invoked. The cloud provider call is also wrapped in a HystrixCommand so that it also is subjected to SLA constraints. If the fall back provider cannot fulfill the request, then the there is no fallback for it and failure will occur fast. One thing to note is that if a name provided was invalid, for example null, the service would throw an InvalidNameException. The same should not contribute toward calling a fallback or short circuiting metrics. For this reason, a HystrixBadRequestException is thrown which will not result in the fallback being invoked as well as not contributing toward the failure metrics.

Results of their efforts


Once the new client was released every consumer adopted the same were able to have their applications be resilient to the failure of the dreaded Greeting Service :-)

Logs looked like:
INFO - com.welflex.example.client.HystrixGreetingClient$GetGreetingHystrixCommand.getFallback(62) | Fallback default greeting is being provided
INFO - com.welflex.example.client.HystrixGreetingClient$GetGreetingHystrixCommand.getFallback(62) | Fallback default greeting is being provided
INFO - com.welflex.example.client.HystrixGreetingClient$GetGreetingHystrixCommand.getFallback(62) | Fallback default greeting is being provided
.. More of the above
INFO - com.welflex.example.client.HystrixGreetingClient$GetGreetingHystrixCommand.getFallback(62) | Fallback default greeting is being provided
INFO - com.welflex.example.client.HystrixGreetingClient$GetGreetingHystrixCommand.run(56) | Obtained Greeting from Service
INFO - com.welflex.example.client.HystrixGreetingClient$GetGreetingHystrixCommand.getFallback(62) | Fallback default greeting is being provided
INFO - com.welflex.example.client.HystrixGreetingClient$GetGreetingHystrixCommand.getFallback(62) | Fallback default greeting is being provided
INFO - com.welflex.example.client.HystrixGreetingClient$GetGreetingHystrixCommand.getFallback(62) | Fallback default greeting is being provided
INFO - com.welflex.example.client.HystrixGreetingClient$GetGreetingHystrixCommand.run(56) | Obtained Greeting from Service
INFO - com.welflex.example.client.HystrixGreetingClient$GetGreetingHystrixCommand.run(56) | Obtained Greeting from Service
....
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand.run(82) | Obtaining greeting from Primary service
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand.run(82) | Obtaining greeting from Primary service
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand.run(82) | Obtaining greeting from Primary service
.... More of the above
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand$GreetHystrixFallbackCommand.run(107) | Obtaining greeting from Secondary Service
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand$GreetHystrixFallbackCommand.run(107) | Obtaining greeting from Secondary Service
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand.run(84) | Obtained greeting from Primary service
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand$GreetHystrixFallbackCommand.run(109) | Obtained greeting from Secondary Service
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand$GreetHystrixFallbackCommand.run(109) | Obtained greeting from Secondary Service
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand.run(82) | Obtaining greeting from Primary service^M
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand$GreetHystrixFallbackCommand.run(109) | Obtained greeting from Secondary Service
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand$GreetHystrixFallbackCommand.run(109) | Obtained greeting from Secondary Service

ERROR - com.netflix.hystrix.HystrixCommand.executeCommand(812) | Error executing HystrixCommand
java.lang.RuntimeException: Unable to greet:500
        at com.welflex.example.client.GreetingJerseyClient.greet(GreetingJerseyClient.java:57)
        at com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand.run(HystrixGreetingClient.java:83)
        at com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand.run(HystrixGreetingClient.java:67)
        at com.netflix.hystrix.HystrixCommand.executeCommand(HystrixCommand.java:764)
        at com.netflix.hystrix.HystrixCommand.access$1400(HystrixCommand.java:81)
        at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:706)
        at com.netflix.hystrix.strategy.concurrency.HystrixContextCallable.call(HystrixContextCallable.java:45)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
        at java.util.concurrent.FutureTask.run(FutureTask.java:166)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
        at java.lang.Thread.run(Thread.java:722)
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand$GreetHystrixFallbackCommand.run(107) | Obtaining greeting from Secondary Service
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand$GreetHystrixFallbackCommand.run(107) | Obtaining greeting from Secondary Service
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand$GreetHystrixFallbackCommand.run(107) | Obtaining greeting from Secondary Service
..More of the above
INFO - com.welflex.example.client.HystrixGreetingClient$GreetHystrixCommand.run(84) | Obtained greeting from Primary service

The getGreeting() command falls back to the fall back greeting agreed upon when the primary service shows degradation. On the call to greet(), the cloud service is invoked when the primary service shows degradation and then reverts back to the primary service when it health improves.

Monitoring

For monitoring the company set up all web applications using the Greeting service to have the Hystrix Event Stream Servlet so that statistics and metrics could be obtained from each deployment of the application on how the command's were performing. They aggregated these metrics using Turbine and finally had a pretty Hystrix Dashboard at which the Network Operations folk spent hours admiring the animated graph.

An Example

As always, herewith is a maven example of the above GreetingWebService and Hystrix for download. It is a simple Jersey 2.0 application with a test that demonstrates fallback and fail fast. It does not exercise all the configuration that Hystrix provides, that's for you to have fun with. You can tweak the Rest resources to include further delays, tweak the Hystrix commands with different SLAs. Simply run the following command and witness a simple test demonstrating the functionality:
>mvn install 

Conclusion

Why would you want to adopt Hystrix? I ask why not? Do you believe your network to be infallible, your software bug free, your availability ready to be questioned? Yes there is a cost, a cost toward the extra code that one needs to wrap around network calls with Hystrix Commands but compared to the cost of your leaders taking to narcotics or more appropriately your site being non-responsive, it might be a small price to pay. Adoption is not easy IMHO. Care must be taken to understand the exact SLA parameters to configure a Hystrix Command as getting it wrong might hurt rather than alleviate a problem. For this reason a gradual adoption is the preferred route.

I have taken the liberty of a creating a HystrixClient that is only partially configurable. I do not believe this to be the right approach and feel that separate client that allows full configuration of the HystrixCommand by a service consumer is the correct approach.

As always, I might have got things wrong in which case, I would love to learn the error of my ways :-) In the end, remember its Hystrix not Hysterics as I often confused it to be. Enjoy!

All images linked with in this BLOG are NOT my own. I am simply referring to them for demonstration purposes. If owners have concerns, I will gladly remove them. 

Friday, October 5, 2012

JAX-RS 2.0 - Jersey 2.0 Preview - An Example

Introduction


JAX-RS 2.0 spec has reached public review status. Implementors of the API have been hard at work. One among them is Jersey. I have a vested interest in Jersey as I use it at work and therefore wanted to keep up to date on the changes in JAX-RS 2.0 and Jersey 2.0 and thus this BLOG. Jersey 2.0 is not backward compatible with Jersey 1.0 per their site and is instead a major re-write to correctly handle the new specification. When working with a previous REST provider, I found that when they created a 2.0 version of their product, they did not care to be backward "sensitive". I did not say compatible but sensitive :-). Backward sensitivity IMHO is a way to provide new functionality and refactor of an existing API in a way that provides a gradual migration path of consumers of the API and not requiring an all or nothing upgrade.

When backward incompatible changes are made to API's without changing package names, then binary incompatibilities surface when both versions of the API are required within the same class loader system. These incompatibilities can be easily absorbed by small scale adopters who can easily ensure all dependencies are upgraded simultaneously and only the latest version of the API is present. This requirement of simultaneous upgrade is however an arduous challenge for larger and more complex deployments whose development and deployment windows are very separated. Sure that one can look at OSGI or write their own class loaders if they should choose to but I am too dull headed for such extravagance.

In short what I want to ensure with Jersey 2.0 is the following use case:

There are three Services (REST services running within a Servlet container) written in Jersey 1.X. Each of the Services have multiple consumers but we are currently interested in the following where Service A calls Service B and Service C using their respective clients. A Service client is nothing more than a friendly API/library wrapping Jersey Client calls that can be distributed to different consumers of the service.

For the sake of discussion, let us consider that Service B is upgraded to Jersey 2.0 but Service C is not. As part of the upgrade, the maintainers of Service B provide a client to consume the service which is written in Jersey 2.0 as well. Will the Service clients of Service B and Service C be able to co-exist and function in Service A? Does the fact that Service A has not been upgraded to Jersey 2.0 cause problems for the clients of Service B and Service C to function in the container? In addition, let us say that Service A also gets upgraded to Jersey 2.0, then will the Service client for C that is using Jersey 1.X be able to function within Service A? Clearly, one option is to provide multiple clients of the service, one for Jersey 1.X and a second for Jersey 2.0. The same is achievable but does involve additional work of development and maintainence. So the question remains, whether the compatibility discussed will hold true or not? The BLOG and example provided herewith will evaluate the same.

JAX-RS 2.0 and what to expect


Client API

With earlier versions of the JAX-RS, the specification only accounted for a Server Side API for RESTful calls. The same led to different implementors of the Server API such as Jersey and RESTEasy independently developing a client side API (See my BLOG post on REST Clients). JAX-RS 2.0 will contain a Client API to support the server side specification. The JAX-RS 2.0 Client API is a major incorporation of the Jersey 1.X client API.

Asynchronous Support on Client and Server side

Client Side asynchronous support will allow a request to be dispatched in a non-blocking manner with results made available asynchronously. On the server side, long running requests that are I/O bound can be dispatched away, thus releasing the Application server thread to service other requests.

Validation

The lack of validation of data received from Headers, Body etc is something that I whined about in a previous BLOG. JAX-RS 2.0 will provide support for validation of request data.

Filters and Handlers

JAX-RS 2.0 specification accounts for Client and Server Filters. As in the case of the Client API, where implementer's provided their own versions of these filters for the Client and Server. The JAX-RS 2.0 specification absorbs the same into its API. For a more detailed understanding of changes in JAX-RS 2.0, a read of Arun Gupta's blog is recommended. This BLOG will be leaning on the Notes Web Service example that I had previously utilized where Notes can be created, updated and deleted via a client.

Jersey 2.0 and HK2



In previous version of my examples, I have used either Spring or Guice for injecting dependencies into Resources. At the time of this BLOG, I could not find the Spring or Guice support and have therefore used what Jersey 2.0 natively uses for its dependency injection and service location needs, i.e., HK2. HK2 is based on JSR 330 standard annotations. In the following example, a service binder utilizes HK2 and binds interface contracts to implementors as shown below. This binder which is defined in a "service" maven module is then used in the web maven module to enable injection of the NotesService:
public class ServiceBinder implements Binder {
  @Override
  public void bind(DynamicConfiguration config) {
    config.bind(BuilderHelper.link(NotesServiceImpl.class).to(NotesService.class).build());
  }
}

/**
 * Application class
 */
public class NotesApplication extends ResourceConfig {
  
  public NotesApplication() {
    super(NotesResource.class..);
    
    // Register different Binders
    addBinders(new ServiceBinder(), new JacksonBinder());
  }
}

/**
 * A Resource class injected with the Notes Service
 */
@Path("/notes")
public class NotesResource {
  @javax.inject.Inject
  private NotesService service; // Notes Service injection
  ...
}



Client



As previously mentioned, JAX-RS 2.0 supports a client API and Jersey provides an implementation of the same. At the time of writing this BLOG, I could not find hookins for Apache HTTP Client and therefore will be using the standard HttpClient for HTTP calls.

Client Creation

The process of configuring a client and creating the same is very similar to Jersey 1.X.
// Configuration for the Client
ClientConfig config = new ClientConfig();

// Add Providers
config.register(new JacksonJaxbJsonProvider());
config.register(new LoggingFilter());

// Instantiate the Client using the Client Factory
Client  webServiceClient = ClientFactory.newClient(config);

Executing REST Request

With Jersey 1.X, a typical POST to create a Note would have looked like-
NoteResult result = client.resource(UriBuilder.fromUri(baseUri).path("/notes").build())
        .accept(MediaType.APPLICATION_XML).entity(note,  MediaType.APPLICATION_XML).post(NoteResult.class);
The same request using the JAX-RS 2.0 client looks like:
NotesResult result = client.target(UriBuilder.fromUri(baseUri).path("/notes").build())
        .request(MediaType.APPLICATION_XML) // Request instead of accept
        .post(Entity.entity(note, MediaType.APPLICATION_XML_TYPE), NoteResult.class);
The primary difference seems to be where the Resource is identified as "target" rather than "resource". There is no "accepts" method but is replaced with "request" for media type. The Entity is built from a Entity Builder method which returns an instance of java.ws.rs.client.Entity. Overall the API seems quite flowing and does not seem to conflict with the Jersey 1.X client API.

Server Changes



One of the changes I wanted to work with is the Validation but I do not believe the same has been fully implemented as yet. Apart from the Async support mentioned earlier, one area of interest for me  is the Filters and Name Binding feature of JAX-RS.

Name binding and Filter

For those who have used Jersey's ContainerRequest and ContainerResponse Filters, the JAX-RS 2.0 equivalents should be quite familiar. An example of a simple ContainerRequestFilter that logs header attributes is shown below:
@Provider
@Loggable
public class LoggingFilter implements ContainerRequestFilter {
  private static final Logger LOG = Logger.getLogger(LoggingFilter.class);

  @Override
  public void filter(ContainerRequestContext requestContext) throws IOException {
    MultivaluedMap<String, String> map = requestContext.getHeaders();
    LOG.info(map);
  }
}
The annotation @Loggable shown above is a custom one that I created to demonstrate the Name Binding features of JAX-RS 2.0 where a Filter or Handler can be associated with a Resource class, method or an invocation on the client. In short this means one has finer grained control over which methods/resources the filter or handler is made available to. The annotation @Loggable is shown below:
@NameBinding
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Loggable {
}
With the above, if we wish to apply the LoggingFilter only to a specific method of a Resource, one simply has to annotate the method with @Loggable:
@Path("/notes")
public class NotesResource {
  @POST
  @Loggable // Will be logged
  public Response create(Note note) {
   ...
  }

  @GET // No annotation @Loggable added
  public Note get(Long noteId) {
    ...
  }
If one wanted to apply the filter on a global level, then Global Binding is used where annotating the filter or handler with @GlobalBinding makes it applicable to all resources of an application.

Asynchronous HTTP



This is where its time for fun, at least for me :-). With Servlet 3.0, one can utilize NIO features and thus free service threads from blocking IO Tasks. Jersey supports suspending the incoming connection and resuming it at a later time. On the client side, the ability to dispatch requests asynchronously is a convenience.

Fictitious Requirements of the Notes Service
  • Notes Client should be able to submit a large number of Notes for creation. The Service should not block the JVM Server thread while it waits to submit but instead should background the same. Client should be notified of a Task ID asynchronously when it becomes available.
  • Notes Client should be able to obtain the status of Note creation asynchronously. The Service should not block the JVM Server thread while waiting for task completion. Upon obtaining the task result, the client should be notified of the same.
  • Notes Client should be able to register a listener that is notified of Note Creation events from the server

Posting a list of Notes for Creation

A list of Notes are submitted to the resource to asynchronously create them. The call itself is asynchronous with a Task ID being delivered to the InvocationCallback when available. One could accomplish the same via an Executor Service etc but this feature simplifies the operation.
 @Override
  public void submitAsync(List<Note> notes, InvocationCallback<Long> taskCallback) {
    webServiceClient.target(UriBuilder.fromUri(baseUri).path("/notes/async").build())
        .request(MediaType.TEXT_PLAIN).async()
        .post(Entity.entity(new NotesContainer(notes), MediaType.APPLICATION_XML), taskCallback);
  }
On the server side, the AsyncNotesResource handles the POST as shown below:
  @POST 
  public void createAsync(@Suspended final AsyncResponse response, final NotesContainer notes) {

    QUEUE_EXECUTOR.submit(new Runnable() {
      public void run() {
        try {
          // This task of submitting notes can take some time
          Long taskId = notesService.asyncCreate(notes.getNotes());
          LOG.info("Submitted Notes for Creation successfully, resuming client connection with TaskId:"
            + taskId);
          // Resume the connection
          response.resume(String.valueOf(taskId));
        }
        catch (InterruptedException e) {
          LOG.error("Waiting to submit notes creation interrupted", e);
          response.resume(e);
        }
      }
    });
  }
One can also obtain a java.util.concurrent.Future using the API if desired.

Obtaining Result of Task Processing Asynchronously

The InvocationCallback of the POST request will be provided a Task ID. Using this Task ID, an asynchronous GET request can be dispatched to obtain the results of the task. As the task might not be completed at the time of the request, the client issues the request in an asynchronous manner and is provided the results of the task when available through the InvocationCallback.
 public void getAsyncNoteCreationResult(Long taskId,
    InvocationCallback<NotesResultContainer> resultCallback) {
    webServiceClient.target(UriBuilder.fromUri(baseUri).path("/notes/async/" + taskId).build())
        .request(MediaType.APPLICATION_XML).async().get(resultCallback);
  }
The Server resource as shown below will not block the JVM server thread but will background the process of obtaining the Task results and the backgrounded process will resume the client connection with the task result when available. Also a time out is set on the request, if the server is not able to respond within the alloted period of time, then a 503 response code is sent back with a "Retry-After" header set to 60 sec.
@GET 
@Path("{taskId}") 
public void getTaskResult(@Suspended final AsyncResponse ar, @PathParam("taskId") final Long taskId) {

    LOG.info("Received Request to Obtain Task Result for Task:" + taskId);
    ar.setTimeout(10000L, TimeUnit.MILLISECONDS);
    ar.setTimeoutHandler(new TimeoutHandler() {
      
      @Override 
      public void handleTimeout(AsyncResponse asyncResponse) {   
        // Respond with 503 with Retry-After header set to 60 seconds
        // when the service could become available.     
        asyncResponse.cancel(60);
      }
    });
    QUEUE_EXECUTOR.submit(new Runnable() {

      @Override public void run() {
        try {
          // This call will block until results are available.
          List<Long> noteIds = notesService.getAysyncNoteCreationResult(taskId);

          NotesResultContainer resultContainer = new NotesResultContainer();
          ....
          LOG.info("Received Note Creation Result, resuming client connection");
          ar.resume(resultContainer);
        }
        catch (InterruptedException ex) {
          LOG.info("Interrupted Cancelling Request", ex);
          ar.cancel();
        }
      }
    });
  }
Server Sent Events

A Feature of Jersey, though I believe not to be really a feature of JAX-RS is the ability to listen to Server sent events. The same is accomplished by the client obtaining an instance of org.glassfish.jersey.media.sse.EventChannel from the Service which then receives events published by the server. The channel is kept alive until disconnected. The Client code to obtain the EventProcessor is shown below:
   WebTarget target = webServiceClient.target(UriBuilder.fromUri(baseUri)
            .path("/notes/async/serverSent").build());
   
   // Register a reader for the Event Processor
   target.configuration().register(EventProcessorReader.class);

   // Obtain an event Processor from the Service
   EventProcessor processor = target.request().get(EventProcessor.class);

   processor.process(new EventListener() {
     public void onEvent(InboundEvent inboundEvent) {
       Note inboundNote = inboundEvent.getData(Note.class);

       for (NoteListener listener : serverEventListeners) {
         listener.notify(inboundNote);
       }
      }
   });
On the service end, a thread is started that will dispatch Note creation events to the channel.
  @Produces(EventChannel.SERVER_SENT_EVENTS)
  @Path("serverSent")
  @GET
  public EventChannel serverEvents() {
    final EventChannel channel = new EventChannel();
    new EventChannelThread(channel, notesService).start();

    return seq;
  }
  
  public class EventChannelThread extends Thread {
     ...
     public void run() {
        // While loop jargon ommited for lack of real estate
        channel.write(new OutboundEvent.Builder().name("domain-progress")
                  .mediaType(MediaType.APPLICATION_XML_TYPE).data(Note.class, noteToDispatch).build());

     }
  }

Example Project



An example project can be downloaded from HERE.

As mentioned previously, the example uses the concept of CRUD as related to a Note. A multi-module maven project is provided herewith that demonstrates the Notes application as written with Jersey 1.X and Jersey 2.X. The Jersey 2.X version has some additional features such as ASYNC, Name Binding and Filter that are demonstrated. In addition, there is a compatibility-test project that evaluates the compatibility concerns that I had mentioned at the beginning of my BLOG. Please note that 2.0-SNAPSHOT of Jersey is being used. A brief description of the maven modules you will find in the example:
  • Notes-common: Contains Data transfer objects and common code used by the Jersey 1.X and Jersey 2.X applications
  • Notes-service: Contains Java classes that are functional services for managing the Notes.
  • Notes-jersey-1.X: Contains Client, Webapp and Integration test modules that demonstrate Jersey 1.X.
  • Notes-jersey-2.X: Contains Client, Webapp and Integration test modules that demonstrate Jersey 2.X features
In order to exercise the compatibility tests two test webapps are created, one using Jersey 1.X and the second using Jersey 2.0. The Notes-client's of Jersey 1.X and Jersey 2.X are exercised within both mentioned test webapps to check for compatibility problems.
The compatibility integration test invokes a resource "/compatibility" on a test web app that is developed using jersey. When the resource is invoked, both the Notes Jersey Clients are exercised to perform CRUD operations using RESTful calls to a Notes Web service Webapp. There are two versions of the test-jersey-webapp, one written in Jersey-1.X and the second in Jersey 2.0.

You can run the full suite of Integration tests merely by executing a "mvn install" from the root level of the project. If you wish to run individual tests, you can also drill down into the Notes-jersey-1.X and Notes-jersey-2.0 modules and execute the same command.

Compatibility Test Results



So can Jersey 2.0 and Jersey 1.X co-exist in the same class loader system? The answer seems to be "Yes" but with some effort. jersey-client-1.X.jar depends on jersey-core-1.X.jar. The latter bundles JAX-RS 1.X (javax.ws.rs.*) packages in it. jersey-client-2.X depends on javax.ws.rs.* packages that are part of JAX-RS 2.0 and therefore requires that JAX-RS API 2.0 is available.

If an application that is running Jersey 1.X wishes to utilize a Jersey 1.X and Jersey 2.X client to call other web services, it is best to upgrade the service in question. The reasoning for the same is best established by the following use case:

Consider the javax.ws.rs.core.UriBuilder from jersey-core-1.X.jar:

public abstract class UriBuilder {
 public abstract UriBuilder uri(URI uri) throws IllegalArgumentException;
}

An implementation by Jersey of the same is com.sun.jersey.api.uri.UriBuilderImpl which is as included part of jersey-core-1.X.jar.

Now the JAX-RS 2.0 version of UriBuilder has additional abstract methods one of which is shown below:

public abstract class UriBuilder {
 public abstract UriBuilder uri(URI uri) throws IllegalArgumentException;

 // New Method Added as part of JAX-RS 2.0
 public abstract UriBuilder uri(String uri) throws IllegalArgumentException;
}
An implementation of the same is org.glassfish.jersey.uri.internal.JerseyUriBuilder.

When a UriBuilder is loaded, it is important that the implementation that defines all the abstract methods of UriBuilder is loaded. If not, one would get see an exception like:
java.lang.AbstractMethodError: javax.ws.rs.core.UriBuilder.uri(Ljava/lang/String;)Ljavax/ws/rs/core/UriBuilder;
The same problem could exist for other JAX-RS 2.0 classes for which Jersey 1.X does not have concrete implementations. As the JAX-RS 2.0 API has to exist in the classpath, one needs to ensure that JAX-RS 1.X API classes are not included as well.

The jersey-core-1.X.jar includes javax.ws.rs.* packages from jax-rs 1.0 API. Re-bundling the jersey-core-1.X jar without these packages and including jersey 2.0 and jax-rs 2.0 API classes will always ensure that the API's have the latest and complete implementations.

In addition, the implementation of javax.ws.rs.ext.RuntimeDelegate is responsible for instantiating a version of UriBuilder. If the Jersey 1.X version of RuntimeDelegate is chosen at runtime, then it will attempt to load com.sun.jersey.apu.uri.UriBuilderImpl which will not have the abstract methods defined as part or JAX-RS 2.0's UriBuilder. For this reason, the org.glassfish.jersey.server.internal.RuntimeDelegateImpl must be chosen so that the JAX-RS 2.0 implementation of UriBuilder, i.e., org.glassfish.jersey.uri.internal.JerseyUriBuilder is always created. The same is accomplished by defining in META-INF/services a file java.ws.rs.ext.RuntimeDelegate that contains a single line, org.glassfish.jersey.uri.internal.JerseyUriBuilder.

The compatiblity-test web applications exercise the above mentioned route in addition to including a custom version of jersey-core-1.X.jar which is nothing more than a version that is stripped of javax.ws.rs.* java packages.

With the above changes, I found that my compatibility tests passed. Of course I am working under the assumption that the JAX-RS 2.0 API classes did not change method signatures in an incompatible way :-).

Conclusion



Overall I feel that the JAX-RS 2.0 implementation with the unification of the Client API is definitely a move in the right direction. Jersey 2.0-SNAPSHOT also seemed to work well from an API and implementation perspective. It is heartening to know that co-existence can be achieved using Jersey 1.X and Jersey 2.0.The Jersey folks have also stated that they will be providing a migration path for Jersey 1.X adopters which is really professional of them. Some work has already been done to that effect on their site.

On the Async features of JAX-RS 2.0, I see potential value on the server side with NIO. On the client side, I find the API to be a convenience. It is arguable that in a clustered stateless environment client side ASYNC might provide limited value.

I have not covered all JAX-RS 2.0 features but only those that have interested me. There are many changes and I might investigate the remaining come another day. Finally, if I have got some areas wrong or my understanding if flawed, I look forward to your corrections on the same.

UPDATE - An example of the released jersey 2/jax-rs 2.0 that uses Spring DI is now available

Sunday, July 8, 2012

Spring MVC 3.1 Presentation and Tutorial

A few months ago, I had a chance to present on Spring MVC. It was a simple 45 minute presentation but I had a blast doing it.  A friend of mine had asked whether I could share the same and this BLOG post is exactly that.

My audience for the presentation had no experience with the framework and therefore my presentation was geared toward introducing the same to them. A Spring MVC presentation without supporting code is like having a cake without the icing. So included herewith is also the code I developed for the same.

The model object of interest for the demo code is an Application in some company's domain. It could be any application such as a web app, web service or whatever. The Application has a single property, its name. The final example demonstrates connectivity between applications using arbor.js to display the relationship.

The examples provided are in the form of a multi-module maven project and are meant to gradually progress a reader into Spring MVC.

1. springMvcBasic

This is the basic project and provides the simplest example of a Controller that renders a list of applications.

 2. springMvcFormValidation

The springMvcFormValidation module demonstrates the use of JSR 303 validations of Forms and form submission.

3. springMvcRequestResponses

This project demonstrates the use of the REST support of Spring MVC by sending and receiving XML and JSON.

4. springMvcDecorated

The springMvcDecorated module is conglomeration of the above examples and also demonstrates the following:

a. Internationalization
b. Spring Theme support using Twitter Bootstrap for the styling.
c. arbor.js for demonstrating application interaction
d. displayTag and dataTables.js for table support
e. sitemesh for decorating pages

Enter into each project and execute a mvn jetty:run to start the application. You can access each application at the corresponding ports.

The presentation is attached herewith.  The code samples can be downloaded from here.

Follow the presentation along with the provided examples to see it in action. I am grateful to the multitude of community presentations and blogs that helped me with my presentation. 

Friday, June 29, 2012

Spring Data MongoDB Example

Sometime ago I had Blogged about using Morphia with Mongo DB. Since then I have come across the Spring Data project and wanted to take their API for Mongo  on a ride. So this BLOG is duplicating the functionality of what was present in the Morphia one with the difference that it uses Spring Data and demonstrates Mongo Map-Reduce as well. As most of my recent Blogs that use Spring, I am going to be using a pure JavaConfig approach to the example.

 1. Setting up Spring Mongo 


The Spring API provides an abstract Spring Java Config class, org.springframework.data.mongodb.config.AbstractMongoConfiguration. This class requires the following methods to be implemented, getDatabaseName() and mongo() which returns a Mongo instance. The class also has a method to create a MongoTemplate. Extending the mentioned class, the following is a Mongo Config:
@Configuration
@PropertySource("classpath:/mongo.properties")
public class MongoConfig extends AbstractMongoConfiguration {
 
  private Environment env;  
 
  @Autowired
  public void setEnvironment(Environment environment) {
    this.env = environment;
  }

  @Bean
  public Mongo mongo() throws UnknownHostException, MongoException {
    // create a new Mongo instance
    return new Mongo(env.getProperty("host"));
  }

  @Override
  public String getDatabaseName() {
    return env.getProperty("databaseName");
  }
}

2. Model Objects and Annotations 


As per my former example, we have four primary objects that comprise our domain. A Product in the system such as an XBOX, WII, PS3 etc. A Customer who purchases items by creating an Order. An Order has references to LineItem(s) which in turn have a quantity and a reference to a Product for that line.

2.1 The Order model object looks like the following:

// @Document to indicate the orders collection
@Document(collection = "orders")
public class Order {
  // Identifier
  @Id
  private ObjectId id;

  // DB Reference to a Customer. This is a Link to a Customer from the Customer collection
  @DBRef
  private Customer customer;

  // Line items are part of the Order and do not exist independently of the order
  private List<LineItem> lines;
 ...
}
The identifier of a POJO can be ObjectId, String or BigInteger. Note that Orders is its own rightful mongo collection however, as LineItems do not exist without the context of an order, they are embedded. A Customer however might be associated with multiple orders and thus the @DBRef annotation is used to link to a Customer.

3. Implementing the DAO pattern 


One can use the Mongo Template directly or extend or compose a DAO class that provides standard CRUD operations. I have chosen the extension route for this example. The Spring Mongo API provides an interface org.springframework.data.repository.CrudRepository that defines methods as indicated by the name for CRUD operations. An extention to this interface is the org.springframework.data.repository.PagingAndSortingRepository which provides methods for paginated access to the data. One implementation of these interfaces is the SimpleMongoRepository which the DAO implementations in this example extend: 
// OrderDao interface exposing only certain operations via the API
public interface OrderDao {
  Order save(Order order);

  Order find(ObjectId orderId);

  List<Order> findOrdersByCustomer(Customer customer);

  List<Order> findOrdersWithProduct(Product product);
}

public class OrderDaoImpl extends SimpleMongoRepository<Order, ObjectId> implements OrderDao {

  public OrderDaoImpl(MongoRepositoryFactory factory, MongoTemplate template) {
    super(new MongoRepositoryFactory(template).<Order, ObjectId>getEntityInformation(Order.class), template);
  }

  @Override
  public List<Order> findOrdersByCustomer(Customer customer) {
    // Create a Query and execute the same
    Query query = Query.query(Criteria.where("customer").is(customer));

    // Note the equivalent of Hibernate where one would do getHibernateTemplate()...
    return getMongoOperations().find(query, Order.class);
  }

  @Override
  public List<Order> findOrdersWithProduct(Product product) {
   // Where the lines matches the provided product
    Query query = Query.query(Criteria.where("lines.product.$id").is(product));
    return getMongoOperations().find(query, Order.class);
  }
}
One of the quirks that I found is that I was not able to use Criteria.where("lines.product").is(product) but had to instead resort to using the $id. I believe this is a BUG and will be fixed. Another peculiarity I found between Mongo 1.0.2.RELEASE and the milestone of 1.1.0.M1 was in the save() method of SimpleMongoRepository:
//1.0.2.RELEASE
public <T> T save(T entity) {
}

// 1.1.0.M1
public <S extends T> S save(S entity) {
}
Although the above will not cause a Runtime error upon upgrading due to erasure, it will force a user to have to override the save() or similar methods during compile time. If upgrading from 1.0.2.RELEASE to 1.1.0.M1, you will have to add the following to the OrderDaoImpl in order for it to compile:
@Override  
@SuppressWarnings("unchecked")
public Order save(Order order) {
   return super.save(order);
}

4. Configuration for the DAO's


A Java Config is set up that wires up the DAO's
@Configuration
@Import(MongoConfig.class)
public class DaoConfig {
  @Autowired
  private MongoConfig mongoConfig;

  @Bean
  public MongoRepositoryFactory getMongoRepositoryFactory() {
    try {
      return new MongoRepositoryFactory(mongoConfig.mongoTemplate());
    }
    catch (Exception e) {
      throw new RuntimeException("error creating mongo repository factory", e);
    }
  }

  @Bean
  public OrderDao getOrderDao() {
    try {
      return new OrderDaoImpl(getMongoRepositoryFactory(), mongoConfig.mongoTemplate());
    }
    catch (Exception e) {
      throw new RuntimeException("error creating OrderDao", e);
    }
  }
  ...
}

5. Life Cycle Event Listening 


The Order object has the following two properties, createDate and lastUpdate date which are updated prior to persisting the object. To listen for life cycle events, an implemenation of the org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener can be provided that defines methods for life cycle listening. In the example provide we override the onBeforeConvert() method to set the create and lastUpdateDate properties.
public class OrderSaveListener extends AbstractMongoEventListener<Order> {
  /**
   * This method is responsible for any code before updating to the database object.
   */
  @Override
  public void onBeforeConvert(Order order) {
    order.setCreationDate(order.getCreationDate() == null ? new Date() : order.getCreationDate());
    order.setLastUpdateDate(order.getLastUpdateDate() == null ? order.getCreationDate() : new Date());
  }
}

6. Indexing 


The Spring Data API for Mongo has support for Indexing and ensuring the presence of indices as well. An index can be created using the MongoTemplate via:
mongoTemplate.ensureIndex(new Index().on("lastName",Order.ASCENDING), Customer.class);

7. JPA Cross Domain or Polyglot Persistence


If you wish to re-use your JPA objects to persist to Mongo, then take a look at the following article for further information about the same.
 http://www.littlelostmanuals.com/2011/10/example-cross-store-with-mongodb-and.html

8. Map Reduce


The MongoTemplate supports common map reduce operations. I am leaning on the basic example from the Spring Data site and enhancing it to work with the comments example I have used in all my M/R examples in the past. A collection is created for Comments and it contains data like:
{ "_id" : ObjectId("4e5ff893c0277826074ec533"), "commenterId" : "jamesbond", "comment":"James Bond lives in a cave", "country" : "INDIA"] }
{ "_id" : ObjectId("4e5ff893c0277826074ec535"), "commenterId" : "nemesis", "comment":"Bond uses Walther PPK", "country" : "RUSSIA"] }
{ "_id" : ObjectId("4e2ff893c0277826074ec534"), "commenterId" : "ninja", "comment":"Roger Rabit wanted to be on Geico", "country" : "RUSSIA"] }

The map reduce works of JSON files for the mapping and reducing functions. For the mapping function we have mapComments.js which only maps certain words:
function () {
   var searchingFor = new Array("james", "2012", "cave", "walther", "bond");
   var commentSplit = this.comment.split(" ");
 
    for (var i = 0; i < commentSplit.length; i++) {
      for (var j = 0; j < searchingFor.length; j++) {
        if (commentSplit[i].toLowerCase() == searchingFor[j]) {
          emit(commentSplit[i], 1);
        }
      }
    }
}
For the reduce operation, another javascript file reduce.js:
function (key, values) {
    var sum = 0;
    for (var i = 0; i < values.length; i++) {
        sum += values[i];
    }
    return sum;
}
The mapComment.js and the reduce.js are made available in the classpath and the M/R operation is invoked as shown below:
public List<ValueObject> mapReduce() {
    MapReduceResults<ValueObject> results =  getMongoOperations().mapReduce("comments", "classpath:mapComment.js" , "classpath:reduce.js", ValueObject.class);

    return Lists.<ValueObject>newArrayList(results);    
}
Upon executing the map reduce, one would see results like:
ValueObject [id=2012, value=119.0]
ValueObject [id=Bond, value=258.0]
ValueObject [id=James, value=241.0]
ValueObject [id=Walther, value=134.0]
ValueObject [id=bond, value=117.0]
ValueObject [id=cave, value=381.0]


Conclusion

As always, the Spring folks keep impressing me with their API. Even with the change to their API, they preserved binary backward compatibility thus making an upgrade easy. The MongoTemplate supports common M/R operations, sweet! I have not customized the M/R code to my liking but its only a demo after all.
I quite liked the API, it is intuitive and easy to learn. I clearly have not explored all the options but then I am not really using Mongo at work to do the same ;-)

Example


Download the example from here. It is a maven project that you can either import into Eclipse or simply run mvn test from the command line to see the simple unit tests in action. The tests themselves make use of an embedded mongo instance courtesy of https://github.com/michaelmosmann/embedmongo.flapdoodle.de.
Enjoy!