Sunday, May 8, 2011

Running Sinatra application on Tomcat

My recent fascination with the Sinatra framework has made me look for viable deployment options. At first I started with JRuby and Tomcat6.

So what do I need (top-to-bottom with only JDK installed) to successfully deploy a hello-world style application on Tomcat?

Installing prerequisites.

First you need a copy of JRuby and Tomcat6. Unzip both of them some place you'll have easy access to. In this tutorial I'll be using the root of drive C: because this is a folder everyone will have.
After doing so you should have 2 folders on your disk with the appropriate applications:

C:\apache-tomcat-6.0.29
C:\jruby-1.6.1

Installing required gems:

  C:\jruby-1.6.1\bin\jruby -S gem install sinatra sinatra-reloader warbler bundler

Creating the application.

Creating the hello-world style application (a bit extended version) is really simple. First you create a folder in a place of your choosing. In that folder you just need to create 2 files:

hello.rb:
  require 'rubygems'
require 'sinatra'
require 'sinatra/reloader' if development?

get '/' do
@message = "Hello, world! from Sinatra running JRuby on Tomcat"
erb :index
end

views/index.erb
  <h1><%= @message %></h1>
The hello.rb is the main application file and views/index.erb is the supporting view.
All further command line commands must be executed from the folder you've just created!

Testing if everything works as expected.

To test if the application performs as expected issue the following command inside the folder you have created:
  C:\jruby-1.6.1\bin\jruby hello.rb
and if there are no errors you'll have the application available on port 4567 right away. Test it with your favorite browser navigating to http://localhost:4567

Creating deployment files.

This is a little bit more involving. We'll create 3 files now:

Gemfile:
  source :rubygems
gem "sinatra"

config.ru:
  require 'rubygems'
require 'hello'

set :run, false

run Sinatra::Application

config/warble.rb:
  Warbler::Config.new do |config|
config.dirs = %w(views)
config.includes = FileList["hello.rb"]
config.jar_name = "hello"
end

The Gemfile is just the definition which gems are required by the application. The config.ru file defines how rack application is supposed to start. config/warble.rb is to tell the warbler tool which files go into the actual WAR file and what the output file name should be.

Create hello.war

To create the output hello.war file issue the following command:
C:\jruby-1.6.1\bin\jruby -S warble
At this point you should have the hello.war file ready for deployment.

Deploying to Tomcat.

This is the easy part. Simply copy the file hello.war to C:\apache-tomcat-6.0.29\webapps and start Tomcat.

Conclusion

It wasn't that hard, was it? Next time we'll see how we can do exactly the same with Apache HTTP Server and Passenger (a.k.a. mod_rails) running on a freshly installed Ubuntu 10.04.1.

And last but not least (because I know you've been waiting for it): the source code is obviously here for your entertainment :)

Have fun!

Edit on May 9th, 2011
I found out why specifying gem dependencies in config/warbler.rb doesn't work as it should and why the workaround with Gemfile is needed. Obviously for any bigger project it is better to use Gemfile and bundler as it solves the problem when another developer jumps in and wants to start hacking the application. For simple projects the config/warbler.rb should be enough though. Up until now specifying gems didn't work as expected due to an obvious bug in warbler. Here's a fix that I proposed to solve the problem:

https://github.com/padcom/warbler/commit/b4b24e17dee5bb98525203c82519a8901874ef81

With that in place the config/warbler.rb would look like this:
Warbler::Config.new do |config|
config.jar_name = "hello"
config.dirs = %w(views)
config.includes = FileList["hello.rb"]

config.gems = ["sinatra"]
end
and naturally the Gemfile would go away (one file less to keep track of :D in such a simple application)

Friday, May 6, 2011

Sinatra - the power is back!

I honestly don't know what I've been doing for the past 5 years. Really. I've been trying to solve simple problems by learning "corporate" bullshit allowing the really good stuff go unnoticed. But that has changed. I'm a new person now. I'm changed, changed forever.

Today I've been working on my json-rest-api plugin to allow for excluding some properties if you really wanted to. Ok I admit, I needed that in the application I'm writing for the corporate. And so I've been browsing, looking, searching because I somehow knew that something is wrong with the world around me. I couldn't quite tell what it was but it felt like a splinter in my mind that I just couldn't get rid of.

I've stumbled upon Graffiti micro-framework for building web applications in Groovy. I was so amazed with the immediate results it gave me that I started digging deeper and deeper into the roots of where this damn thing came from. And so I found Sinatra...

And the world will never be the same...

Sinatra is a micro-framework giving a DSL style to create simple web applications. Since I'm a windows addict (don't shoot me please!) I needed to find something else than shotgun to do the hot reloading of my app. And so I found sinatra/reloader...

