Showing posts with label clojure. Show all posts
Showing posts with label clojure. Show all posts

Wednesday, July 02, 2014

Running a Clojure webapp on IBM Bluemix

<EDIT>: a few people have reported seeing the following error when pushing the app to Bluemix:
java.lang.NoClassDefFoundError: javax/servlet/http/HttpServletRequest, compiling:(ring/middleware/multipart_params.clj:39:5)
If you see this then you can work around it by moving the dependency on javax.servlet/servlet-api "2.5" out of :dev :dependencies and into :dependencies (or just clone the latest code from https://github.com/cdpjenkins/hello-clojure). I'll update this once I get to the bottom of why this started failing for some people.
</EDIT>

Today, I signed up for a free trial account on the public beta of Bluemix, IBM's Platform as a Service (disclosure: I work for IBM, though not on Bluemix). Bluemix performs a similar function to Heroku and Azure, allowing you to run applications in a variety of languages and has the advantage that it built upon the open Cloud Foundry platform.

Having played played around deploying Clojure apps on IBM's internal Bluemix beta, I thought I'd write a guide to getting started with Clojure on Bluemix now that it is open to the world.

To sign up, go to http://www.bluemix.net/. In order to do this, you need to create an IBM id if you don't already have one.

This gives you access to the Bluemix dashboard from which you can create new apps and services.



This post won't talk about using the Dashboard because I'm more comfortable using the command line. In order to use the command line, you first need to download the tools. You can find the installation package for your favourite OS here:

https://github.com/cloudfoundry/cli#downloads

In my case, I installed the tools on my Mac using Homebrew:
$ brew tap pivotal/tap
Cloning into '/usr/local/Library/Taps/pivotal/homebrew-tap'...
remote: Reusing existing pack: 200, done.
remote: Total 200 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (200/200), 30.78 KiB | 0 bytes/s, done.
Resolving deltas: 100% (101/101), done.
Checking connectivity... done.
Tapped 7 formulae
$ brew install cloudfoundry-cli
==> Downloading https://cli.run.pivotal.io/stable?release=macosx64-binary&versio
######################################################################## 100.0%
  /usr/local/Cellar/cloudfoundry-cli/6.2.0: 2 files, 18M, built in 26 seconds
Once the Cloud Foundry CLI tools is installed, you can run the cf command to interact with Bluemix. First, let's  point it at the Bluemix API (note to any IBMers who are using the IBM internal Bluemix: your API endpoint will be different to this):
$ cf api https://api.ng.bluemix.net
Setting api endpoint to https://api.ng.bluemix.net...
OK

                 
API endpoint:   https://api.ng.bluemix.net (API version: 2.2.0)   
Not logged in. Use 'cf login' to log in.
... and log in using your IBM ID:
$ cf login
API endpoint: https://api.ng.bluemix.net

Email> xxxx@xxxx.com

Password> 
Authenticating...
OK
...<snip>
Having done that, we need an app. Let's create Clojure web application using the leiningen compojure template:
$ lein new compojure hello-clojure
Let's check out the contents of our project.clj:
(defproject hello-clojure "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [compojure "1.1.6"]]
  :plugins [[lein-ring "0.8.11"]]
  :ring {:handler hello-clojure.handler/app}
  :profiles
  {:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
                        [ring-mock "0.1.5"]]})
Check that the version of lein-ring is at least 0.8.11 because this will make our life easier if we want to run an uberwar of our webapp on Bluemix at a later date.

The compojure template gives us a simple Hello World app which is good enough for our purposes here. We want to push it up to Bluemix using the command line tool but there is one problem: Bluemix does not natively support Clojure web apps and neither does Cloud Foundry. However, that's not a problem because Cloud Foundry and Bluemix have native support for Heroku buildpacks.

In this example, we're going to use the Heroku buildpack for Clojure unchanged. You might be thinking that this is a little dangerous; Heroku might make a change to their buildpack tomorrow that breaks Bluemix. That is true and a safer bet would be to maintain a fork of the Heroku buildpack and test that fork against Bluemix (or even write a new buildpack completely from scratch that is specifically aimed at Bluemix). For now, though, we're going to live on the edge and use the Heroku buildpack directly.

The buildpack already knows how to compile our Clojure webapp but we need to tell it how to run it or it will default to lein trampoline run, which isn't what we want. We do so by adding a file called Procfile to our project with the following contents:
web: lein trampoline ring server-headless ${PORT}
We also need to specify :min-lein-version "2.0.0" in order to require the use of Leiningen 2. Edit project.clj to look like this:
(defproject hello-clojure "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [compojure "1.1.6"]]
  :plugins [[lein-ring "0.8.11"]]
  :ring {:handler hello-clojure.handler/app}
  :profiles
  {:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
                        [ring-mock "0.1.5"]]}}
  :min-lein-version "2.0.0")
