Entries with the tag Ratpack

Create Lightweight Groovy Web Apps with Ratpack

Besides having one of the most awesome names for a Groovy project, well besides Griffon ;), Ratpack is a nice web framework to scaffold and prototype ideas.

Inspired by the Sinatra Ruby web framework, Ratpack puts routes (URL mappings) at the forefront. Compared to Grails, it's a more bootstraped experience but is useful for small projects. Just as Sinatra coexists with Rails, Ratpack can coexist with Grails.

Route Basics

Each route contains the code that will be executed when it is accessed. Here is a basic route that prints "Hello, World!" when someone navigates to localhost:5000/helloworld:

get("/helloworld") {
    "Hello, World!"
}

Behind the scenes, there is a handler that is injecting a HttpServletRequest and HttpServletResponse for us to interact with. That means that you don't have to learn a new protocol and you can leverage your Java Servlet knowledge. To further illustrate this, any of the following routes would work as well:

get("/") {
    response.sendRedirect("/helloworld")
}
get("/error/:error") {
    response.sendError(new Integer(urlparams.error))
}
get("/data") {
    request.toString()
}

We can see from the error route that the :variableName notation gives us an equivalent to $variableName in Grails. They aren't listed here for brevity but any of the HTTP verbs can be routed to.

Running Ratpack

After downloading the package from Github at https://github.com/bleedingwolf/Ratpack, you'll need to run the gradle task 

gradle deployRatpack

This retrieves Jetty and other dependencies, compiles the framework, and drops all the libs into your .groovy/libs directory. I haven't had any problems with these libraries specifically but in the past libs in the .groovy directory have caused version conflicts, so just be aware. After you have the libs installed, you can run Ratpack in two different ways. Static mode uses a bash script named ratpack and any changes to files won't be picked up until you bounce the server.

ratpack <application file>

Dynamic mode operates similar to grails run-app in dev mode bouncing the server automatically when a file in the watched directory is changed:

groovy <RatpackDir>scripts/runapp.groovy <app file> <dir to monitor>

Drop the code from the two code snippets in a file and you're off to the races. No import files needed.

It remains to be seen if an ecosystem of middleware extensions as vibrant as Sinatra or Node.js builds around it but I'm looking forward to playing more with Ratpack.

Serving Images (Favicons) with Ratpack Middleware

One of the things I noticed from running the sample material was that I would get a 404 error when clients try to retrieve the favicon.ico file. I thought it would be a matter of just dropping the favicon.ico file into the app directory. If only it were that simple... What ended up working is creating a new route for the favicon and working some HttpResponse mojo to get Ratpack to serve up the image. Adding a route directly to our app file is nice but a bit messy. I split out the new route into its own file in an attempt to create a pseudo-Ratpack middleware. The file is listed below:

class Helpers {
    static addFavicon = { app ->
	def handler = {
	    try {
		response.setContentType("image/ico");
		file = new File('favicon.ico')
		response.setContentLength((int)file.length())
					
		inStream = new FileInputStream(file)
		out = response.getOutputStream();
					
		// Copy the contents of the file to the output stream
		byte[] buf = new byte[1024];
		int count = 0;
		while ((count = inStream.read(buf)) >= 0) {
		    out.write(buf, 0, count);
		}
		inStream.close();
		out.close();
	    } catch(Exception ex) {}
		""
	}

	app.get('/favicon.ico', handler)
    }
}

The empty string at the end of the handler is to make sure Ratpack doesn't balk at us handling the strream ourselves.

The code from the last post was just a closure so we need to modify it a little bit to automagically handle the favicons. Be aware that saving the file as a script in this manner(below) somewhat breaks the script from working with the ratpack script file. The reason for this is because the ratpack file expects there to only be a closure in any file you pass to it. The scaffolding we added to the file duplicates what the ratpack script was already doing so it fusses at you ... something about being DRY ;) ... but you've already deployed the libs to your .groovy so you can run it like any other groovy script making sure to pass in the Helpers.groovy file as well.

import com.bleedingwolf.ratpack.*
import groovy.ratpack.*

def app = Ratpack.app({
    get("/") {
	response.sendRedirect("/helloworld")
    }
    get("/helloworld") {
	"Hello, World!"
    }	
    get("/error/:error") {
	response.sendError(new Integer(urlparams.error))
    }	
    get("/data") {
	request.toString()
    }	
})

