Toyokumo Tech Blog

トヨクモ株式会社の開発本部のブログです。

Clojureで作るAPI ビルドして実行できるようにする

[連載]Clojureで作るAPIの6記事目です。

前回の記事はこちらです。

tech.toyokumo.co.jp

この記事ではビルドしてJARファイルを生成し、 java コマンドでアプリケーションを実行できるようにしていきます。

JARファイルとは

まずJARファイルとは、Javaのライブラリやアプリケーションを配布するためにJavaクラスファイル、メタデータ、リソース(textや画像など)を1つのファイルにまとめた物です。

詳細については英語版のWikipediaをご覧ください。

en.wikipedia.org

JARファイルの他に uberjar という言葉もありますが、これは実行に必要な全てのファイルやデータを含んだJARファイルを指します。 アプリケーションを実行するためには、uberjarであった方が都合がいいため、この先はJARファイルという言葉をubarjarであるという前提のもとで使用します。

tools.buildを使う

この目的のためにtools.buildというClojure公式ライブラリがあります。こちらを使っていきましょう。

github.com

基本的に公式サイトに載っている使い方の通りにやればうまくいきます。

まずライブラリを :build というaliasに追加します。

;; ./deps.edn
{:paths ["src"]
 :deps {org.clojure/clojure {:mvn/version "1.11.1"}
        info.sunng/ring-jetty9-adapter {:mvn/version "0.17.6" :exclusions [org.slf4j/slf4j-api]}
        spootnik/unilog {:mvn/version "0.7.30"}
        com.stuartsierra/component {:mvn/version "1.1.0"}}
 :aliases {:dev {:extra-paths ["dev"]}
           :build {:deps {io.github.clojure/tools.build {:git/tag "v0.8.2" :git/sha "ba1a2bf"}}
                   :ns-default build}}}

次に build.clj にビルドスクリプトを用意します。

;; ./build.clj
(ns build
  (:require
   [clojure.tools.build.api :as b]))

(def class-dir "target/classes")
(def basis (b/create-basis {:project "deps.edn"}))
(def uber-file "target/cljapi.jar")

(defn uber [_]
  (b/copy-dir {:src-dirs ["src" "resources"]
               :target-dir class-dir})
  (b/compile-clj {:basis basis
                  :src-dirs ["src"]
                  :class-dir class-dir})
  (b/uber {:class-dir class-dir
           :uber-file uber-file
           :basis basis
           :main 'cljapi.core}))

最後にmain関数となる cljapi.core/-main を実行できるようにするために、このnamespaceをクラスとして生成させます。 そのためには :gen-class をつけます。

;; ./src/cljapi/core.clj
(ns cljapi.core
  (:gen-class)
  (:require
   [cljapi.system :as system]))

(defn -main
  [& _args]
  (system/start))

ここまで記述できたら clojure -T:build uber を実行すれば実行可能なJARファイルを生成できます。

$ clojure -T:build uber
# targetディレクトリにJARファイルが生成されました。
# 中身を見てみます。
$ cd target
$ tree -L 3
.
├── classes
│   ├── cljapi
│   │   ├── component
│   │   ├── core$_main.class
│   │   ├── core$fn__1158.class
│   │   ├── core$loading__6789__auto____140.class
│   │   ├── core.class
│   │   ├── core.clj
│   │   ├── core__init.class
│   │   ├── system$fn__1153.class
│   │   ├── system$loading__6789__auto____142.class
│   │   ├── system$new_system.class
│   │   ├── system$start.class
│   │   ├── system$stop.class
│   │   ├── system.clj
│   │   └── system__init.class
│   ├── com
│   │   └── stuartsierra
│   └── ring
│       ├── adapter
│       ├── core
│       └── util
└── cljapi.jar

実行して確認してみましょう。

$ java -jar target/cljapi.jar
# 別のterminalでcurlしてみる
$  curl http://localhost:8000
Hello, Clojure API

Makefileに追記

オプションを覚えるということはしたくないので、Makefileにも追記しておきます。

# ./Makefile
.PHONY: format
format:
  cljstyle check

.PHONY: lint
lint:
  clj-kondo --lint src

.PHONY: static-check
static-check: format lint

.PHONY: clean
clean:
  rm -fr target/

.PHONY: build
build: clean
  clojure -T:build uber

おわりに

ここまでで、単一のJARファイルを生成して java コマンドでアプリケーションを起動できるようになりました。

コードはGitHubのリポジトリに上げてあります。 06_ビルドして実行できるようにするというタグからご覧ください。

次は設定を外部ファイルで管理できるようにして、開発環境、ステージング環境、本番環境で値を変えられるようにしていきます。


トヨクモでは一緒に働いてくれる技術が好きなエンジニアを募集しております。

採用に関する情報を公開しております。 気になった方はこちらからご応募ください。