Getting your Rails app running on the SAP HANA Cloud Platform
Getting your Rails app running on the SAP HANA Cloud Platform
SAP HANA Cloud Platform is a PaaS service from SAP which you can use to host web applications running on the JVM and using SAP's in-memory database HANA. Hosting Java applications is obviously the most straightforward route but thanks to the rich ecosystem of JVM languages it's not the only one. In this tutorial I will show you how you can adapt a Ruby on Rails application to achieve a successful HANA deploy. Some of the instructions are based on this tutorial by Krum Baklasky: tutorial
If you don't already have a SAP HANA Cloud Platform account, you can grab a free trial account here: get account
Application setup
- Get the SAP HANA Cloud Platform SDK from HANA (neo-java-web-sdk-*.zip) and unpack it anywhere you want (in this tutorial we'll assume ~/neo). Make sure you have written permissions to this directory.
- Create an alias for ~/neo/tools/neo.sh or symlink somewhere in your
$PATH
. You will be using this tool a lot so it's nice to have a convenient command. If neo complains with this error Unable to set
JAVA_HOME
environment variable you need to set theJAVA_HOME
environment variable yourself. For example, on Ubuntu adding the following line to ~/.bashrc (or ~/.zshrc) fixes the issue:Install JRuby. If you are using rbenv, run: "rbenv install jruby" which will list the available jruby versions. As I am writing this post the newest version is jruby-1.7.16 so I ran:
- To speed up JRuby start time, set this environment variable:
export JRUBY_OPTS=--dev
More information about this option can be found in the JRuby wiki. Set up JRuby as the Ruby version for your project:
If you are creating a new app, you need to install Rails with
- To be able to use our database in the SAP HANA Cloud Platform with ActiveRecord (Rails ORM) we need to add some gems. SAP provides some gems for use with the MaxDB database (cloud-active-record-maxdb-adapter, cloud-jdbc-maxdb-gem) but there is no official gem (yet?) for working with the HANA database in the cloud. (Actually there is a activerecord-hana-adapter gem but unfortunately it doesn't work with JRuby.) Because of this, we will be using our forks of SAP's gems which we adapted to work with HANA Add these lines to your Gemfile:
To be able to test our application locally, we need to change the sqlite3 gem to:
We will also need the Warbler gem which is used to create a .war file which can be deployed to the SAP HANA Cloud Platform.
- Run bundle install to install all the gems required by your application
- We need to edit the database configuration of our application (config/database.yml). It should look something like this:
Generate warble's config file by running
- There is currently no way to issue a rake db:migrate manually on the remote HANA instance. To overcome this obstacle, create a new initializer (config/initializers/hana_db_migrate.rb for example) with the following code (adapted from github):
export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64
rbenv install jruby-1.7.16
If you are using rvm the command you are looking for is:
rvm install jruby --1.7.16
mkdir my_app # if you are creating a new app cd my_app rbenv local jruby-1.7.16
in rvm you have to put this line in your ruby-version file:
jruby-1.7.16
gem install rails
and generate the file structure of your application with
rails new .
Otherwise, running
gem install bundler
should install everything your application needs, including Rails.
gem 'activerecord-maxdb-adapter', github: 'prograils/cloud-activerecord-maxdb-adapter'
gem 'jdbc-maxdb', github: 'prograils/cloud-jdbc-maxdb-gem'
gem 'activerecord-jdbcsqlite3-adapter', group: [:development, :test]
gem 'warbler', group: :development, require: false
default: &default
  adapter: jdbcsqlite3
  timeout: 5000
  encoding: utf8
development:
  <<: *default
  database: db/development.sqlite3
test:
  <<: *default
  database: db/test.sqlite3
production:
  adapter: maxdb
  encoding: utf8
  reconnect: false
  jndi: java:comp/env/jdbc/DefaultDB
  pool: 5
  prepared_statements: true
local:
  <<: *production
  adapter: derby
warble config
This creates a file config/warble.rb. In this file, we need to uncomment and edit the line containing config.webxml.jndi, like so:
config.webxml.jndi = 'jdbc/DefaultDB'
# This initializer runs the equivalent of rake db:migrate on application start
# It relies on having all the 'db' folder contents packaged with the application's WAR
if Rails.env.production? && !defined?(::Rake)
  puts "============================= Executing application migrations ...
  ============================= "
  migrations = Rails.root.join('db','migrate')
  ActiveRecord::Migrator.migrate(migrations)
  puts "============================= Application migrations are executed.
  ============================= "
end
The conditional statement makes sure the migration doesn't fire off when you are just testing your application or running RAILS_ENV=production rake assets:precompile
(which we will need later on).
Local testing
You should be able to test most of your application's functionality with the standard Rails tools. Running your test suite as well as the local web server should work. However, if your application uses features specific to the SAP HANA Cloud Platform, such as authentication, you have the options of deploying your application to a remote HANA instance or to a local server. Here are the basic steps if you choose the second option:
- Run neo install-local to install the local test server into ~/neo/server.
- Precompile your assets:
SettingEXECJS_RUNTIME=Node RAILS_ENV=production rake assets:precompile
EXECJS_RUNTIME
is optional but speeds up the process (and requires Node.js to be installed). - Run warble war to create a .war file for your application.
- Clean out old assets by running assets:clean
- Run
RAILS_ENV=local neo start-local
to start the server (this command is needed only once as the server will keep running unless explictly stopped). We set the rails environment to local so we can use a different database configuration for the local server. - Run neo deploy-local -s my_app.war Your application should now be available at `http://localhost:8080/my_app`. You can view the local server's logs in your ~/neo/server/logs directory (ljs_trace.log seems to have the most useful information).
Testing specific features may require editing the server's configuration files which you can find in the ~/neo/server/config_master directory. You can find details on this topic in the SAP HANA Cloud Platform documentation. For example, here's the section on how to test authentication: documentation
One thing to keep in mind is that this local server uses Apache Derby as the database and not HANA so you still need to deploy to a real HANA instance to make sure your database-hitting code works as expected.
Deployment
To deploy your application to a real HANA instance you will need your username and account ID, both of which you can find in your SAP HANA Cloud Platform Cockpit after logging in. The steps for deploying your application are the following:
Precompile your assets:
- Run "warble war" to create a .war file for your application.
- Clean out old assets by running
assets:clean
- Deploy your war file to your cloud account:
- Restart your application:
EXECJS_RUNTIME=Node RAILS_ENV=production rake assets:precompile
neo deploy -s my_app.war -h hanatrial.ondemand.com -u <username> -a <username>trial -b my_app
You can append a "-p
neo restart -h hanatrial.ondemand.com -u <username> -a <username>trial -b my_app -y
The -y option makes the command wait for your application to be started.
To avoid typing your account details with every deploy and restart command you can use a properties file. In your ~/neo/tools/samples/deploy_war directory you will find a file named example_war.properties containing a template, which you can copy and fill out. Store your properties file inside your project's directory. Then you can use the above commands like so:
neo deploy my_app.properties
neo restart my_app.properties
You will still need to give your password, either interactively or with a -p option. More information about the properties file here: documentation
For even more convenient deployment, you can throw the above commands into a script. Here's the script I used:
#!/bin/bash
NEO=~/neo/tools/neo.sh
PASSWORD=<your password>
EXECJS_RUNTIME=Node RAILS_ENV=production rake assets:precompile
warble rake assets:clean $NEO deploy hana-booking.properties -p $PASSWORD && \
$NEO restart hana-booking.properties -y -p $PASSWORD && \
notify-send 'Deploy done!'
The last line sends you a desktop notification when your deploy is complete (assuming you are on Linux and have a notification system that supports libnotify installed). You can, of course, create a similar script for local testing.
Bonus: SQL console
It is often useful to be able to check the state of your application's database especially when debugging. Unfortunately there doesn't seem to be a way to run a rails console on your cloud-hosted application. However you can get access to your application's database via an SQL console. Below I present a script which makes this quick and easy (based on this script in the documentation: script).
#!/bin/bash -ex
# Opens an SQL console connected to a SAP HANA Cloud Platform database
# Requires neo (https://tools.hana.ondemand.com/)
# and hdbclient (https://hanadeveditionsapicl.hana.ondemand.com/hanadevedition/)
PATH=$PATH:~/neo/tools:/usr/sap/hdbclient # add console client and HANA client to PATH
user=<username>
account=<username>trial
dbSchema=<name of db schema>
HCP_PASSWORD=<your hana cloud platform password>
json=$(neo.sh open-db-tunnel -h hanatrial.ondemand.com -a "$account" -u "$user" -p "$HCP_PASSWORD" -i "$dbSchema" --background --output json)
regex='.*"host":"([^"]*)".*"port":([^,]*),.*"instanceNumber":"([^"]*)".*"dbUser":"([^"]*)".*"dbUserPassword":"([^"]*)".*"sessionId":"([^"]*)".*'
[[ $json =~ $regex ]]
dbHost=${BASH_REMATCH[1]}
dbPort=${BASH_REMATCH[2]}
dbInstance=${BASH_REMATCH[3]}
dbUser=${BASH_REMATCH[4]}
dbPassword=${BASH_REMATCH[5]}
tunnelSessionId=${BASH_REMATCH[6]}
rlwrap hdbsql -n "$dbHost:$dbPort" -i "$dbInstance" -u "$dbUser" -p "$dbPassword"
neo.sh close-db-tunnel --session-id $tunnelSessionId
As you can see in the comments, to get this working you will need to download the HANA database client.
You will also need the rlwrap package which should be available through your package manager (or through brew if you are on a Mac). This little program improves your console experience by making arrow keys (and others) work as expected.
Once you are connected to your database, type the command \dt to list your tables. You'll see that their names are prefixed with the SQL schema name which looks something like NEO_XXXXXX
. To avoid typing out this prefix with every SQL statement, you can set the current schema with set schema NEO_XXXXXX
.
credits: photo from unsplash.com