And the world will never be the same...

Fortunately Sinatra is not married with any ORM so I could find a tool that's equally simple and does the heavy lifting of moving data from and to the database for me. And so I found DataMapper...

And the world will never be the same...

At this point I'm after re-writing the ihatejsf.com application (34 lines of Ruby code!!!) using Sinatra and DataMapper and after deployment on Heroku. It was an experience I'll never forget. It has changed me forever and ever.

I'm a changed man.

Wednesday, April 27, 2011

Running test on a live test instance of the application

I've faced a unique challenge today: test my json-rest-api plugin. Unique, isn't it? :D

The problem


Well all would be really easy if not for the fact that I wanted to do an end-to-end test just like a client would use the generated API for a domain class. In this case using integration tests was out of the question because the actual http listener is not running. And I wanted to test it all the way through...

After some digging I've found out that there's a number of plugins that provide this kind of functionality where the application starts and tests are being executed. Those plugins are functional-test, easyb-acceptance-test and webtest.

The unhappy seeker


I've tried to play around with those and one thing struck me down the most: they are mostly targeted at testing web pages and not RESTful services. So they were not as nice for my case as I'd like them to be.

What made me wonder though is that all of those plugins do exactly the same thing to actually spawn the application before tests:
eventAllTestsStart = {
if (getBinding().variables.containsKey("functionalTests")) {
functionalTests << "functional"
}
}
Obviously different plugins use different names for their tests but the idea remains the same. So I asked myself what would happen if I'd introduce the same thing in my scripts/_Events.groovy without those plugins installed... Much to my surprise Grails build system started the application right after spitting out "Starting integration test phase ..." :)

The road continues


Now all I had to do was to find out a nice way to actually get the test going. As it turns out there's a fantastic utility called HTTPBuilder that does exactly what I needed! Just a couple of lines in BuildConfig.groovy later...
test('org.codehaus.groovy.modules.http-builder:http-builder:0.5.0') {
excludes "commons-logging", "xml-apis", "groovy"
}
... and testing RESTful services has never been easier!
import groovy.util.GroovyTestCase
import org.junit.Test

import groovyx.net.http.*

class RESTfulTests extends GroovyTestCase {
@Test
void willReturnErrorIfSavingNotSuccessfull() {
def client = new RESTClient("http://localhost:8080/example/api/")
def response = client.post(
path: "person",
body: [ data: [ firstName: 'John', lastName: 'Doe' ] ],
requestContentType : ContentType.JSON.toString())
assert response.status == 200
}
}

If you want to see a more sophisticated usage of this approach with more assertions I encourage you to take a look at the basic application in json-rest-api-examples project.

The obvious


One note to add: if you'd like to run those integration tests as part of your continuous integration it'll most probably fail because the port is already in use by Hudson... To overcome this issue set the server.port parameter to some port value that's not in use. In Hudson it looks more/less like this:



Obviously you need to use this port in your tests instead of the default 8080...

Have fun!

Sunday, April 24, 2011

Hosting a Git repository on Ubuntu 10.04 LTS

I thought I'll post this recipe for anyone having issues hosting their own Git repos via HTTP. It's not that hard but a few things have to happen to actually make it work.

The automated way


If you're as lazy as I am here's a script that you can just download, execute as root and all the steps below will be executed for you. This means that you can turn a plain vanilla Ubuntu 10.04 installation into Git host in a matter of seconds:

https://gist.github.com/1034042 (raw)

