I've been refactoring a Rails 3 application with a spaghetti configuration mess into something more in line with The Twelve Factor App where I'm using the dotenv gem for configuration. (One day we may want to use a service like Consul for configuration. That day is not yet today.)

I also wanted to see what sort of performance I could get out of using Tomcat 7 with Amazon's ElasticBeanstalk especially since the autoscaling story configuration there is easy to setup.

To run my Rails application on EB successfully I needed to add environment variables for the service account executing the Tomcat daemon, or introduce them after application startup, which would mean modifying the source code to include the actual configuration values - something I wanted to avoid.

Fortunately EB does provide "Environment Properties" which is a series of key/value settings where you can define your own key and values. This meets the Store config in the environment tenet of the Twelve Factors. However its easy to be lead into believing these values are environment variables; meaning the code you may use on MRI during development to access ENV, i.e., ENV[API_KEY] should just work. Not so fast. For the Tomcat version of EB the values specified are standard -D java switches for system properties. If you didn't know this going in its easy to get frustrated. (I certainly got frustrated. In fairness they are environment properties so its like a smattering of environment variables and system properties.)

In order to get around this problem I created the following class:

if RUBY_PLATFORM == "java"  
  require 'java'
end

class Env  
  # necessary to support the way AWS EBS handles environment variables for Tomcat
  def self.[](name)
    if RUBY_PLATFORM == "java"
      ENV[name] || java.lang.System.getProperty(name)
    else
      ENV[name]
    end
  end
end  

Then I just substitute all instances of ENV with Env in my Rails application.

If you're using warbler to package your application one of the things it does is create a web.xml file for you with a setting embedded in it (whatever was specified when building the jar, or production if no setting was specified), preventing the executing environment from specifying the setting you intend.

On Tomcat 7 you can specify that system property values should be used in any XML file using ${system.variable.name}. So if you configure a rails.env key and value staging in your Beanstalk configuration and place a web.xml file under the config directory you can add an environment configuring setting like so (in the web.xml file):

<context-param>  
    <param-name>rails.env</param-name>
    <param-value>${rails.env}</param-value>
</context-param>