Wednesday, April 25, 2012

Building a URL Shortener in Grails - Pt 1

This is a lightning tutorial about how to create a URL shortener in Grails (working with 2.03) that I prepared in my own time and shared with some colleagues at work. I present it below with a bit more explanatory wording. I hope it is useful.

What we are building

The design goal of this tutorial is to build a simple URL shortener. URL shorteners have no been around for years (for an example look at the bit.ly service). By navigating to a short URL, your browser is redirected to the longer one (which might be highly unmemorable and clumsy - I'm looking at you, Sharepoint!)

Throughout this tutorial I refer to Short URL as the actual URL you would type into your browser and Short link as the name of the unique part of the URL after the domain.

For our application we want to be able to:
  1. Create new short link definitions (eg. Our short link 'cats' should send the user to  http://en.wikipedia.org/wiki/Cat) and then edit and delete them as required
  2. Actually redirect the user when they visit a short URL corresponding with a short link we have defined.
We'll call our application 'slink', for 'short link.' Lets get started.

Update 30-Apr-2012: This application is available on Github and tagged as it is at the end of the tutorial.

Pre-requisites

To complete this tutorial, you need a working Grails installation, so therefore you need to have Java and Groovy installed. These need to be installed in this order: Java, Groovy, Grails. Grails is built on Groovy which in turn is built on Java.

Take care to set up (or check that the installation program has set up) your environmental variables. From the command line in whichever folder you would like to use as a working directory, you should be able to run the following commands:

C:\projects\> java -version
java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04-415-11M3635)
Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01-415, mixed mode)
C:\projects\>  groovy -version
Groovy Version: 1.8.6 JVM: 1.6.0_31 Vendor: Apple Inc. OS: Mac OS X
C:\projects\>  grails -version
Grails version: 2.0.3
C:\projects\> 

The versions don't really matter unless you might have installed one of these a long time ago.

Creating an Application

Grails is a web framework, development environment, test framework and automation framework all in one. It provides everything you need to get an application developed quickly from a standing start.

When starting a new Grails application, the first thing you need to do is to ask Grails to create a new application. The create-app command creates a new subfolder to your current folder and populates it with both a starting point to your application code and all in the information required to deploy it locally.

To run a grails command directly from the command line, use the format:

C:\projects\> grails command [params]

From the command line in your working folder, create a new application named 'slink':

C:\projects\> grails create-app slink

| Loading Grails 2.0.3
| Configuring classpath.
| Environment set to development.....
| Installing zip tomcat-2.0.3.zip.....
...
C:\projects\>


When using Grails commands to modify or run or test an application (as Grails development normally entails), we need to be within our application folder.


Navigate to the subfolder that grails just created for your new application:


C:\projects\> cd slink
C:\projects\slink >


Have a look around using the command line, Windows Explorer, Finder or however you feel comfortable. This folder already contains a folder structure for your application code, resources, tests, configuration and more.


We are going to launch the Grails command line interface, which lets us enter commands without constantly prefixing them with 'grails'. It also gives us access to tab-completion, which really helps to explore all of the commands available within Grails.


C:\projects\slink\> grails
| Enter a script name to run. Use TAB for completion:

If you need to exit back to the raw command prompt or shell, just type exit. The first time you run Grails, it will download a number of supporting libraries.

Even now we can run our application. Into the grails command-line, execute the run-app command:


grails> run-app
| Server running. Browse to http://localhost:8080/slink
| Application loaded in interactive mode. Type 'exit' to shutdown.
| Enter a script name to run. Use TAB for completion: 
grails> 

The run-app command starts a lightweight development server hosting your application. To demonstrate this is true, open http://localhost:8080/slink in your browser as directed. You will see the default Grails homepage - your only page so far - showing a welcome message and listing the plugins you have installed. Creating this empty application is nearly always the first thing you will do when beginning grails development.


Default home page of a Grails application

Creating a ShortLink Domain Class

Its time to start turning your empty application into the URL shortener we intend to make. One of the advantages to working with a framework like Grails is that you are strongly encourage to follow good development practices and separate the different parts of your application. Grails separates the model into what it calls domain classes and makes it super-simple to persist instances of those classes to the database.

In the URL shortener we are making, we only really need to track our list of short links. Create the application's model now:

grails> create-domain-class shortlink
| Created file grails-app/domain/slink/Shortlink.groovy
| Created file test/unit/slink/ShortlinkTests.groovy