The manual way


Install packages

sudo apt-get install apache2 git-core gitweb

Create folder to host your repositories

sudo mkdir /srv/git

Configure Apache to serve the repositories

Add the following to /etc/apache2/conf.d/git (create the file if it does not exist!):

# GIT
SetEnv GIT_PROJECT_ROOT /srv/git/
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAliasMatch \
"(?x)^/git/(.*/(HEAD | \
info/refs | \
objects/(info/[^/]+ | \
[0-9a-f]{2}/[0-9a-f]{38} | \
pack/pack-[0-9a-f]{40}\.(pack|idx)) | \
git-(upload|receive)-pack))$" \
/usr/lib/git-core/git-http-backend/$1
ScriptAlias /git /usr/share/gitweb/index.cgi

<LocationMatch "^/git/.*/git-receive-pack$">
AuthType Basic
AuthName "Git repository"
AuthUserFile /srv/git/users
Require valid-user
</LocationMatch>

Create a link to gitweb.js

This step is required so that the proper javascript library is available for GitWeb:
sudo ln -s /usr/share/gitweb/gitweb.js /var/www

Create users file

sudo htpasswd -c /srv/git/users john
and type the password twice

Create script to create repositories

Create a file /srv/git/create.sh with the following content:
#!/bin/sh
git init --bare $1.git
cd $1.git
git config http.receivepack true
git config gitweb.owner "John Doe"
cd ..
chown www-data:www-data -R $1.git

Use the create.sh script to create a repository

cd /srv/git
sudo sh create.sh example

Update path to repos for gitweb

In /etc/gitweb.conf update the $projectroot to:
$projectroot = "/srv/git"

Restart apache

sudo invoke-rc.d apache2 restart

That's it! you should now be able to clone and browse your repository using the smart HTTP transport!

The outcome


Remember that when you use this setup you'll have a Git repository that than be cloned by everyone but only users specified in /srv/git/users can push to this repo! This means that you'll have to provide username in the URL like this:
http://john@myserver/git/example.git

If you don't do that you'll see the following error while trying to push:
Counting objects: 3, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 1.05 KiB, done.
Total 3 (delta 0), reused 0 (delta 0)
error: RPC failed; result=22, HTTP code = 401
fatal: The remote end hung up unexpectedly
fatal: The remote end hung up unexpectedly

Hope this will save someone some time :)

Saturday, April 23, 2011

I'm finally a Git convert

Well, yes... It happened. I don't know how or when but it just did. I'm now officially a Git convert. Period.

For those of you who know me you might also know that I was a huge Subversion advocate. This was also seen in the role of Subversion trainer that I inherited after a friend of mine in my current company. I was teaching and preaching SVN for the better part of this millennium. So why?

Well, there are a couple of things that distinct SVN from Git and the distributed/centralized part of it ain't the most important one. There are some key features that come out of the fact how Git works but they could work in Subversion OK if this damn thing wouldn't be so eager to use the fricken network... In this case the distributed part of Git is not so much about giving commit access to nobody besides me. It's also not about the "I can commit when I'm on the plane". I mean, come on, geeks! How many of regular developers do the coding at FL300 to avoid wiretaps? :D

The best part of Git is speed. I mean speed as in fricken space rocket! Like it or not in a corporate environment there's always going to be this "everybody on the team has commit access" thing and nothing is going to change that. Get over it! The best thing about Git is that it works just as good in a centralized environment as Subversion or even better, faster, lighter, jezzier, sexier and all that :D

One more thing: setting up Git server to host the one true version of the repository in a centralized environment is so damn easy! There's like 10 different ways with the one truly blessed prescription (Smart HTTP Transfer). Just go google it. It's amazing how incredibly fast it is! And it plays so nicely with Trac and Redmine!

Now that I have that out of my chest I'd like to say a few words about other counterparts: Bazaar, Mercurial, Monotone

If you're still using Bazaar - I'm so sorry you didn't see the light yet. There's definitely a guy sitting next to you that already knows what this Git thing is so you better ask him before you'll be left alone in the dark.