We can now push our app to Bluemix. In this example, I'm giving it the name hello-clojure. If you try this out while my app is still live, you'll need to give your app a different name.
$ cf push hello-clojure -b https://github.com/heroku/heroku-buildpack-clojure.git
Creating app hello-clojure in org cdpjenkins@gmail.com / space dev as cdpjenkins@gmail.com...
OK

Creating route hello-clojure.mybluemix.net...
OK

Binding hello-clojure.mybluemix.net to hello-clojure...
OK

Uploading hello-clojure...
Uploading app files from: /Users/cdpj/hello-clojure
Uploading 1.6M, 773 files
OK

Starting app hello-clojure in org cdpjenkins@gmail.com / space dev as cdpjenkins@gmail.com...
OK
-----> Downloaded app package (904K)
Cloning into '/tmp/buildpacks/heroku-buildpack-clojure'...
-----> Installing OpenJDK 1.6...done
-----> Installing Leiningen
       Downloading: leiningen-2.4.2-standalone.jar
       Writing: lein script
-----> Building with Leiningen
       Running: lein with-profile production compile :all
       (Retrieving lein-ring/lein-ring/0.8.11/lein-ring-0.8.11.pom from clojars)
       (Retrieving org/clojure/clojure/1.2.1/clojure-1.2.1.pom from )
       (Retrieving org/clojure/data.xml/0.0.6/data.xml-0.0.6.pom from )
       (Retrieving org/clojure/pom.contrib/0.0.25/pom.contrib-0.0.25.pom from )
       (Retrieving org/sonatype/oss/oss-parent/5/oss-parent-5.pom from )
       (Retrieving org/clojure/clojure/1.3.0/clojure-1.3.0.pom from )
       (Retrieving leinjacker/leinjacker/0.4.1/leinjacker-0.4.1.pom from clojars)
       (Retrieving org/clojure/core.contracts/0.0.1/core.contracts-0.0.1.pom from )
       (Retrieving org/clojure/pom.contrib/0.0.26/pom.contrib-0.0.26.pom from )
       (Retrieving org/clojure/core.unify/0.5.3/core.unify-0.5.3.pom from )
       (Retrieving org/clojure/clojure/1.4.0/clojure-1.4.0.pom from )
       (Retrieving org/clojure/data.xml/0.0.6/data.xml-0.0.6.jar from )
       (Retrieving org/clojure/clojure/1.2.1/clojure-1.2.1.jar from )
       (Retrieving org/clojure/core.unify/0.5.3/core.unify-0.5.3.jar from )
       (Retrieving org/clojure/core.contracts/0.0.1/core.contracts-0.0.1.jar from )
       (Retrieving lein-ring/lein-ring/0.8.11/lein-ring-0.8.11.jar from clojars)
       (Retrieving leinjacker/leinjacker/0.4.1/leinjacker-0.4.1.jar from clojars)
       (Retrieving org/clojure/clojure/1.5.1/clojure-1.5.1.pom from )
       (Retrieving compojure/compojure/1.1.6/compojure-1.1.6.pom from clojars)
       (Retrieving org/clojure/core.incubator/0.1.0/core.incubator-0.1.0.pom from )
       (Retrieving org/clojure/pom.contrib/0.0.20/pom.contrib-0.0.20.pom from )
       (Retrieving org/clojure/clojure/1.3.0-alpha5/clojure-1.3.0-alpha5.pom from )
       (Retrieving org/clojure/tools.macro/0.1.0/tools.macro-0.1.0.pom from )
       (Retrieving clout/clout/1.1.0/clout-1.1.0.pom from clojars)
       (Retrieving ring/ring-core/1.2.1/ring-core-1.2.1.pom from clojars)
       (Retrieving org/clojure/tools.reader/0.7.3/tools.reader-0.7.3.pom from )
       (Retrieving ring/ring-codec/1.0.0/ring-codec-1.0.0.pom from clojars)
       (Retrieving commons-codec/commons-codec/1.6/commons-codec-1.6.pom from )
       (Retrieving org/apache/commons/commons-parent/22/commons-parent-22.pom from )
       (Retrieving org/apache/apache/9/apache-9.pom from )
       (Retrieving commons-io/commons-io/2.4/commons-io-2.4.pom from )
       (Retrieving org/apache/commons/commons-parent/25/commons-parent-25.pom from )
       (Retrieving commons-fileupload/commons-fileupload/1.3/commons-fileupload-1.3.pom from )
       (Retrieving org/apache/commons/commons-parent/28/commons-parent-28.pom from )
       (Retrieving org/apache/apache/13/apache-13.pom from )
       (Retrieving commons-io/commons-io/2.2/commons-io-2.2.pom from )
       (Retrieving org/apache/commons/commons-parent/24/commons-parent-24.pom from )
       (Retrieving clj-time/clj-time/0.4.4/clj-time-0.4.4.pom from clojars)
       (Retrieving joda-time/joda-time/2.1/joda-time-2.1.pom from )
       (Retrieving org/clojure/core.incubator/0.1.0/core.incubator-0.1.0.jar from )
       (Retrieving org/clojure/tools.macro/0.1.0/tools.macro-0.1.0.jar from )
       (Retrieving org/clojure/tools.reader/0.7.3/tools.reader-0.7.3.jar from )
       (Retrieving commons-codec/commons-codec/1.6/commons-codec-1.6.jar from )
       (Retrieving org/clojure/clojure/1.5.1/clojure-1.5.1.jar from )
       (Retrieving commons-io/commons-io/2.4/commons-io-2.4.jar from )
       (Retrieving commons-fileupload/commons-fileupload/1.3/commons-fileupload-1.3.jar from )
       (Retrieving joda-time/joda-time/2.1/joda-time-2.1.jar from )
       (Retrieving compojure/compojure/1.1.6/compojure-1.1.6.jar from clojars)
       (Retrieving clout/clout/1.1.0/clout-1.1.0.jar from clojars)
       (Retrieving ring/ring-codec/1.0.0/ring-codec-1.0.0.jar from clojars)
       (Retrieving clj-time/clj-time/0.4.4/clj-time-0.4.4.jar from clojars)
       (Retrieving ring/ring-core/1.2.1/ring-core-1.2.1.jar from clojars)
       Compiling hello-clojure.handler
