This post explains how to write and deploy a Clojure application
with Eclipse and Maven, the build automation tool for Java that is
bundled in the Eclipse package “Eclipse IDE for Java”.

In comparison to other articles about Clojure and Maven, this
article is Eclipse centric and task focused. We will:

create and structure a Maven project in Eclipse.

understand the Maven life-cycle, and tweak it to compile, test
and package our Clojure codes.

configure Eclipse so that we can trigger all the phases of the
Maven life-cycle from the GUI.

Maven did not start as an Eclipse tool. It is a standalone piece
of software, which exists separately from Eclipse and is usable from
inside Eclipse via the m2e plugin. Maven is itself extensible with
plugins, one of which allows it to compile Clojure code.

For now, we will not use Counterclockwise, the Clojure plugin for
Eclipse. This is on purpose, in order to help us understand what we can
do without it.

We will start by writing the code and creating the project. Then
I'll explain about Maven. From there we will configure the project and
we will finally run and package it.

The resulting project can be found on github.
Any trouble with pom.xml during
the course of this tutorial, have a look at the final
pom.xml.

The Code

Our application is a standalone command-line program that
exclaims: "Yes, I wrote Clojure in Eclipse and tested/ran/deployed
it with Maven!" (a bit of pride never hurts).

The project shall consist of four Clojure codes.

Namespace chaomancy.maven is the
application.

(ns chaomancy.maven

(:gen-class))

