ClojureをReal Worldで使うためにMavenと連携する

この記事は、Clojure Advent Calendar 2011の2日目の記事です。

Clojureを導入するには

Clojureをプロジェクトで使いたい、という現場の要望は結構多いかと思う。
安心してほしい。
フロントエンドにいきなりClojureを使うのはさすがに通りにくいと思うが、 バッチ等をJava(もしくはJVMで動く何か)で動かしているならClojureでそのJavaのバッチを置き換えられる。
置き換えるのはとても簡単だし、周囲の理解や上司の理解も得られるのではないだろうか。
何と言ったってコンパイルしてしまえばプロダクトコードは関係なくなる。
動いていてほしいのはバッチで、コードを気にするのはあなたで、あなたが快適に仕事ができるなら今すぐJavaをすててClojureでコードを書こう。

開発はleiningenで

Clojureで何か作るときは基本はleiningenを使うのが一番いいと思う。
leiningenについての記事はいろいろあるのでここでは割愛する。
しかし、このleiningenの環境を実行環境に整えるは結構面倒くさいと思っている。
Mavenならバイナリをダウンロードしてすぐに使えるのが強力な点だ。
それに、もしあなたプロジェクトでJavaを使っているならMavenはすでに使っていることだろう。

Clojure Maven Pluginの導入

Clojureは基本的にJavaだ。 JVMの心を理解しないといけないのはJavaと一緒である。
MavenJVM上で動作するツールとして非常に親和性が高いと思っている。 Clojureで何か作業するにしてもJavaで作られたライブラリを使いたいことは多々ある(今後Clojureが進化してClojureだけでいろいろできたらいいとは思う)。
Mavenを使っていればMaven上にあるJavaのモジュールをすぐに取り込めたりもできる。
そこで、ClojureMavenで動かすためにClojure Mavenプラグインを導入しよう。

まずは、lein pomでproject.cljからpom.xmlを生成する。

$ lein pom

そして、生成されたpom.xmlにClojure Mavenプラグインの依存関係を追加する。

<build>
  <plugins>
    <plugin>
      <groupId>com.theoryinpractise</groupId>
      <artifactId>clojure-maven-plugin</artifactId>
      <version>1.3.8</version>
      <configuration>
        <sourceDirectories>
          <sourceDirectory>src</sourceDirectory>
        </sourceDirectories>
        <mainClass>example.run</mainClass>
      </configuration>
      <executions>
        <execution>
          <id>compile-clojure</id>
          <phase>compile</phase>
          <goals>
            <goal>compile</goal>
          </goals>
        </execution>
        <execution>
          <id>test-clojure</id>
          <phase>test</phase>
          <goals>
            <goal>test</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

プロジェクトの設定

Clojure Mavenプラグインでは、プロジェクトの構成に柔軟に対応するためconfiguration要素で設定できる。

ソースコードの場所指定

Maven Clojureプラグインではデフォルトで、src/main/clojure/*.clj、src/test/clojure/*.cljをコンパイルしようとする。 leiningenのlein newで生成したプロジェクトは、デフォルトでsrc/{proj}/core.clj、src/{proj}/test/core.cljを作成し、core.cljでnamespaceを、

(ns {project}.core)
(ns {project}.test.core)

のように設定している。
ソースコードの場所を指定するディレクトリはパッケージのディレクトリを指定することになるので、leiningenのデフォルトに対応しようとすると、

<configuration>
  <sourceDirectories>
    <sourceDirectory>src</sourceDirectory>
  </sourceDirectories>
  <testSourceDirectories>
    <testSourceDirectory>test</testSourceDirectory>
  </testSourceDirectories>
</configuration>

と設定する。

もちろん自由にプロジェクトの構成は変えていい。
個人的にClojureプロジェクトでおすすめなディレクトリ構成は次のようにする。

<configuration>
  <sourceDirectories>
    <sourceDirectory>src/main/clj</sourceDirectory>
  </sourceDirectories>
</configuration>

clj/以下は*.cljのコードをおいて、さらにJava等のモジュールがあればsrc/main/jvm以下に置く。

Goalに対する設定

Maven Clojureプラグインで実行できるGoalはいくつかあるが、一番重要なものがclojure:runだろう。 特にcompileしたものを動作させるのか、それともClojureのコード自体を直接実行するのかどうかで設定する項目も変わってくる。 Clojureのコードを実行させるには、

<configuration>
  <script>src/example/run.clj</script>
</configuration>

また、main関数を定義してそこを起点にして実行させたい場合は、コンパイルが必要になる。 main関数を定義したクラスを指定することで実行できるようになる。

<configuration>
  <mainClass>example.run</mainClass>
</configuration>

デプロイ後はMavenで実行

さて、ここからあとはデプロイして実行するだけだ。
Javaで今までバッチを動かしていたならjdkやMavenは導入済みだろう。
Mavenがないって?
問題ない。バイナリを今すぐダウンロードしてパスを通す
そして、今までバッチをキックしていたshellスクリプト等でJavaを動かしていたものを

mvn clojure:compile
mvn clojure:run

に置き換える。たったこれだけだ。

これまで通りバッチは仕事をし続けてくれるし、JVMで動作するので問題があったとしてもこれまで通りのデバッグ方法(jstackによるスレッドダンプやVisual VM等のツール)が通用する。
あなたは今までの1/10の時間でClojureでコードを書いて余った時間はさらに自己鍛錬に打ち込める。
で、あるとき、誰かが*.cljコードを見つけたときはバックエンドはすべてClojureに置き換わっていて、十分に信頼性を得られたことになる。
すべてのコードをClojureに置き換える日も近いだろう。
yay!