-----> Uploading droplet (68M)

0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
1 of 1 instances running

App started

Showing health and status for app hello-clojure in org cdpjenkins@gmail.com / space dev as cdpjenkins@gmail.com...
OK

requested state: started
instances: 1/1
usage: 1G x 1 instances
urls: hello-clojure.mybluemix.net

     state     since                    cpu    memory         disk   
#0   running   2014-07-01 10:28:01 PM   0.0%   377.9M of 1G   173.6M of 1G   
Bluemix has downloaded the dependencies for the app, built it and started it running.

Finally, we can access the app at the URL http://hello-clojure.mybluemix.net
It's not the most exciting thing in the world but it's a start and we can continue to develop our app as we would with any other Clojure app.

The source code for my little hello-clojure app is available here:

https://github.com/cdpjenkins/hello-clojure

It's also available in IBM's jazzhub DevOps Services in case you are interested:

https://hub.jazz.net/project/cdpj/hello-clojure

My first impressions of Bluemix are good so far. It appears to work and I was able to get my app running without too much bother, which is great. And Bluemix supports a fair few applications and services, some open source and some from IBM. I'll try to write about deploying a slightly more complex Clojure app in a future post.

Sunday, June 29, 2014

Using htmlize to prettify Clojure source code

I have been trying to figure out the best way to include syntax-highlighted source code in a blog post. One solution is to use htmlize in emacs.

Let's say I want to include the following syntax-highlighted code in my blog:
(defn cheese-ston [a b]
  (println a b))
The first step is to install htmlize. I've already got MELPA added so I just install the htmlize package from that.

Next, I want to configure htmlize to output any CSS inline (so that I can cut and paste HTML snippets into Blogger) so I add the following to my init.el and execute it immediately.
(setq htmlize-output-type 'inline-css)
Next, I highlight the code that I want to export and then do:
M-x htmlize-region
This gives me a new buffer with a complete HTML file containing my text, wonderfully highlighted. I can then copy everything within the body of that HTML page into the raw HTML editor in Blogger:
<pre>
<span style="color: #707183;">(</span><span style="color: #a020f0;">defn</span> <span style="color: #0000ff;">cheese-ston</span> <span style="color: #7388d6;">[</span>a b<span style="color: #7388d6;">]</span>
  <span style="color: #7388d6;">(</span><span style="color: #483d8b;">println</span> a b<span style="color: #7388d6;">)</span><span style="color: #707183;">)</span></pre>
Before switching back to the Compose view, I have to ensure that Blogger is not going to quote my HTML so I hit Options on the right and make sure that Show HTML literally is selected.

Previewing the post, I can now see my Clojure code wonderfully highlighted as above.

This whole process is slightly faffy. What I really want is to remove the step where I have to find the right bit of HTML to copy into Blogger's raw HTML editor. One solution would be to figure out how to get htmlize to output HTML snippets (rather than whole documents). Another alternative would be to use org-mode - which works well with htmlize - to output the whole blog post. I'll write more if I get round to trying that.