Building Incanter applications with Leiningen

[NOTE: Make sure to use Leiningen version 1.1.0 or greater. The version can be determined with ‘lein version’]

This is a quick guide to using Leiningen to build applications that use Incanter, based on the very useful post by Zef Hemel, “Building Clojure Projects with Leiningen“.

(Note: the following tutorial uses Unix shell commands (i.e. Mac OS X or Linux), but Roland Sadowski has created a Leiningen PowerShell script for Windows that works with this example: lein.ps1)

First get a copy of Leiningen’s self-installing script:

$ cd ~/bin
$ wget http://github.com/technomancy/leiningen/raw/stable/bin/lein
$ chmod +x lein

and run the self-installer.

$ lein self-install

Now create a project directory called ‘incanter-helloword’ and give it a ‘src’ subdirectory.

$ mkdir incanter-helloworld
$ mkdir incanter-helloworld/src
$ cd incanter-helloworld

Next create the following project.clj file, which includes Incanter in the dependencies section:

(defproject incanter-helloworld "0.1.0"
  :description "The Incanter version of hello world."
  :dependencies [[incanter "1.2.3-SNAPSHOT"]]
  :main hello)

The project will have a single source file called hello.clj, which will contain a -main function that will be called when the jar file is executed. Create it in the incanter-helloworld/src directory:

(ns hello
  (:gen-class)
  (:use (incanter core stats charts)))

(defn -main [& args]
  (view (histogram (sample-normal 1000))))

The program will display a histogram of sample data from a standard normal distribution.

Now download and install all the project’s dependencies, including Incanter, using Leiningen’s ‘deps’ command:

$ lein deps

Compile the project:

$ lein compile

And build an ‘uberjar’ that includes all the Incanter jar files:

$ lein uberjar

And finally, run incanter-helloworld,

$ java -jar incanter-helloworld-standalone.jar

which displays the following histogram.

You can also start a Clojure REPL with a CLASSPATH that includes all your project’s dependencies, including Incanter and its dependencies, using the following command:

$ java -cp 'lib/*' clojure.main

The lein repl command won’t work correctly, since it uses Clojure 1.1 and Incanter requires Clojure 1.2. You can also use the repl and swank scripts available in the scripts directory of the Incanter distribution on Github.

