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:
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:
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>