Helpers.addFavicon(app)
RatpackServlet.serve(app)

Alternatively, we can add the Helpers.... line to the ratpack script file. Or even yet, one might provide a gradle script and have the file be copied somewhere that is accessible to all the Ratpack apps.

Quick News Items

  • After much prodding and poking(albeit the nicest kind), by @pstehlik since I started at @Taulia, I have agreed to speak at the San Francisco Grails Cafe Centro Meetup Group on July 19.
  • Earlier that month on the 7th of July, I'm speaking at the Sacremento Groovy Users Group(SacGRU). The topic for both talks will be Ratpack.
  • My book Learning HTML5 Game Programming, that you might have heard about once or twice on this site ;) is now available for pre-order on Amazon.com.

Making a Chat Server with Ratpack

In this post, we're going to use Ratpack and WebSocket4J to create a simple chat website and server. I used the method from here to get Ratpack installed into a local Maven store for use with Grape and I pulled in a couple other dependencies that I needed. WebSocket4J also had to be added manually to Maven.

The routing part of the application is fairly simple, the only new addition was setting the 'public' property which we will explain a bit later. Our sole route renders the index.html template. Our WebSocket code is where it gets a little bit interesting. We start a ServerSocket on port 5555 that will initially accept all connections. If the client is trying to connect on any other enpoint but "/chat", the socket is closed. Each client has a ChatServer thread spun up for it.

@Grapes([
    @Grab(group='com.bleedingwolf', module='Ratpack', version='0.2'),
    @Grab(group='org.mortbay.jetty', module='jetty', version='6.1.25'),
		@Grab(group='org.websocket4j', module='WebSocket4J', version='1.3'),    
		@Grab(group='org.json', module='json', version='20090211'),
    @Grab(group='net.sf.mime-util', module='mime-util', version='1.2'),
    @GrabConfig(systemClassLoader=true)
])
def app = Ratpack.app {
    set 'public', 'public'
    get("/") {
    	render 'index.html'
    }
}

WebServerSocket socket = new WebServerSocket(5555);
try {
	while (true) {

		WebSocket ws = socket.accept();
		System.out.println("GET " + ws.getRequestUri());
		if (ws.getRequestUri().equals("/chat"))
			(new ChatServer(ws)).start()
		else {
			System.out.println("Unsupported Request-URI");
			try {
				ws.close();
			} catch (IOException e) {
			}
		}
	}

} finally {
	socket.close();
}

This listing below shows the code for the Chat Server. When initialized, it kicks off a new thread and adds the socket to the list of sockets to communicate with. It has one main function that does work called processMessage. processMessage acts on messages with the operation code "broadcast" and sends them to all the other sockets.

 

import org.json.*
import websocket4j.server.WebSocket
import websocket4j.server.WebServerSocket
public class ChatServer extends Thread {
static ArrayList <WebSocket> sockets = []
WebSocket ws
public ChatServer(WebSocket ws) {
this.ws = ws
sockets.add(ws)
}
def processMessage = { msg ->
cleanupConnections()
// convert to JSON object
def json = new JSONObject(msg)
def op = json.getString('operation')
if (op == 'broadcast')
processBroadcast(json)
}
def cleanupConnections = {
def newList = []
sockets.each {
if (!it.isClosed()) {
newList.add(it)
}
}
println "cleaning up connections"
sockets = newList
}
def processBroadcast = { json ->
for (s in sockets) {
s.sendMessage(json.toString())
}
}
public void handleConnection() {
try {
while (true) {
String message = ws.getMessage();
processMessage(message)
if (message.equals("exit"))
break;
}
} catch (IOException e) {}
finally {
try {
ws.close();
} catch (IOException e) { }
}
}
public void run() {
handleConnection();
}
}

 

Lastly, we have our HTML code. One thing that you might notice at first glance is that some $ symbols are escaped. The reason for that is both Groovy (which is evaluating the template) and Zepto/JQuery use the $ symbol. Unescaped, Groovy will try to evaluate the contents, otherwise it treats it as text. The same is the case for \ literals like \n. I've colored the extra \'s in red.

I personally like the feel of CoffeeScript so that's what the interaction code is in. When the page loads, it attempts to connect to the chat server on localhost:5555/chat. If the session is new and the user hasn't entered his or her name, there is a popup prompt. When the user clicks send, the message is sent to the server and distributed to all the clients.

