March 30, 2012

Using Java from Clojure in plain !@#%^%ing language, with some simple goddamn examples.

This is something I found a bit lacking in the official docs, or maybe I just didn't look hard enough.

Basically, I'm not that smart, and after 2 weeks, I have more experience in Clojure than I ever did in Java (first-year CS courses don't count). Whether by lack of resources, or general dumbness, I had an unreasonably difficult time coming across the knowledge that I am about to impart. I hope it helps any of my peers in the same position.

Basics

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
; Short answer: use import to import a given class
; In java:
; import java.io.File;
(import java.io.File)
 
; Preferred: put it in your ns
(ns my-namespace
(:import java.io.File))
 
; Instantiate an object with an argument
;
; File f = new File("C:/file.txt");
;
(File. "C:/file.txt")
 
; Use some methods
;
; File f = new File("C:/file.txt");
; if(f.exists()){
; return f.getName();
; }else{
; return null;
; }
;
(let [f (File. "C:/file.txt")]
(if (.exists f)
(.getName f)
nil))
 
; Use a static method, with arguments
;
; File.createTempFile("prefix", "suffix", "C:\Temp");
;
(File/createTempFile "prefix" "suffix" "C:\Temp")

Using Externals (with lein)

Java doesn't have quite everything built in, although it sure does its best. Here is the process I took to get pegdown, a very nice Markdown renderer (it does a bunch of other stuff too), working.

1. Add the scalatools repository to project.clj

I was actually doing this a different (stupid) way before, which you can still read about for posterity below. This way is better.

First, you need to inform lein, via project.clj, of the maven repository containing the tool you want.

:repositories {"scalatools" "http://scala-tools.org/repo-releases/"}

2. Add the dependency, formatted correctly

The formatting was a pain to get right because I couldn't find it explained anywhere. Here is the bit of knowledge that I couldn't find:

<group-id>/<name> "<version>"

So, for pegdown, it was:

[org.pegdown/pegdown "1.1.0"]

If you don't write it just right, maven will not find it. What a pain.

3. Use the dependencies

1 2 3 4 5 6
(ns pegdown-test
(:import org.pegdown.PegDownProcessor))
 
(defn markdown-to-html [md]
(let [pd (PegDownProcessor.)]
(.markdownToHtml pd md)))

The dumb way

I left this in for transparency. All below might be useful still, but the way above is better -- it resolves dependencies and all that.

----------------

1. Track down dependencies

Don't skip this, no matter how dearly you believe in your heart that you can. Read the docs for pegdown, and it lists a dependency on parboiled. Go to parboiled, and you'll find that it has a dependency on asm. Asm, blessedly, has no dependencies.

2. Round up jars

I needed 4:

  • pegdown-1.1.0
  • parboiled-core-1.0.2
  • parboiled-java-1.0.2
  • asm-all-3.3.1

3. Install jars, with help from lein and maven2

You need maven2. Install it somehow.

Lein can't install jars for you, or if I can, I don't know how. But, if you give it something that it can't find, it'll spit out the mvn command that you need to install it! Since I don't know anything about maven, this is what we will use.

First, add those as dependencies in your project.clj. The format goes like so:

[pegdown "1.1.0"]
    [parboiled-core "1.0.2"]
    [parboiled-java "1.0.2"]
    [asm-all "3.3.1"]

Once you do that, run

> lein deps

This will result in an error, during which the mvn command you need will be suggested it to you. Copy/paste it, fill out the file= arg with the path to the appropriate jar, and run the command. Do it for all 4, and voila! The jars are now part of your project, and you can use them with impugnity.

Easy! Also, if anyone has a guide to maven, preferably a way to use it without adding 20 command-line arguments, I'd love to hear about it.