Hail to all the HG users! You're also great :D But not as jazzy and sexy as the Gitters :D

Monotone... Well.. letsnotbetohardonthoseguysastheyareprobablytheonesthatwrotethisthing...

Thursday, April 14, 2011

I still hate JSF - but a little less :D

If you know me you also know this for a fact: I hate JSF with a passion! There are no bad enough words that you could use to express the hatred I feel. What can I say - I'm broken this way...

Today I finally gave JSF2 a spin. This time I already knew that working with Java EE is a pain in the neck so I've decided to use my usual environment (Grails) to do the test.

JSF / JSF2 has some major drawbacks out of the box. It's heavy and it's a misery to introduce changes (again, out of the box). Sure you can use like JRebel and stuff to make your life easier but then JRebel costs money and if you're just playing with the technology with no apparent reason spending any money is just not justified at all.

So back to the topic: JSF2 on Grails. Sounds, scary, right? Yes, I've got the goosebumps too :)

What's in the box? (cited after http://grails.org/plugin/jsf2):

- Bean By convention (no need for faces-config)
- Bean access to controller parameters (session,request,params...)
- Beans dynamic methods for navigation redirect(action?,bean?,view?,uri?,url?) and - render(action?,view?,bean?),
- Automatic bean and service properties resolution
- - 'void init()' called at bean initialization if present
- 'void dispose()' called at bean destruction if present
- JSF Extended request scope ('view')
- Access to web.xml configuration via Jsf2Config.groovy
- Access to faces-config generated in web-app/faces-config.xml
- Converters for Locale, Closure, Currency, Timezone
- i18n ready, fast access with #{m['key']}
- create-bean script
- Hibernate session managed from view rendering to view response
- Execute groovy code in EL expression ( #{gtag.groov[' def i = 2; 3.times{ i++ }; i; ']} )
- Support JSF2 Components - @ManagedBean ...

And so my test went like this. I've installed the plugin the usual way, I've create a class called StatusBean (in a .groovy file!), a little bit of copy-paste from the plugin's page to figure out the signature of an event listener did the heavy lifting for me, I've checked the FAQ section to see where should I put my xhtml files and it all worked right out of the box!

Well, I'd definitely NOT go as far as saying that I actually like JSF2. Hell no! I despise it just as much as I did before. I do however recognize that there's world outside of GSPs and SiteMesh and the alternatives are good for some things.

Sunday, April 10, 2011

Quicksort - the groovy way

I must confess: I didn't write quicksort by myself ever. I always felt it's one of those things that I don't really have to do. Well, Dierk Koenig showed that it might be useful to write your own implementation of quicksort. Not for regular use but for training purposes.

You all know the Collections.sort method, right? We all know it's damn fast, well tested and all that stuff. Let's see what does it take in Groovy to implement quicksort ourselves:
def qs(list) {
if (list.size() < 2) return list
def pivot = list[0]
def items = list.groupBy { it <=> pivot }.withDefault { [] }
qs(items[-1]) + items[0] + qs(items[1])
}

WTF? 4 lines plus function definition? And it can easily be shrunk to 3 lines by inlining the pivot variable:
def qs(list) {
if (list.size() < 2) return list
def items = list.groupBy { it <=> list[0] }.withDefault { [] }
qs(items[-1]) + items[0] + qs(items[1])
}


One of my colleges told me that before he saw the implementation of quicksort in Groovy he didn't quite get the idea, what it does and all. Same here :) I never tried to dig into the details, I just accepted that it works. But with this implementation I just see what's going on! There's nothing to understand, just 3 lines to read :D
By comparison an implementation of bubble sort is around 10 lines long and is about 100 times slower :D
If you're wondering how does this implementation perform by comparison to the Collections.sort() method here are the results (sort a 100 elements list 1000 times):

def qs(list)Collections.sort()
1938ms19ms

The actual timing varies but the ratio more/less 100:1 sticks.

It's interesting to note that there's a 10 times difference in timing when invoking list.sort() and Collections.sort(list) in favor for the latter one. That's probably due to the dynamic stuff going on in between.