(defn exclaim []

"Yes, I wrote Clojure
in Eclipse and tested/ran/deployed it with Maven!")

(defn -main [& args] (println (exclaim)))

Namespace chaomancy.test.maven
contains a single test, which uses the clojure.test
toolset:

(ns chaomancy.test.maven

(:use chaomancy.maven)

(:use clojure.test))

(deftest

test-maven-msg

(is (= (exclaim)

"Yes,
I wrote Clojure in Eclipse and tested/ran/deployed it with
Maven!")))

the script maven-run.clj is a
basic launcher for the application

(use '(chaomancy maven))

(-main)

the script maven-repl.clj contains
the instruction we want to be executed when we start the REPL.

(use '(chaomancy maven))

(use '(chaomancy.test maven))

(use '(clojure test))

(println "Namespaces loaded. You can now write some
Clojure.")

Installing Eclipse

If you don't have Eclipse installed yet, then you can just download and install the
“Eclipse IDE for Java Developers” (not the EE version). Install it,
fire it up and go to Window > Open
Perspective > Other..., pick Java
(default) and off you go. If you are a Linux user, do not install
Eclipse via your distribution's software installation center; get it
from the Eclipse website instead (speaking from painful experience
here).

If you already have Eclipse installed, update it to the latest version,
and check whether you have plugin
m2e - Maven Integration for Eclipse installed
already. If not, then do install it (version 1.0 or later)

Creating the project

Let us create our project and place our code in it.

Maven Project

Execute File > New > Project...
and choose Maven Project. In the New Maven Project window:

Tick Create a simple project
and press Next >

Enter the GroupId, ArtifactId and Version
(mandatory, see their definitions)
as well as the name and description (optional). ArtifactId will be used as project
identifier in Eclipse. Also notice that the packaging is set to jar.

GroupId: chaomancy

ArtifactId: clj-maven-tutorial

Version: 0.0.1-SNAPSHOT

Name: clj-maven-tutorial

Description: Clojure in Eclipse with Maven

Press Finish.

Tick Build automatically off in the Project menu, so that it doesn't slow
things down when we declare dependencies and configure plug-ins later.

Source Folders and Clojure Files

The project is structured for Java codes. Let us keep the Java
folders and create Clojure folders alongside them.

Using command File > New >
Source Folder, create the following source folders in the project:

src/main/clojure

src/test/clojure

src/main/scripts

Using the commands File > New >
Package and File > New > File,
create the following packages and source files in the source folders we
just created.

In src/main/clojure

create a package called chaomancy

in this package, create a file called maven.clj and copy the code of
namespace chaomancy.maven into
it.

In src/test/clojure

create a package called chaomancy.test

in this package, create another file called maven.clj and copy the code of
namespace chaomancy.test.maven
into it. See the pattern regarding package and file name? The word
after the last dot in the namespace should be the name of the source
file, and everything before the last dot is the name of the package
in which the source file should be placed.

In src/main/scripts

create files maven-repl.clj
and maven-run.clj and copy
their respective codes into them.

Now we are ready for some configuration action.

Planning the configuration of Maven and Eclipse

How Maven Works

The pom.xml file tells Maven how
it should handle your project. Double click on it. See the actual
content of the file in the pom.xml tab
and how Maven interprets it in the Effective
POM tab. Maven's default implicit POM is much larger than what our pom.xml
says. Whatever we enter in pom.xml will override or complement
what Maven does by default. An empty pom.xml
doesn't mean that Maven won't do anything: it means that we haven't
overriden any of Maven's defaults.

Have a look at the Effective POM, and in particular:

<repositories> These are the
online repositories in which Maven will search for libraries. We will
provide new ones for clojure codes.

<sourceDirectory> and
following lines: Where Maven expects to find files / intends to write
files. We will not need to override this, but be aware of them.

Maven splits the development lifecycle of a project in phases.
We'll look at these in a second, but let's remain in the POM for now to
look at one of them: the test phase.

Search for maven-surefire-plugin
in the effective POM. What we see is that the test command (a “goal” in Maven linguo)
of the Surefire plugin is bound to Maven's test phase. How does this
work? When Maven runs the test phase, it goes through pom.xml,
looks for all plugins that have an element <phase>test</phase>,
and runs the indicated goal/command of the plugins it finds. In this
case, Maven knows that, for the test phase, it needs to execute the test goal of the Surefire plugin
(abbreviated surefire:test)

Exercise: What does Maven run by default at the compile phase? The compile goal of maven-compiler-plugin (abbreviated compiler:compile).

A bit cumbersome, but we can now guess how we will tell Maven to
compile clojure code. It will be by adding a new plugin that knows how
to handle Clojure and by binding some of its goals to some of Maven's
phases. Cool?

Now let's look at the overall project lifecycle in Maven. Here is a
list of Maven's phases for a jar deployed project (see the introduction
to Maven's build lifecycle). I encourage you to locate how each of
these phases is bound to a plug-in in the effective POM, and to check
that it matches the table below.

Phase

What it does

plugin:goal identifier

process-resources

copy and process the resources into the destination
directory, ready for packaging

resources:resources

compile

compile the source code of the project

compiler:compile

process-test-resources

copy and process the resources into the test destination
directory.

resources:testResources

test-compile

compile the test source code into the test destination
directory

compiler:testCompile

test

test the compiled source code using a suitable unit testing
framework

surefire:test

package

take the compiled code and package it in its distributable
format, such as a JAR

jar:jar

install

install the package into the local repository, for use as a
dependency in other projects locally

install:install

deploy

copies the final package to the remote repository for sharing
with other developers and projects

deploy:deploy

An important convention here is: when you call a phase in Maven,
all the prior phases are executed too. If you call the deploy phase
(the last one in the list), then all phases will be run; Maven will
compile, re-test and re-package the project before deploying.

In practice and by default, Eclipse only allows you to call
phases test and install via the GUI, but we can define launchers for
other phases or for specific goals by ourselves, which we will merrily
do.

Clojure-maven-plugin

Clojure functionalities for Maven are provided by the clojure-maven-plugin, which provides
Clojure related functions, or goals. They can be bound to phases of the
Maven life-cycle or can be called independently.

package phase: package our application both as a jar file
(with jar:jar) and as a
distribution (with assembly:single).
I haven't explained about this yet; will do further down.

We will also create launchers in Eclipse to:

call the package phase (a nice to have for non production
projects such as this one).

Execute tasks that are not part of Maven's vanilla lifecycle:

Launching a REPL and initialising it with the maven-repl.clj script.

Launching the maven-run.clj
script.

So our bespoke lifecycle, with our changes and additions in red,
will be:

Phase

plugin:goal identifier

Callable via GUI

process-resources

resources:resources

-

compile

compiler:compile clojure:compile

-

process-test-resources

resources:testResources

-

test-compile

compiler:testCompile

-

clojure:repl

Let's add it

clojure:run

Let's add it

test

surefire:test clojure:test

Already there

package

jar:jar assembly:single

Let's add it

install

install:install

Already there

deploy

deploy:deploy

-

I visually inserted clojure:repl
and clojure:run in the life-cycle.
We will make them call the phases that are before them in the list, but
they will be “dead-ends”, in the sense that they won't contribute to
the later phases of the lifecycle.

Still with me? Off we go... now it's just action.

Configuring Maven

Let us populate pom.xml. Most of
our changes will be done directly in the XML file. But I will use GUI
helpers when they exist, for they help working library versions out.

Repositories

First, add the following block to pom.xml.
This tells Maven where Clojure libraries can be found.

<repositories>

<repository>

<id>Clojure
Releases</id>

<url>http://build.clojure.org/releases</url>

</repository>

<repository>

<id>Clojars</id>

<url>http://clojars.org/repo</url>

</repository>

</repositories>

Dependencies

The only dependency we will declare here is the Clojure language
library.

Move to to the Dependencies
pane of the POM editor and click Add...
(or right-click on the project and go to Maven
> Add Dependency).

Type clojure, which
brings all the libraries that Maven found in the repositories and that
have clojure in their name.
Scroll to and expand org.clojure
clojure, and pick the version of clojure you want (I am picking 1.3.0 [jar]).

This is the place you should come back to when you want to add
other libraries to your project later (e.g. incanter).

Go see the content of pom.xml and
notice the <dependencies> block that
was added.

Note that Maven will place downloaded libraries into a local
repository. This way it won't need to go online when you need them next
time.

Registering the Clojure plugin and binding it to Maven's phases

right-click on the project in the Package
Explorer and go to Maven > Add
Plugin.

Type clojure, which brings com.theoryinpractice clojure-maven-plugin.
Expand it and pick the latest version.

The nice thing with using GUI tools is that they make you aware
of the latest releases of libraries and plugins.

Now, there are two of the plug-in's goals (commands) that we want to
bind to Maven phases. The compile
goal to the compile phase and the test
goal to the test phase. In order to do so, edit the pom.xml:

<plugin>

<groupId>com.theoryinpractice</groupId>

<artifactId>clojure-maven-plugin</artifactId>

<version>1.3.9</version>

<executions>

<execution>

<id>clojure-compile</id>

<phase>compile</phase>

<goals>

<goal>compile</goal>

</goals>

</execution>

<execution>

<id>clojure-test</id>

<phase>test</phase>

<goals>

<goal>test</goal>

</goals>

</execution>

</executions>

</plugin>

For tests, the plugin uses a default test script, which you can change
to your own. See the documentation
.

Binding packaging plugins to Maven's package phase

In the effective POM, you can see that the maven-jar-plugin is bound by default to
Maven's package phase. This plugin will create a jar file, dependencies
excluded. Let us tell the plugin that the executable class of our
project is chaomancy.maven. For
this, add the following lines to pom.xml
(note: for version number, use the version number that is in the
effective POM).

<plugin>

<artifactId>maven-jar-plugin</artifactId>

<version>2.3.1</version>

<configuration>

<archive>

<manifest>

<mainClass>chaomancy.maven</mainClass>

</manifest>

</archive>

</configuration>

</plugin>

This is all good, but I would also like to create a distribution of the
application. This will be a jar with dependencies included (i.e. the
bits of clojure that are used by our code) that we can execute on any
machine that has java on it. This is something for the maven-assembly-plugin. My prefered
option is to bind it to the package phase too. We also need to tell it
that the executable class in our project is chaomancy.maven. I omitted the plugin
version; at the time of writing, Maven seems to know how to manage
version on its own.

<plugin>

<artifactId>maven-assembly-plugin</artifactId>

<executions>

<execution>

<phase>package</phase>

<goals>

<goal>single</goal>

</goals>

</execution>

</executions>

<configuration>

<archive>

<manifest>

<mainClass>chaomancy.maven</mainClass>

</manifest>

</archive>

<descriptorRefs>

<descriptorRef>jar-with-dependencies</descriptorRef>

</descriptorRefs>

</configuration>

</plugin>

We are done with binding plugins to the Maven life-cycle.

Setting scripts paths

Before we go and setup launchers in Eclipse, let's go back to the <plugin> block of the clojure-maven-plugin and tell the repl goal and run goal what scripts to use. Just
before the closing </plugin> tag,
insert:

<configuration>

<replScript>src/main/scripts/maven-repl.clj</replScript>

<script>src/main/scripts/maven-run.clj</script>

</configuration>

Later, have a look at all the plugin's configuration options in the documentation

We are done with editing the POM. If it is a bit messy, open the code
and execute Source > Format to make
it all nice and indented.

Tidying up

Before going further, we need to deal with a peculiarity in the
way Eclipse handles Maven plug-ins, and with a small Eclipse bug with
source folders.

Open the Problems window and
notice errors there, as well as the error flags in the Package Explorer (white x on red square).
This is because the maven-clojure-plugin
doesn't implement some functions that Eclipse is expecting. We don't
really care about this (as far as I am aware), so we are going to ask
Eclipse to ignore this.

In the Problems window,
expand Errors and Warnings. right-click "plugin
execution not covered...", select Quick
Fix, and choose "permanently
mark goal compile in pom.xml as
ignored"
. The error message should disappear; delete it (right-click) if it
doesn't.

The POM now contains a new big <pluginManagement>
block, which tells Eclipse to ignore what it doesn't like with the maven-clojure-plugin. Not very elegant,
but well...

You might also see an error "Project
configuration is not up-to-date". If so then right-click on it, select
Quick Fix and then press Finish. This probably made Eclipse loose
track of your source folders. If so, recreate them using command File > New > Source Folder:

src/main/clojure

src/test/clojure

src/main/scripts

This will make make source folders, packages and scripts reappear
where they should.

Pfew. Done with Eclipse's idiosyncrasies, and your pom.xml
should look
like this.

Creating Eclipse Launchers

What I call launcher is called a Run
Configuration in Eclipse. Let's create some.

Go to the menu Run > Run
Configurations...

Click and highlight Maven Build
and press the New icon (the blank
document with a + in the top right corner), and create new
configurations:

The first one to run the package phase

Name: maven-tutorial
package

Base Directory: press Browse
Workspace and choose the project, which should result in ${workspace_loc:/clj-maven-tutorial}

Running, testing and packaging the project

Let's have fun now. The first time you perform a task, Maven will spend
a few seconds to download the packages it needs from online
repositories, so don't worry if the log is cluttered at first. This is
why we turned Build Automatically off.
Once you've done something a first time, redo it and go through the
uncluttered log to see what happened.

Let's start by running our application via the maven-run.clj script:

Execute Run > Run As...
> Maven Build and select maven-tutorial
run

See in the log how all phases from process-resources to
compile were called, after which our script was executed ("Yes, I
wrote Clojure in Eclipse and tested/ran/deployed it with Maven!")
and a report was produced. Remember that we configured the run
configuration to call the compile
phase first and then clojure:run
(in this case, calling clojure:run
only would have run the clojure code fine; but I am not sure resources
processing and java compilation would have taken place).

Now let's test our code:

Execute Run > Run As...
> Maven test

Notice the Maven phases that were executed, and look at the
test traces in the log. There are two of them: the first is the one
for Java codes in the project (none). The second is the one for
clojure codes. All successful. Go and change the test to make it fail
to see how it looks like in the log.

Next Let's start a REPL, which we configured to be initialised with maven-repl.clj:

Execute Run > Run As...
> Maven Build and select maven-tutorial
REPL

And play. Try things such as (exclaim)
or (run-tests
'chaomancy.test.maven)

See in the log how all phases from process-resources to
testcompile were called, after which our initialisation script was
executed. When we created the run configuration for this, we didn't
tell that we wanted test-compile to be executed prior to goal clojure:repl. The clojure-maven-plugin
did this on its own.

The log shows that your code was retested and then packaged as
two jars, created by the two plugins we bound to the package phase
earlier.

Open a terminal window, go to the target directory in the
project, and type:

java -jar
clj-maven-tutorial-0.0.1-SNAPSHOT-jar-with-dependencies.jar

which should display "Yes, I wrote some Clojure in Eclipse
and tested/ran/deployed it with Maven!".

Mission accomplished.

Finally, we can install the application in our local Maven
repository to make it available to ourselves for future projects

Execute Run > Run As...
> Maven install

We're done.

What Next?

I hope that this article gave you the confidence you can write,
test and deploy whatever you want with Eclipse and Maven. Obiously,
such an infrastructure makes sense for projects that are a tad bigger
than the one we just created!

You can get away with skipping some of the configuration steps we
took. But my goal has been to make you take control of Maven, its
life-cycle, its plug-ins, so that you can make Maven dance for you. You
should now also be able to experiment with launching other phases (I
left deploy for you) and other commands/goals of the
clojure-maven-plugin. We haven't looked into what Maven does during its
resource phases, which I leave you explore.

Now, our clojure codes were just flat files in Eclipse. No code
highlighting, and no Clojure orientated productivity gadgets. This is
where Counterclockwise,
the indispensable Clojure plugin for Eclipse, comes in. This is the
object of the next tutorial: Part
2: Counterclockwise + Maven.

An educated person does not take lightly to being ignored. He has the strength to stand up for his rights and does not shy away when someone tries to snatch what truly is his. He has the ability to voice out his opinion.

One small problem so far: you've spelled "theoryinpracitse" as "theoryinpractiCe" (with 'c' instead of 's') in one place. For some reason eclipse didn't suggest anything, when I tried to add the plugin as you've described, so I tried by adding the groupId and artifactId manually, and got "bitten".

The "Plugin execution not covered..." message you're getting is from m2e. This is because it doesn't know whether to run these goals during *incremental* compilation or not. That lifecycle-mapping section tells m2e to ignore m-clojure-p. I guess this is not a problem for you because you explicitly call Maven. However, the goal of m2e is to allow seamless incremental build in Eclipse, same as you get if you aren't using Maven.

To enable this m2e needs a little help. Right now you need to install an m2e-clojure integration plugin, which as far as I know hasn't been written yet. When m2e 1.1 is released a small piece of XML included with the m-clojure-p should be sufficient.

If it wouldn't be too much work for them I might ask the m-clojure-p author to add that.

Bear in mind I'm just another user so I could be wrong about any of the above. I looked into the issue for m-less-p once already, which is how I've learnt about it.

@ivant: corrected, many thanks! I hope you enjoy working with the Maven/Clojure combination!

@Alexis: Many thanks for figuring out and for articulating what is going on! Makes perfect sense, and I can see how much nicer it would be if the issue were fixed as you indicate. Thanks for having flagged the issue to mark@talios. Looking forward to this being fixed! :-)

Excellent article - this was a Clojure enabler for me. Every book on clojure should start with a chapter like this. With the techniques described in your article one is able now to produce artifacts which can be used by a Java project. Very useful, thanks again.