As the messages show, Grails has created two files - the actual domain class Shortlink.groovy and a unit test for this class.

We will put aside talking about the unit test for the purpose of this tutorial, but it is worth noting that nearly everything has a defined approach to testing in Grails, and many commands - like the above - create a corresponding test for an item you create.

Open grails-app/domain/slink/Shortlink.groovy in your favourite text editor or IDE. You’ll see:

package slink
class Shortlink {
    static constraints = {
    } 
}

This is an empty domain class. Lets define the data we are trying to model. To do this we add property definitions to the class that Grails will automatically map to the database when we class special methods added to this class at runtime.

Add a short link and target URL property to the domain class. We represent both these as strings:

class Shortlink {
     String shortLink
     String targetUrl
     static constraints = {
     }
}

add some constraints, save, and close the file:

class Shortlink {
...
    static constraints = {
           shortLink size:1..25
           targetUrl size:1..255
    } 
}


Constraints provide additional behaviour about the properties that Grails can use when validating new objects prior to saving or for influencing how the property is displayed when a domain class is scaffolded (more on that later).

And thats all you need to do within the domain class for the moment. Later you can go into the class, add additional constraints and perhaps a few more useful properties.

Creating an Admin Controller

So far we defined a single domain class, but our application is unchanged (visit your application if you want to make sure - the Development Server is still running unless you have stopped it).

We need to tell our application to actually do something. That is what controllers are for: responding to user input (such as requesting a specific web page) and deciding how to react.

We are now going to create a controller for our application. This controller will be responsible for responding to all of our instructions to administer our short links. Back at the Grails command line, type the following:

grails> create-controller admin
| Created file grails-app/controllers/slink/AdminController.groovy
| Created file grails-app/views/admin
| Created file test/unit/slink/AdminControllerTests.groovy

Grails has created 2 files and a folder
  • AdminController.groovy is the controller itself. Notice how Grails appended Controller to the name.
  • The folder /views/admin is a default location for any views you create for that controller. A view defines how the program output is presented to the end user, quite separately from the controller (which decides what to do).
  • AdminControllerTests.groovy is a unit test.

Open the newly created controller in your text editor or IDE. You will see something like:

package slink
class AdminController {
    def index() { }
}

There isn't much there, but as of the creation of this file, Grails will direct any requests for http://localhost:8080/slink/admin to this controller to work out what needs to be done. Each public method inside of the controller defines an action that can be undertaken by the controller. Grails determines the action from the URL of the request. By default:

http://.../slink/admin will call method AdminController.index() (index is the default controller action)
http://.../slink/admin/index will call method AdminController.index()
http://.../slink/admin/abc will call method AdminController.abc()
http://.../slink/admin/dogsandcats will call method AdminController. dogsandcats()

...and so forth.

Remove the index method and tell Grails that this controller should use dynamic scaffolding for provide create, read, update and delete functionality for ShortLink domain class objects.

Make your controller file look like this:


package slink
class AdminController {
    static scaffold = Shortlink
}

Scaffolding

Scaffolding is the name for automatically generating code to suit a common generic purpose. What we did with our changes to the AdminController above was tell Grails it should scaffold the domain class. In this context that means Grails will dynamically generate methods on our controller to provide create, read, update and delete (CRUD) actions for the ShortLink domain class. The corresponding views are also dynamically generated.

What this means is that a basic version of our Short Link administration functionality is complete.

Have another look at the application. If you refresh the page at http://localhost:8080/slink you should see now that a list of controllers is included on the homepage, which now includes the AdminController we just created. If you click the link for the AdminController or type in the URL http://localhost/slink/admin you will see you have a fully functional facility to view and manage Short Link objects.

Try creating, updating or deleting some short links.


Note: Grails does its best to update the development server as you make changes, but my experience is that sometimes you need to restart the development server to see changes or clear a strange error. To restart the development server you started at the command prompt, type exit and then run-app.

As you add, update, view and delete Short Links, Grails is using its own GORM (Grails Object Relational Mapper) technology store the date you enter and change in the bundled in-memory H2 database. The default database and its configuration means that any data you enter only exists until the development server is running. This makes a lot of sense for development work but like everything else in Grails, if you don't like the convention you can choose configuration.

Adding a Controller for Redirection

Creating and managing short links is useless we implement the means by which a user requests a short URL containing the short link and is redirected to the target URL. Since this is a response to a user event (requesting the short URL) and generates a response (the redirection) this is the job for a new controller. Lets create one now. Back at the Grails command prompt:

grails> create-controller redirector
| Created file grails-app/controllers/slink/RedirectorController.groovy
| Created file grails-app/views/redirector
| Created file test/unit/slink/RedirectorControllerTests.groovy

Again, notice that Grails has gone to the trouble of creating not only the controller, but also a test and a folder to contain default views for the controller.

Unlike the Admin Controller where we could use scaffolding to achieve our aims, there is no way Grails could know what we actually want from this controller. So we need to know what we want.

What we need the controller to do is handle a request routed to the controller to accept a short link and redirect to the corresponding target URL. In other words, we want to:

  1. Accept a value from the request, being the short link we want to redirect to
  2. Find the the ShortLink object with the same short link value, and
  3. redirect to the URL in the targetUrl property of that short link 

To write the controller method that will do this, open the RedirectorController.groovy file we have just created in your text editor or IDE and provide an implementation of the index() method, like this:

class RedirectorController {
    def index(String shortLinkName) {
           redirect url:Shortlink.findByShortLink(shortLinkName).targetUrl
     }
}

Save the controller when you are done. There are quite a few things here we could take note of:
  • We are accepting a shortLinkName parameter. Grails will provide this value when it is provided in the request. Whether or not the parameter is found in a request query string, form post or the request URL is not important from the point of view of the controller.
  • We use the redirect method to send a redirection instruction back to the user's browser
  • We find the target URL we want to redirect to by retreiving the appropriate short link using a static method defined on the ShortLink (findByShortLink()). We didn't have to create this method in our domain class - it's behaviour is dynamically generated when it is called.
Despite the number of things we could note, the entire method is implemented in one line of code, and the basic behaviour of our redirection is now defined.

Testing the Redirection Manually