<html>
	<head>
		<title>Chat</title>
		<script src="/js/json2.js"></script>
		<script src="/js/zepto.min.js"></script>	

		<script src="/js/coffee-script.js"></script>	
		<script type="text/coffeescript">
			socket = null
			if window.name is ""
				name = prompt 'What is your name'
				window.name = name
			
			connectToServer = ->
				socket = new WebSocket "ws://localhost:5555/chat"
				socket.onmessage = (event) ->
					obj = JSON.parse(event.data)
					console.log(obj)
				
					val = \$('#chat').get(0).value
					val += obj.data.sender + ' said:' + obj.data.message + '\\n'
					\$('#chat').get(0).value = val
				window.socket = socket
				
			sendMessage = ->
				msg = \$("#message").get(0).value
				packet = {}
				packet.data = {'message':msg, 'sender':window.name}
				packet.operation = 'broadcast'
				json = JSON.stringify(packet)
				console.log(json)
				socket.send(json)
				
			window.connectToServer = connectToServer
			window.sendMessage = sendMessage
		</script>
	</head>
	<body onload="connectToServer()">
			<h1>Chat</h1>
			<textarea id='chat'readonly rows='10' columns='50' style='width:200px;height:550px'></textarea>
			<br/>
			<input type='text' columns='40' id='message'/>
			<input type='button' value='Send' onclick="sendMessage()"/>
	</body>
</html>

Static files: If you set the application config property "public" to anything, it will attempt to serve what it thinks are files out of that directory. There is currently a bug on trunk for static file serving, you can get the fix on my git fork. Zepto.js is a lightweight JQuery-like library. Here are the links for the other JavaScript libraries I used: json.js and coffee-script.js. All of these files would go in the public directory we specified in the app declaration.

Basic Authentication with Ratpack

In this post, we're going to cover how to create a AJAX login request using JQuery. While the format of the request is specific to JQuery, any front side JS library will do. If you are new to Ratpack, check out my introductory post here.

Basic Authentication receives access to a resource by submitting a request and attaching a header formed by Base64 encoding the concatenation the username, a colon, and the password. It is easy to implement but relies on the connection between the client and the server be secure. A MITM attack could easily compromise data if it is transmitted using HTTP.
Starting from the front end, I created a HTML file to hold our AJAX code, show below. One thing that is useful to note is that although it appear to be a regular HTML file, it is also a Groovy template. That is why the $ that JQuery uses have to be escaped. The form takes a username and password, encodes them, and sets the header. On success, the result text is pushed to a div.

    <html>
	<head>
		<title>Login with Ratpack</title>
		<script src="/js/jquery.js"></script>
		<script src="/js/jquery.base64.js"></script>
		<script>
		function login() {
			var username = \$("#username").val();
			var password = \$("#password").val();
			\$.ajax({
					'type':'POST',
					'url': 'http://localhost:5000/login',
					'otherSettings': 'othervalues',
					'beforeSend': function(xhr) {
						xhr.setRequestHeader("Authorization", 
                                                      "Basic " + \$.base64.encode(username + ":" + password))
					},
					success: function(result) {
						\$("#results").html(result);
					}
			});
		}
		</script>
	</head>
	<body>
			<h2>Login Form with Ratpack</h2>
			<div>
				<span>Username:</span><input id="username" type="text" columns=20>
			</div>
			<div>
				<span>Password:</span><input id="password" type="password" columns=20>
			</div>
			<button onclick='login()' >Login</button>
			<div id="results"/>
	</body>
    </html>
    


Next, let's move to the backend. Let's start by creating a helper to assist with authentication requests. The handler takes a HttpServletRequest in as a parameter and attempts to pull off the Authorization header. From that header, it retrieves the encoded username and password (in non-demo code, use try-catch blocks, do as I say not as I do). The demo punts on any type of strong validation but you can see where it would go. We end by using meta-magic to attach the doAuth function to the application.

    class Auth {
	    static doAuth = {app ->
		    def handler = { req ->
    	        def authHeader = req.getHeader("Authorization")
    	        def encodedValue = authHeader.split(" ")[1]
    	        def decodedValue = new String(encodedValue.decodeBase64())?.split(":")
    	        // do some sort of validation here
    	        if (decodedValue[0] == "") {
    		        return "Unauthorized"
    	        } else {
    		        decodedValue
    	        }
            }
            app.metaClass.doAuth = handler;
        }
    }
    