30 responses to “Building Incanter applications with Leiningen

  1. This is really slick – thanks for the example!

  2. 1 required artifact is missing.

    for artifact:
    org.apache.maven:super-pom:jar:2.0

    from the specified remote repositories:
    clojars (http://clojars.org/repo/),
    clojure-snapshots (http://build.clojure.org/snapshots),
    central (http://repo1.maven.org/maven2)

    • What part of the build did this error message appear? I can’t reproduce it.

      I have added the above simple project example to the Github repository under examples/blog/projects/incanter-helloworld. Follow the instructions in the README file in the project directory and let me know if this error occurs again.

      David

    • I think this may be because clojure-alpha is now clojure-master…

      • Good catch! I actually think the original problem occurred before the renaming, but now there is a new problem. I think the renaming is going to cause problems for every project on Clojars that depends on clojure-alpha until new poms can be uploaded.

        I just sent an email to the Clojure list to see if we can have the old name linked to clojure-master as a short term fix.

  3. Thanks! I’ll try again. The command was “lein deps”

    I’m new to java so the complexity of build and deployment is kind of daunting.

    • I’ve forgotten to do ‘lein deps’ or the ‘ant deps’ more than once myself :)

      JVM builds and deployments can be daunting, but Leiningen has been a great improvement. Good luck.

  4. This failed for me on OSX Snow Leopard. I pulled the example code from the incanter github @ examples/blog/projects/incanter-helloworld. The same error occurred.

    lein deps works fine.

    > lein compile
    Downloading… (x3)
    Compiling hello
    [null] java.lang.Error: Cannot load com.apple.laf.AquaLookAndFeel (hello.clj:1)

    • Yep, that’s a recently introduced bug with Leiningen that, I think, was caused by the recent Java update on Mac OS X. The Leiningen community is aware of it, and hopefully will have a fix out soon.

      Here’s a thread on the Leiningen Google group discussing the bug, and some possible fixes, in the context of Swank.

  5. Works beautifully on Leopard, if you

    – correct a formatting error that puts & in the source code when & would be better
    – follow the HACKING guideline for the MacOSX Leiningen fork

  6. When I try the above, lein uberjar fails with

    “Wrong number of arguments to task uberjar.”

    What additional arguments does uberjar need? How can I fix this? I am using leiningen 1.0.1 on Windows.

    • Are you doing the following lein commands:
      $ lein deps
      $ lein compile
      $ lein uberjar
      $ java -jar incanter-helloworld-standalone.jar

      I didn’t get any errors.

      • Yes. lein deps and lein compile went fine. lein uberjar and lein jar both complain about a wrong number of arguments. Interestingly an incanter-helloworld.jar is created nonetheless. But this jar contains only META-INF and nothing else.

        I have to add that I am not very experienced in building Java applications, so may be I am missing something… Could this be a Windows issue?

    • I’m trying to reproduce your error, but I’m not having any luck. Can you post the exact command and error message?

      Although, I am running into problems when I try to run the app, after creating the uberjar. Leiningen has been undergoing a lot of changes lately, and that may be the cause of both of our problems.

      • Thanks for your help!

        e:\Users\felix\Documents\Clojure>mkdir incanter-helloworld
        mkdir incanter-helloworld

        e:\Users\felix\Documents\Clojure>cd incanter-helloworld
        cd incanter-helloworld

        e:\Users\felix\Documents\Clojure\incanter-helloworld>mkdir src
        mkdir src

        I now created project.clj and src/hello.clj exactly described in the blog post. Then:

        e:\Users\felix\Documents\Clojure\incanter-helloworld>lein deps
        lein deps
        [copy] Copying 17 files to e:\Users\felix\Documents\Clojure\incanter-helloworld\lib

        e:\Users\felix\Documents\Clojure\incanter-helloworld>lein compile
        lein compile
        Compiling hello

        e:\Users\felix\Documents\Clojure\incanter-helloworld>lein uberjar
        lein uberjar
        Wrong number of arguments to task uberjar.

    • Interesting, it looks like you’re on Windows, are you using the lein Bash script with Cygwin, or are you using the Python or PowerShell versions of it?

      Have you tried the lein example at Zef’s site (http://zef.me/2470/building-clojure-projects-with-leiningen), it is even simpler and may help eliminate variables to debug.

      If you can’t run that example, I would try to use the latest version of lein from github (it takes a bit more work to set up than the stable version, which can use self-install).

      If that doesn’t help, the best place to get Leiningen help is its Google Group: http://groups.google.com/group/leiningen

      David

  7. Thanks for a useful post.
    I’m trying to run on Fedroa, and get the followng error in the deps part:
    Unable to resolve artifact: Missing:
    1) org.clojure:clojure-contrib:jar:1.2.0-master-SNAPSHOT

    Any idea?
    Thanks

    • The version number scheme for clojure-contrib has recently changed; it no longer contains the word ‘master’ in it. I have updated the post to point to a newer version of Incanter that uses the correct name. So just update your project.clj file to use version 1.2.3-SNAPSHOT, or later, of Incanter:

      [incanter “1.2.3-SNAPSHOT”]

      Good luck,
      David

  8. Thanks David
    appreciate you help.
    This did made a change, now the following artifact is missing:

    org.clojure:clojure-master:jar:1.1.0-alpha-SNAPSHOT

    BTW, this maybe the result of my slow connection, not the procedure at all.

  9. problem solved!

    I jump directly to the incanter-webapp example and use the following project file:
    (defproject incanter-webapp “1.0.0-SNAPSHOT”
    :description “A simple Incanter web-app.”
    :dependencies [[incanter “1.2.3-SNAPSHOT”]
    [compojure “0.3.2”]]
    :main simple_web_app)

    Please note that the git version of the example at


    ;; This code is from the following blog post:
    ;; Building a simple Clojure web application with Incanter, Compojure, and Leiningen
    ;; http://incanter-blog.org/2009/11/29/incanter-webapp/
    (ns simple_web_app
    (:gen-class)
    (:use [compojure]
    [compojure.http response]
    [incanter core stats charts])
    (:import (java.io ByteArrayOutputStream
    ByteArrayInputStream)))
    ;; Pass a map as the first argument to be
    ;; set as attributes of the element
    (defn html-doc
    [title & body]
    (html
    (doctype :html4)
    [:html
    [:head
    [:title title]]
    [:body
    [:div
    [:h2
    [:a {:href "/"}
    "Generate a normal sample"]]]
    body]]))
    (def sample-form
    (html-doc "sample-normal histogram"
    (form-to [:get "/sample-normal"]
    "sample size: " (text-field {:size 4} :size)
    "mean: " (text-field {:size 4} :mean)
    "sd: " (text-field {:size 4} :sd)
    (submit-button "view"))))
    (defn gen-samp-hist-png
    [request size-str mean-str sd-str]
    (let [size (if (nil? size-str)
    1000
    (Integer/parseInt size-str))
    m (if (nil? mean-str)
    0
    (Double/parseDouble mean-str))
    s (if (nil? sd-str)
    1
    (Double/parseDouble sd-str))
    samp (sample-normal size
    :mean m
    :sd s)
    chart (histogram
    samp
    :title "Normal Sample"
    :x-label (str "sample-size = " size
    ", mean = " m
    ", sd = " s))
    out-stream (ByteArrayOutputStream.)
    in-stream (do
    (save chart out-stream)
    (ByteArrayInputStream.
    (.toByteArray out-stream)))
    header {:status 200
    :headers {"Content-Type" "image/png"}}]
    (update-response request
    header
    in-stream)))
    ;; define routes
    (defroutes webservice
    (GET "/"
    sample-form)
    (GET "/sample-normal"
    (gen-samp-hist-png request
    (params :size)
    (params :mean)
    (params :sd))))
    ;; define main function that starts webserver
    (defn -main [& args]
    (run-server {:port 8080}
    "/*" (servlet webservice)))

    have the namespace:
    simple-web-app
    but the post state correctly it should be
    simple_web_app

    Thanks again for a great work.
    This will keep my busy for days and days!

    • I’m glad you solved the problem, and I fixed the namespace on the gist, thanks for pointing that out.

      David

  10. I tried your instructions here and it all works well, java -jar …. brings up the window with the histogram.

    So then why is it that when I run lein repl, then try this in the repl I get the error?

    user=> (ns hello (:use (incanter core stats charts)))
    java.lang.VerifyError: class incanter.core$loading__4347__auto__ overrides final method meta.()Lclojure/lang/IPersistentMap; (NO_SOURCE_FILE:1)

    And if I try it a second time, I get this:

    hello=> (ns hello (:use (incanter core stats charts)))
    java.lang.NoClassDefFoundError: Could not initialize class incanter.core__init (NO_SOURCE_FILE:2)

    It’s like lein repl doesn’t use the appropriate classpath??

    lein version is 1.1.0 here.

  11. Thanks!

    It’d be nice if “lein new myproj” would create a template with those scripts you mention and a project.clj with incanter already typed in, etc…

    • Haha, yes it would be nice, but Leiningen is a general Clojure build tool, and not everybody needs Incanter in every Clojure project :-)

      Once Clojure 1.2 is released (in the next couple weeks or so), Leiningen will likely upgrade its version of Clojure and ‘lein repl’ will work correctly.

  12. You kidding? Everybody needs Incanter!

    Say… the same problem occurs even when I do “lein swank”. I had to copy your script/swank to get that to work too.

    Probably doesn’t matter, but I notice in your project.clj that you are using [leiningen/lein-swank “1.1.0”], and it seems that’s “deprecated” now as I see from here http://clojars.org/search?q=swank

    I’m using [swank-clojure “1.2.1”] and I got the error still.

  13. This worked for me almost on the first try. Thanks! Looks lein has change a minor detail. I had to change the aot class to include “.core” instead of just the class name (notice the last line in the below project.clj)

    [~/clojure/hello]$ cat project.clj
    (defproject hello “1.0.0-SNAPSHOT”
    :description “FIXME: write description”
    :dependencies [[org.clojure/clojure “1.2.1”]
    [incanter “1.2.3-SNAPSHOT”]]
    :main hello.core)

  14. lein uberjar results in:

    “Release versions may not depend upon snapshots.
    Freeze snapshots to dated versions or set the LEIN_SNAPSHOTS_IN_RELEASE environment variable to override.”

    tried on two machines. Same result.

  15. I’m running my clojure project using docker on windows. When I try use incenter it gives java.awt.HeadlessException. Is there a way to solve this
    ?

Leave a comment