If your development server is still running, return to the home page (http://localhost:8080/slink if you have followed along exactly) and see if the new RedirectorController is listed. If not, restart the development server by typing exit and then run-app at the Grails command line.

Vist the administration pages again by browsing to http://localhost:8080/admin and create a short link to test. In this example, we define a grails short link pointing to target URL http://www.google.com.au/search?q=grails (a google search for the term 'grails'):



Once created and saved, visit http://localhost:8080/slink/redirector?shortLinkName=grails. You should be redirected to the Google search result for 'grails'. There is your redirection!

But wait - We had to enter: 
http://localhost:8080/slink/redirector?shortLinkName=grails

...to be redirected to:
http://www.google.com.au/search?q=grails

How is that a short URL? The next few sections will make it much shorter.

Shortening our Short URLs - Remapping URLs

The first thing to shorten about our short URLs is the way we are specifying the short link name. Currently, we are passing the short link name to the redirector controller through the query string. Grails' default parameter handling mechanism is automatically mapping the ShortLinkName parameter in our URL to the shortLinkName parameter in the redirector controller. 

To modify the way grails maps URLs in particular and requests in general to controllers is specified in a (appropriate named file called UrlMappings.groovy in the conf directory immediate under the root directory of the application. Open this file and you will see the default mapping:

class UrlMappings {

    static mappings = {
        "/$controller/$action?/$id?"{

            constraints {
                // apply constraints here
            }
        }
        "/"(view:"/index")
        "500"(view:'/error')
    }
}

The default UrlMappings.groovy file contains three URL mappings: a mapping to automatically select a controller and action from the requested URL and pass an id value, a view handler for a server error (status code 500) and the home page. Both of the later render views only, so no controller is called.

Instead of this URL with a query string:
http://localhost:8080/slink/redirector?shortLinkName=grails

We really want to use a URL like this to pass in the ShortLinkName:
http://localhost:8080/slink/grails

From an implementation point of view what we are really saying is that the application should call the RedirectorController by default and pass in the second path element of the URL as the shortLinkName.

The one caveat to our plan is that we still need to access our administrations pages, so
http://localhost:8080/admin should not follow a short link called 'admin' but display the default page of our administration pages. ie. it should call the AdminController, not the RedirectorController.

Modify and save the UrlMappings.groovy file so that it looks like this:

class UrlMappings {

    static mappings = {
        "/admin/$action?/$id?" (controller:"admin")
        "/$shortLinkName" (controller:"redirector")

        "/"(view:"/index")
        "500"(view:'/error')
    }
}

In the above file we are explicitly defining the admin controller mapping prior to defining the mapping that shortens our short URLs. The use of the $shortLinkName parameter in our short link mapping means that whatever follows the first slash below the application is treated in exactly the same way as original redirector URL with the short link name in the query string.

Try it out - you should now be able to browse to your short link at 
http://localhost:8080/slink/grails. You might need to restart the development server for the change to take effect. If you do, remember to recreate the short link you wish to test.

Shortening our Short URLs - Changing the Context Root

You might have noticed the name of our application is still taking up valuable keystrokes in the format of our short URLs. When Grails applications run they run as an application inside a Java Enterprise Edition (Java EE) container. These containers have the concept of a context root or document root which is the top level virtual 'folder' in which a single application lives. This allows multiple applications to co-exist on a single server.

For our purposes we would like to use the root context so that any request reaching server at a URL below '/' reach our application. To do this we simply need to edit a Grails property. The config.groovy file in the conf subfolder of our application contains most of the standalone properties configuring a Grails application. Open this file (conf/config.groovy) now and add this line anywhere:

grails.app.context = "/"

Exit the development server and run it again. Note the URL is different when you run the application:

grails> run-app
| Server running. Browse to http://localhost:8080/
| Application loaded in interactive mode. Type 'exit' to shutdown.
| Enter a script name to run. Use TAB for completion:
grails>

Create your testing short link again (‘grails’ = ) again since the database is transient. The URL for our short link is now:

http://localhost:8080/grails

which compares to the original

http://www.google.com.au/search?q=grails

somewhat better.

Shortening our Short URLs - The rest

Sometime in the future, when we deploy our application to production it will most probably run on port 80, the default port for unsecured HTTP. We will then not need to include the port in our URLs. We'll also use a domain name, preferably a short one. Assuming we purchased the domain 'short.ly' (which in reality would never be available) our short URLs would take the form

http://short.ly/grails

which is probably the shortest you could choose to make it without limiting our short link name or finding a shorter domain name.

Running as Production

Grails provides an easy way to compile and execute our Grails application as if it was in production. Using the run-war command, Grails compiles and creates a WAR file and then launches it in the configured server environment, which is Tomcat by default.


grails> run-war
| Done creating WAR target/slink-0.1.war
| Running Grails application
Tomcat Server running WAR (output written to: C:\Project\target\tomcat-
out.txt)
| Server running. Browse to http://localhost:8080/

Epilogue: Done and not Done

At this point our application is functional as per our original intentions. We can add, edit and delete our short links using the scaffolded admin screens, and the short links we define create short URLs which redirect the requests correctly. This is very far from a robust application however, which you might have noticed.

One of the things that need to be fixed with this application includes the complete lack of running tests. If you type test-app at the Grails command line you will see we already have some failing tests we should implement. Additionally since we change the URL mappings we should probably put some tests around them. Writing these tests would do a good job of revealing other problems with the application. The good news is that Grails makes testing nearly everything very easy.



grails> test-app
| Running 3 unit tests... 1 of 3
| Failure:  testSomething(slink.AdminControllerTests)
|  java.lang.AssertionError: Implement me
     at org.junit.Assert.fail(Assert.java:93)
     at
slink.AdminControllerTests.testSomething(AdminControllerTests.groovy:15)
| Running 3 unit tests... 2 of 3

| Failure:  testSomething(slink.RedirectorControllerTests)
|  java.lang.AssertionError: Implement me
     at org.junit.Assert.fail(Assert.java:93)
     at
slink.RedirectorControllerTests.testSomething(RedirectorControllerTests.
groovy:15)
| Running 3 unit tests... 3 of 3
| Failure:  testSomething(slink.ShortlinkTests)
|  java.lang.AssertionError: Implement me
     at org.junit.Assert.fail(Assert.java:93)
     at slink.ShortlinkTests.testSomething(ShortlinkTests.groovy:15)
| Completed 3 unit tests, 3 failed in 1015ms
| Packaging Grails application.....
| Tests FAILED  - view reports in target/test-reports


The application is hardly robust either. Nothing stops or prevents a user from using a Short URL that does not exist. For that matter nothing stops a user from creating a target URL that is not a real URL. Under these conditions our application will fail in a not too graceful way - Try it now.

Security is also completely lacking. Anyone can create a short link or use a short URL, which is probably not a practical attribute in most usage situations.

Fixing the above are all excellent points for a later blog post, but this tutorial ends here. If you have comments or corrections, please leave them below.

In the meantime, enjoy Grails.

No comments:

Post a Comment