Monday, January 23, 2012

Deploying Restlet components in Elastic Beanstalk

I spent some time finding a way to deploy a Restlet component in ElasticBeanstalk without giving up the option of deploying it as a standard Java executable for local development and testing. People have expressed interest in seeing the approach, but I don't have the time to create a full-fledged framework, so I've extracted the following template instead (links to code embedded in the prose). Just copy to your own environment, changing package and class names as you see fit, add your Guice Modules, define your Restlet applications and resources, and you should be able to run the resulting component both standalone and (by bundling everything in a WAR) via Elastic Beanstalk. I make no claims that this code is correct, that it is safe to use, or even that it will compile. You have to read it, compile it, and judge for yourself.

The code depends on Restlet, Guice, Rocoto, Guava, and (minimally) SLF4J. I've included commented-out code that uses the Restlet-Guice extension, which is in incubator status in the Restlet codebase.

The Main class is a GuiceServletContextListener and it has a main method. When you run com.example.server.Main from the command line, the GuiceServletContextListener methods are ignored. When you deploy a WAR that contains the associated web.xml file, the main routine is never called. You can inject the current DeploymentMode into your classes if you want to change behavior depending whether you're running standalone or in Elastic Beanstalk (i.e., as a servlet).

The MainComponent class is where your Restlet component logic goes. Its lifecycle is managed by the MainService class, which is where you can manage other services with lifecycles. (If you use JClouds BlobStores, for example, you can ensure that a singleton BlobStoreContext is closed when the MainService is stopped. Another example: a LifecycleService for the Hazelcast data grid) There is some trickiness to handling shutdown in various cases; make sure you nest try-finally clauses so that every one of your services gets a chance to shut down even if the previous shutdown attempt throws an exception or error.

The MainServletModule and MainServlet classes deal with the details of embedding the component in a servlet (which is necessary in order to use Elastic Beanstalk).

AwsCredentialsModule is an example of how you can inject AWS credentials from either of two sources: system properties defined on the command line (or via Ant invocation) and system properties defined by an Elastic Beanstalk configuration.

The flow of control when running standalone is that the main routine creates an instance of Main with the STANDALONE deployment mode, creates the injector, gets the singleton MainService, and starts it, which causes (in a different thread), the MainComponent to be started.

The flow of control when running as a servlet is that a default instance of Main is created (due to the web.xml directive) and gets the SERVLET deployment mode. Its contextInitialized call creates the injector, which injects the MainService and starts it, causing (in a different thread), the MainComponent to be started. 

  • web.xml - web application descriptor
  • Main - has main() routine for standalone; is context listener for servlet mode
  • MainComponent - the Restlet component we want to be able to deploy in both modes
  • MainService - manages lifecycle of MainComponent and (optionally) other services
  • MainServletModule - included in module list in Main; configures Restlet-Servlet bridge
  • MainServlet - injects MainComponent and returns it as the component it wraps
  • DeploymentMode - enum with two values: STANDALONE and SERVLET
  • AwsCredentialsModule - example of passing AWS credentials for binding with @Named