Last but not least, we get to lay out the routes. After setting up the public and template directories, we render the HTML we declared above on "/." When the login button is clicked, a POST request is made to "/login" which executes the doAuth helper function on the request. If the auth was successful, the server echoes back the username and password submitted. Otherwise, it returns "Unauthorized." Before instructing the servlet to execute the app, we call the static doAuth function on app so that it can add itself to app's metaclass.

   def app = Ratpack.app {
	set 'public', 'public'
	set 'templateRoot', 'templates'
	
	get("/") {
		render 'index.html'
	}
	
	post("/login") {
		result = doAuth(request)
		if (result != "Unauthorized") {
			"Logged in with username ${result[0]} and password ${result[1]}"
		} else {
			"Unauthorized"
		}
	}
    }
    Auth.doAuth(app)
    RatpackServlet.serve(app)
    

Creating Ratpack Apps with the Gradle Application Plugin

Currently, there are two ways to run a Ratpack app, you can build and run the ratpack executable or you can use the runapp groovy script. Both methods are ok but I wanted a way to be able to easily move apps between computers and make sure I didn't miss a jar. I started poking around in the Gradle documentation and found what I needed in the application plugin.

Gradle's Application plugin provides tasks that are common to an application's lifecycle:
run, which runs the app,
installApp, which installs the app into a directory,
startScripts, which creates OS-specific scripts to start the app, and
distZip, which creates an archive of the app directories.

All of this functionality is provided mostly for free by including the following code in your build.gradle file:

    apply plugin: 'application'
    
    mainClassName = '<package and name of your class>'
    

In lieu of installing Ratpack to my local Maven repository and assuming anyone I sent a distro of the app would too, I dropped the file into a lib directory and created a flat repository for that directory.

    repositories {
        flatDir name:'lib', dirs:'lib'
        mavenCentral()    
    }

The Ratpack build jar does not contain all of its dependencies so I copied them over to my dependencies gradle block. You could just as easily drop the dependencies into your flat repo and reference them from dependencies. I decided to let Maven do the heavy lifting and retrieve them at run-time. I set up my source directories in the default Maven style and I was ready to go. gradle run actually started the app and I was able to respond to routes.

However, things failed tragically whenever I tried to access a public file or template from either the installed version, created using gradle installApp, or the extracted archive, created using gradle distZip. I quickly realized that it was because I naively thought gradle was going to package everything in the root directory. After a little wrangling, I happened upon the required incantations to make it work. The following code explicitly copies the template and public directories to the installed app and archive.

    installApp {
        into('build/install/'+applicationName){
            from ('templates').into '/templates'
        }
into('build/install/'+applicationName){ from ('public').into '/public' } }
distZip { into(applicationName){ from ('templates').into applicationName+'/templates' }
into(applicationName){ from ('public').into applicationName+'/public' } }

You can view the whole file here.

Deploying a Ratpack App to a War File

Not much unlike making a deployable app with Ratpack, creating a deployable war file is pretty easy but there are some tweaks we need to make to the gradle file and in a couple of other places.

Under normal conditions, Ratpack spins up an embedded server, removing the need to create a web.xml file, so that's the first thing we need to do. For a basic app, you only need a single servlet as show in the listing below.

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

<servlet>
        <servlet-name>BlogServlet</servlet-name>
        <servlet-class>BlogServlet</servlet-class>
</servlet>

