Update Sept. 4: How to get the latest builds of Clojure & Contrib
Maven is a touchy subject. People tend to have strong opinions about it. But like it or not, it’s the de-facto standard for dependency management in the Java world. Clojure lives in the Java world, so that means we have to live with Maven.
Here are some good things about Maven:
- “Convention over configuration.”
- Plugins are downloaded & installed automatically.
- Handles dependencies of dependencies.
- Declarative configuration, not imperative like Ant.
- Only stores one copy of each JAR, shared by all projects.
Here are some bad things about Maven:
- XML configuration file.
- Verbose command line options.
Doesn’t track latest source code of projects.It does; see comments (Thanks, Tim!)- First run takes forever to download all the plugins.
- Verbose console output.
In my estimation, the good outweigh the bad. And nothing outweighs the huge fact that Maven is already there.
So let’s develop a Clojure app using Maven.
Step 1: Install Maven.
If you don’t already have it, that is. This is pretty easy, just visit maven.apache.org and follow the instructions.
Step 2: Create a new project.
Type the following at the command line:
mvn archetype:generate
Maven will ask a series of questions:
- archetype: At the “Choose a number” prompt, press enter to accept the default project type, maven-archetype-quickstart.
- groupId: Enter a name to identify yourself in the global Maven namespace. All your Maven projects will use the same groupId. This is typically a reverse domain name in the style of Java package names. For example, I could use the groupId com.stuartsierra
- artifactId: Enter a name to identify this specific project in the Maven repository. For example, my-great-clojure-library
- version: Press enter to accept the default, 1.0-SNAPSHOT
- package: Press enter to accept the default, which is the same as your groupId.
- Confirmation: Press enter.
Now you have a directory named my-great-clojure-library containing the skeleton of a new Java project.
Step 3: Configure your pom.xml.
Go into your new project directory and edit the pom.xml file. The basic information will already be filled out.
We can remove the dependency on JUnit, so delete these lines:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency>
Then we need to add the Clojure Maven plugin, which tells Maven how to compile Clojure source code. Before the final </project> tag, add these lines:
<build> <plugins> <plugin> <groupId>com.theoryinpractise</groupId> <artifactId>clojure-maven-plugin</artifactId> <version>1.0</version> <executions> <execution> <id>compile-clojure</id> <phase>compile</phase> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
We also need to add Clojure itself as a dependency of our project (the Clojure Maven plugin does not do this automatically). Inside the <dependencies> tag, add the following lines:
<dependency> <groupId>org.clojure</groupId> <artifactId>clojure</artifactId> <version>1.0.0</version> </dependency>
That’s if you want the official Clojure 1.0.0 release. If you want a cutting-edge version, I’ll explain how in a later post.
Step 4: Delete Java sources.
Your project directory comes pre-equipped with two Java source directories at src/main/java and src/test/java. You can delete both of them, unless, of course, you’re developing a mixed Clojure-Java project.
Step 5: Add dependencies.
If your project does anything interesting, chances are it’s going to depend on some external Java libraries. You can find libraries in the public Maven repositories at mvnrepository.com. Search for a library name, and it will show you the code to put in your pom.xml file.
For example, say we want to use the Apache Commons IO library. At mvnrepository.com, we find the dependency code for this library:
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>1.4</version> </dependency>
And we can add that inside the <dependencies> section of pom.xml.
Step 6: Start coding!
Create the directory src/main/clojure. This is where all your .clj source files will go.
Follow the standard Clojure/Java convention for file names. That is, if you have a Clojure namespace called my.great.library, it should be in a file named src/main/clojure/my/great/library.clj
Step 7: Compile and install.
Run the following command:
mvn install
That will compile all your .clj source files into Java .class files, package them into a JAR, and install that JAR in your local Maven cache. On Unix-like systems, the cache should be at ~/.m2/repository/
Step 8: Live and learn.
There’s a whole lot more to learn about Maven. It’s a very flexible tool, and it can do almost anything. Yes, you will have to write some XML, but it’s really not that much.
Things I hope to cover in future posts:
- Using git submodules to track development versions of Clojure libraries.
- Running tests written in Clojure.
- Including .clj source files in your JAR.
- Creating a stand-alone JAR including all dependencies.
- Setting up a private Maven repository.
I hope this was a reasonable introduction to developing with Maven and Clojure, and that I have shown that Maven isn’t nearly as scary as people make it out to be. I think Maven suffered for a long time from poor documentation, but that’s changing rapidly. I found the (free) book Maven: The Definitive Guide extremely helpful.
Appendix: Complete pom.xml
Here’s the complete pom.xml file for the project I developed in this post:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.stuartsierra</groupId> <artifactId>my-great-clojure-library</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>my-great-clojure-library</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>org.clojure</groupId> <artifactId>clojure</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>1.4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>com.theoryinpractise</groupId> <artifactId>clojure-maven-plugin</artifactId> <version>1.0</version> <executions> <execution> <id>compile-clojure</id> <phase>compile</phase> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
“(maven) Doesn’t track latest source code of projects.” yes it does. Check the “dependency” plugin. It does exactly that. It can even upgrade your deps automatically if you want it to do so.
I have an alternate pom setup on github that does AOT compile and test-is (clojure) testing. If you install this pom and extend from it for your projects (or setup a project that extends from it and then turn it into an archetype pattern).
http://github.com/dysinger/clojure-pom
Sorry I meant to say the “versions” plugin in my 1st post. “versions” and “dependency” plugins are very nice.
Tim D. wrote “I have an alternate pom setup on github…”
Yes, I want to talk about that setup in a future post. Here I was aiming for the simplest, least-intimidating pom.xml I could manage.
Thanks for posting this! I’m glad to see such a clear explanation; I think a lot of people have preconceived notions about Maven that lead them to discount it without really understanding it. I’ve had a lot of luck depending on the clojure-pom parent mentioned above for my projects; it lets you end up with about a third as much XML as your sample. But it’s good to start at the basics.
Very enlightening post!
I’d always been turned off by maven but now I’m going to give it a closer look.
As an aside, using an xml templating engine like Haml might make editing the pom files
even more palatable.
for eg. in jruby,
%foo
%bars
%bar ‘baz’
Is much more pleasant to work with if you have an xml allergy as I do.
Andy B. wrote: “using an xml templating engine like Haml might make editing the pom files even more palatable.”
Yes, you could use any XML generator. Clojure has several, including an implementation of Haml and my own prxml.
Excellent post – I’ve been rather buzy lately and havn’t had a chance to blog my own plugin yet!
Andy: my corkscrew project uses clojure.xml to do this; you get most of the useful bits of Maven exposed, but you only ever have to write s-expressions. I’ve found it quite convenient for my projects.
[…] AltLaw Interview Maven’s Not So Bad: Further Thoughts on Clojure Package Management » Aug 31 […]
Sonatype has been experimenting with alternatives to a xml based pom. They demonstrate a yaml based pom in this blog post: http://www.sonatype.com/people/2009/11/maven-3x-paving-the-desire-lines-part-two/ From my understanding, extending maven to read its pom from a different representation should not be too hard to do.
Then again, the m2eclipse project has a great pom editor, which for a large number of projects eliminates the need to look at the xml underneath.