Lei Wu, Web Developer

Keeping simple things simple.

Sinatra, JBoss 6, JDBC and ActiveRecord

The article describes how to create a JRuby Sinatra web application and deploy it on JBoss 6.

It covers these key points:

  • ActiveRecord (including validation)
  • JDBC (including JBoss connection pool)
  • SQL Server
  • Form Helpers
  • tux
  • Warbler

We also use a little bit of Twitter Bootstrap for our views.

The source code of the application is available on GitHub at https://github.com/wuleicanada/myapp .

Set up dev environment on Windows 7

Install JRuby

For Windows 7, just download and install the exe(x64) executable.

Create the App

Let’s create a folder with the same name of our JBoss application context.

md myapp

cd myapp

Install necessary gems

First things first, install Bundler jruby --command gem install bundler.

Now we’ll create the Gemfile with the following content:

Gemfile
1
2
3
4
5
6
7
8
9
10
11
12
source "http://rubygems.org"

gem "sinatra"
gem "activerecord"
gem "sinatra-activerecord"
gem 'activerecord-jdbc-adapter'
gem 'sinatra-formhelpers-ng'


group :development do
  gem "tux"
end

And then run jruby --command bundle install to get the gems ready.

Note: The tux gem works as a console to interact with my Sinatra application. I would also love to have the shotgun gem, but unfortuantely it’s unavaible for JRuby.

The JDBC driver (sqljdbc4.jar) for SQL Server is included in the source code, but if you prefer, you can also get it from Microsoft’s web site. We’ll just save it to our application root.

SQL Server table

Our application will use one table called “users”.

1
2
3
4
5
6
7
8
9
10
CREATE TABLE [users](
  [id] [int] IDENTITY(1,1) NOT NULL,
  [firstname] [varchar](32) NULL,
  [lastname] [varchar](32) NULL,
  [email] [varchar](64) NULL,
 CONSTRAINT [PK_users] PRIMARY KEY CLUSTERED
(
  [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Let’s insert a user insert users (firstname, lastname, email) values ('John', 'Smith', 'john@smith.com')

Code the application

Our classic style Sinatra application consists of the following files:

config.ru

config.ru
1
2
require File.join(File.dirname(__FILE__), 'app')
run Sinatra::Application

app.rb

app.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
require 'rubygems'
require "sinatra"
require "sinatra/activerecord"
require 'sinatra/form_helpers'
require 'active_record'
require 'active_record/connection_adapters/jdbc_adapter'
require 'sqljdbc4.jar'


config_dev = {
  :adapter => "jdbc",
  :driver => "com.microsoft.sqlserver.jdbc.SQLServerDriver",
  :url => "jdbc:sqlserver://SERVER:1433;databaseName=DB_NAME",
  :username => "USER",
  :password => "PASS"
}

# JBoss connectoin pool. Need to set up datasource in JBoss
config_jboss = {
  :adapter => "jdbc",
  :driver => "com.microsoft.sqlserver.jdbc.SQLServerDriver",
  :jndi => "DATASOURCE_NAME"
}

# On JBoss $servlet_context is defined
config = defined?($servlet_context) ? config_jboss : config_dev
ActiveRecord::Base.establish_connection(config)

class User < ActiveRecord::Base
  validates :consent, acceptance: true, presence: true
  validates :firstname, presence: true
  validates :lastname, presence: true
  validates :email, presence: true, format: { with: /\A[^@]+@([^@\.]+\.)+[^@\.]+\z/ }
end


helpers do
  def h(text)
      Rack::Utils.escape_html(text)
  end
end


get "/users" do
  @users = User.take(10)
  erb :"users/index"
end


get "/users/:id" do
  @user = User.find(params[:id])
  erb :"users/show"
end

put "/users/:id" do
  @user = User.find(params[:id])
  if @user.update_attributes(params[:user])
      redirect '/users'
  else
      erb :"users/show"
  end
end

Our layouts and templates sit in the views folder with the following content.

layout.erb

layout.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<title>MyApp</title>
 <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
 <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>    
</head>
<body>

<%= yield %>

</body>
</html>

users/index.erb

users/index.erb
1
<%= h @users %>

users/show.erb

users/show.erb
1
2
3
4
5
6
7
<%= form('/users/' + @user.id.to_s, :put) %>
 <div>First Name: <%= input(:user, :firstname, :value=>h(@user.firstname), :size=>20)%></div>
  <div>Last Name: <%= input(:user, :lastname, :value=>h(@user.lastname), :size=>20)%></div>
  <div>Email: <%= input(:user, :email, :value=>h(@user.email), :size=>40, :disabled=>:disabled)%></div>
  <div><%= checkbox(:user, :consent, '1', :label=> false) %></span> Yes! I accept the term of service.</div>
 <button type="submit" class="btn btn-default">Submit</button>
</form>

Start our dev server

Run jruby app.rb and now our wonderful little app will be available at http://localhost:4567/users/

Deploy to JBoss 6

Create WAR file using Warbler

Let’s get started by installing Warbler as a gem jruby --command gem install warbler. Next, we need to configure Warbler:

  • Create a new folder called config under our application root
  • Run jruby --command warble config, which generates a configuration template called warble.rb under the config folder
  • Edit config/warble.rb and update these lines:
config/warble.rb
1
2
3
4
5
6
7
8
    # Application directories to be included in the webapp.
    config.dirs = %w(views)

    # Additional files/directories to include, above those in config.dirs
    config.includes = FileList['app.rb', 'sqljdbc4.jar']

    # Uncomment this if you don't want to package rails gem.
    config.gems -= ["rails"]

Now we’re ready to package our app: jruby --command warble

A war file will be created as myapp.war.

Deploy the WAR file

I tried the JBoss 6 Admin console first, but got an Out of Memory error. I worked around it by copying the WAR file to the deploy folder (in my case /usr/jboss6/server/default/deploy/) and restarting JBoss. And voila, our application is live on JBoss at http://jboss_address/myapp .