<servlet-mapping>
        <servlet-name>BlogServlet</servlet-name>
        <url-pattern>/*</url-pattern>
</servlet-mapping>

</web-app>

The servlet file RatpackServlet assumes that you are using a Groovy script file that it wants to load at run-time. You could alter the gradle war task to include the Groovy file, which is a good solution for a one-off app where all the code is in one file, or you could tweak the RatpackServlet file. The listing below is a snippet from BlogServlet, which essentially the stock RatpackServlet file with some modest tweaks, shown below. The first line of code instantiates the app followed by two lines to derive the relative path of any referenced static assets.

void init() {
    	app = new BlogApp().app
    	context = this.getServletContext()
    	rootDir = context.getRealPath("/")
    	// ...
}

Lastly, we need to format the war task in our gradle build file. That includes copying the static files and templates where they need to be, referencing a web.xml file and including the classpath dependencies.

war {
	into('/public') {	
		from('public')
	}
	
	into('/templates') {
		from('templates')
	}
	
	classpath fileTree('lib')
	webXml = file('resources/web.xml')
}

Redirect to Google+ with Ratpack

One thing that I had always wanted to do is to redirect from my domain to my Google+ profile; to have http://jameswilliams.be/+ redirect to Google+. Some folks have accomplished this by modifying their .htaccess file in Apache. I wanted to try to come up with a solution using Ratpack.

 

get("/\\+") { 
    response.sendRedirect("http://profiles.google.com/105400736676917752271/posts")
}

 

Once I figured out how to use "+" in an endpoint(which requires escaping as listed above), it was really simple to get working: use the injected response object to send a 302 redirect to my profile. Easy peasy.

Using Markdown with Ratpack

Groovy's SimpleTemplateEngine provides a very simple way to create dynamic HTML from templates. It is lightweight enough that it is compatible with almost any templating engine. I started playing around with it because I wanted to find a way to create some dynamic client code and wanted something more expressive and compact than regular HTML but not as complex as a JSP. Using Markdown and SimpleTemplateEngine together allowed me to hit that sweet spot.

Overview of Markdown

Markdown was created by John Gruber (of Daring Fireball fame) and Aaron Schwartz to allow one to easily translate a shorthand written in text files to HTML. It uses a limited set of special characters to designate headings, listings, figures, links and code blocks. It's the type of system that allows the formatting of an article to get out of your way and not disrupt your flow. Below is a sample Markdown file.

A First Level Header

====================

This is just a regular paragraph.

 

<% num.times { %>;

<%= it %>. <%= it+1 %> squared = <%= (it+1)*(it+1) %>

<% } %>

 

The following are code blocks and will print as is:

`<br> means break

<hr> means horizontal rule

`

In addition to parsers/translators for almost every programming language imaginable, Markdown is one of the formats that PanDoc can convert to a number of other formats including DocBook and PDF. For this post, I used MarkdownJ.

Using Markdown in A Route

Ratpack receives a request for an endpoint which retrieves the Markdown template, injects the given params, and evaluates the Groovy code contained in it. The resulting text is passed to MarkdownJ where it is converted into HTML and pushed into the response. The code snippet below shows how it all works.

import com.petebevin.markdown.*
//... truncated code
get("/") {
    def m = new MarkdownProcessor()
    def p = render 'template.md', [num:4]
    m.markdown(p)
}

Links

MarkdownJ

Markdown Quick Reference

Running Ratpack inside Grails

When I would present on Ratpack at conferences, one of the main questions I'd get would be around the migration path from Ratpack to Grails. I'd usually answer noting that you could run Ratpack along side Grails. It turns out it is incredibly simple to run a Ratpack app inside Grails. I don't think it's a long term solution and best as an intermediary step to migrate from one side to the other.
 
1. Grab and build Ratpack.
Provided you already have Gradle installed, run gradle buildDistro. The only file you need is the main Ratpack-x.x file. Grails will provide the Groovy and servlet container run time for you. Add this to the lib directory of your Grails app. 
 
2. Create your Ratpack app class. 
Putting the script into a proper class, as shown below, makes it easier to reference it from our Servlet.
 
import java.text.SimpleDateFormat
import com.bleedingwolf.ratpack.*

class SampleApp {

def app = Ratpack.app {
set 'port', 4999

get("/") {
def ua = headers['user-agent']
     "Your user-agent: ${ua} from Ratpack"
}
    
get("/foo/:name") {
"Hello, ${urlparams.name}"
}
    
get("/person/:id") {
"Person #${urlparams.id}"
}
  }

  public static void main(String[] args) {
SampleServlet.serve(new SampleApp().app)
  }
}
3. Subclass *RatpackServlet*.
If you don't mind getting an logging error, you can use something as simple as the following file:
class SampleServlet extends RatpackServlet { 
    void init() {
	app = new SampleApp().app
    }                    
}
 
4. Add the servlet information to your application.
There is a really confusing way to do it using the resources.groovy file but I prefer just adding it to the web.xml by hand. Run
grails install-templates
and navigate to src/templates/war/web.xml and add the following:
<servlet>
	<servlet-name>SampleServlet</servlet-name>
	<servlet-class>SampleServlet</servlet-class>
</servlet>

<servlet-mapping>
	<servlet-name>SampleServlet</servlet-name>
	<url-pattern>/ratpack/*</url-pattern>
</servlet-mapping>
 
The servlet needs to be put on a url-pattern that isn't root (/*) so I used (/ratpack/*). Run grails run-app and you're all set.

 

Creating Autocomplete with Groovy and Ratpack

About a month ago, there were a couple of posts about making an autocomplete solution using tries. Inspired by it as an interesting engineering problem I decided to use the Python code from this post and create a solution with Groovy and Ratpack for a hack day at work.

A trie or prefix tree is a tree structure that generally stores string data. In the figure below, we can see a basic example of a trie that stores airport codes. Typing the characters in a node will return the children. For example, in the figure below, if I type the letter b, it should return BOI, BOS, and BWI but if I type "BO", it should return BOS and BOI.

Graphic of Tries with airport codes

For a seasoned developer, porting Python code to idiomatic Groovy is a breeze and I'll leave you to check out the zipped source or the original article which explains the insertion and lookup code very well.

Instead I'm going to focus on the steps needed to run it in Ratpack.

In my application class, I used JSON data from http://airports.pidgets.com/v1/ to setup my tries. I used a lightweight JSON parser/serializer called yajjl but feel free to drop in your preferred library or use org.json which is bundled with Ratpack.

root = new Trie(null,null)

def parser = new JsonParser()
def text = new File('worldairports.json').getText()
def airports = parser.parseArray(text)
airports.each {
    if (it.type.equals('Railway Stations'))
        if (it.name.equals(""))
            it.name =  it.city+", "+it.state+" Rail"
    def name =  it.name
    def location =  it.city+", "+it.state+", "+it.country
    def code = it.code
    root.insert(name.toLowerCase(), 
        [displayName:name, location:location, code:code])
}
//codes
airports.each {
    if (it.type.equals('Airports')) {
        def location =  it.city+","+it.state+","+it.country
        root.insert(it.code.toLowerCase(), [displayName:it.name,code:it.code, location:location])
    }
}

One of the initial constraints of this project is that it had to work with the YUI widgets we were using. Yes. YUI. I know.

The only endpoint I have to define is the endpoint that YUI hits asking for a list of autocomplete suggestions. It takes in a query string and returns a JSON list of objects.

post("/autoCompleteLocation") {
    def x = root.autocomplete(params.query)
    def list = []
    for (i in x) {
        list.add([
            value:i.extraData["displayName"], 
            text:i.extraData["displayName"]+", "+i.extraData["location"]
        ])
    }
    serializer.serialize(list)          
}

Including the code for the tries that I merely translated from Python to Groovy, it's only 132 lines of code. With minor tweaks, it could work with a JQuery autocomplete widget or be expanded to offer "Did you mean ...?"-style suggestions too.

Dynamic Port Discovery With Ratpack

One of the default settings in Ratpack is that it starts applications on port 5000. It's easy enough to override this but on several occasions I deployed several Ratpack apps in the same container and have one or more fail to deploy because the port was already in use.

A solution to this annoyance is to have Ratpack dynamically select a port instead of defaulting to 5000 as shown in the snippet below:

    def findOpenPort = {
        def port = new ServerSocket(0)
        def portNum = port.getLocalPort()
        port.setReuseAddress(true)
        port.close()
        return portNum
    }

Passing a zero to ServerSocket tells it to dynamically pick an open port. Once the port is opened, we grab the port number, tell Java that we want to immediately reuse the port without a timeout, and close the port.

Listed below is full example file. The first few lines download Ratpack and its dependencies from MavenCentral.

    @Grapes([
        @Grab(group='org.slf4j', module='slf4j-simple', version='1.6.4'),
        @Grab(group='com.augusttechgroup', module='ratpack-core', version='0.5')
    ])
    import java.net.ServerSocket
    import com.bleedingwolf.ratpack.*

    def findOpenPort = {
        def port = new ServerSocket(0)
        def portNum = port.getLocalPort()
        port.setReuseAddress(true)
        port.close()
        return portNum
    }

    def port = findOpenPort()

    def app = Ratpack.app {
        set 'port', port

        get("/") {
            request.toString()
        }
    }
    println "App loaded on port ${port}"
    RatpackServlet.serve(app)