Gradle プラグイン#
3つあります。必要なものだけ入れてください。
| ID | 何をするか | 主なタスク |
|---|---|---|
io.jimble.db |
マイグレーションとコード生成を compileJava の前に流す |
migrate / codegen / codegenCheck |
io.jimble.jte |
src/main/jte を compileJava の前に Java に変換する |
generateJte |
io.jimble.run |
開発用のホットリロード | jimbleRun |
plugins {
application
id("io.jimble.jte") version "2.7.5"
id("io.jimble.run") version "2.7.5"
id("io.jimble.db") version "2.7.5"
}
**Plugin Portal には出していません。**Maven Central から取るので、
settings.gradle.kts に置き場所を書いてください(jimble new の雛形には入っています)。
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
補足
プラグインは Gradle デーモンの中で動くので、バイトコードは Java 17 で出しています。 アプリ側(Java 25)とは別です。Gradle は 9 以上が要ります。
まっさらから書く#
jimble new を使わないなら、この2つを置けば動きます。
下は実際に動かして確かめたものです(gradle build で
migrate → codegen → compileJava が繋がり、gradle run で起動します)。
// settings.gradle.kts
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
rootProject.name = "memo"
// build.gradle.kts
plugins {
application
id("io.jimble.jte") version "2.7.5" // src/main/jte を使うなら
id("io.jimble.run") version "2.7.5" // ホットリロードを使うなら
id("io.jimble.db") version "2.7.5" // DB を使うなら
}
repositories {
mavenCentral()
}
// jimble は Java 25 で作ってあります。これが無いと依存が解決できません
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
dependencies {
implementation("io.jimble:jimble-web:2.7.5")
testImplementation(platform("org.junit:junit-bom:5.11.4"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
// conf/ をリソースに足す。設定もマイグレーションもここから読みます([設定](./config))
sourceSets {
main {
resources {
srcDir("conf")
}
}
}
application {
mainClass = "memo.App"
}
jimbleRun {
mainClass = "memo.App"
}
落とし穴
**ツールチェーンの指定は要ります。**書かないと、依存の解決の時点で止まります。
Dependency resolution is looking for a library compatible with JVM runtime version 21,
but 'io.jimble:jimble-web:2.7.5' is only compatible with JVM runtime version 25 or newer
どれを依存に足すか#
まとめて入るものは書かなくてよいです(jimble-web を足せば jimble-db も jimble-util も付いてきます)。
| 足すもの | 何が入るか | いっしょに付いてくるもの |
|---|---|---|
io.jimble:jimble-web |
Web(Router / Context / Session / テンプレート) | jimble-db → jimble-util → jimble-json / jimble-converter / jimble-core |
io.jimble:jimble-db |
DB だけ(バッチや CLI から使うとき) | jimble-util → jimble-json / jimble-converter / jimble-core |
io.jimble:jimble-mq |
MQ | jimble-db |
io.jimble:jimble-batch |
バッチ・DB スケジューラ | jimble-db / jimble-mq |
io.jimble:jimble-batch-manager |
バッチ管理画面 | jimble-web / jimble-batch |
io.jimble:jimble-mcp |
MCP サーバー | jimble-web |
io.jimble:jimble-json |
JSON の読み書きだけ(jimble のほかを使わないとき) | なし |
io.jimble:jimble-converter |
型変換だけ(Map・Bean・record・日時のあいだ。jimble のほかを使わないとき) | なし |
io.jimble:jimble-otel |
OpenTelemetry へトレースを出す(足したときだけ 0.9MB 増えます) | jimble-core |
DB のドライバは要りません(MySQL / MariaDB と PostgreSQL は jimble-db が持っています)。
io.jimble.db#
./gradlew migrate # 未適用の SQL を当てる
./gradlew codegen # テーブル定義のクラスを作る(migrate のあと)
./gradlew codegenCheck # コミットされている生成物がスキーマと合うか見る
中身は マイグレーションとコード生成 を見てください。
jimble {
sourceRoot = "src/main/java" // 生成先
env = "local" // CLI に -Denv=... で渡る
autoGenerate = true // compileJava の前に繋ぐか
}
| プロパティ | 既定 |
|---|---|
sourceRoot |
src/main/java |
env |
-Pjimble.env → 環境変数 ENV → local |
autoGenerate |
-Pjimble.autoGenerate → env == "local" なら true |
つまりローカルでだけ migrate → codegen → compileJava が繋がります。
CI では繋がらないので、コミットされた生成物でそのままコンパイルできます。
./gradlew build -Pjimble.autoGenerate=true # CI でも繋ぎたいとき
補足
migrate と codegen は毎回走ります(up-to-date になりません)。
DB の状態は Gradle からは見えないためです。
こつ
どちらも実体は CLI(io.jimble.db.cli.JimbleDbCli)を叩いているだけです。
ロジックを Gradle 側に置いていないので、CI や本番では同じことを CLI で直接できます。
注意
クラスパスにこのプロジェクトのクラスは入りません(リソースと依存だけ)。
入れると compileJava → codegen → compileJava で循環します。
SQL も設定もリソース側にあるので、これで足ります。
codegenCheck がずれを見つけると、こう言って落ちます。
コミットされている生成物がスキーマと一致しません(2 件)。codegen を実行してコミットしてください。
生成物が古いです: db/blog_example/table/post/Post.java
生成物が足りません: db/blog_example/table/tag/Tag.java
io.jimble.jte#
src/main/jte の .jte を Java に変換します。コンパイルは compileJava がやるので、
テンプレートの型の間違いはビルドで落ちます(テンプレート)。
| タスク | 入力 | 出力 |
|---|---|---|
generateJte |
src/main/jte |
build/generated/sources/jte/main/java |
generateTestJte |
src/test/jte(変えられません) |
build/generated/sources/jte/test/java |
jte {
sourceDirectory = "src/main/jte"
packageName = "gg.jte.generated.precompiled"
contentType = "Html" // Html | Plain
trimControlStructures = true
htmlCommentsPreserved = false
}
注意
packageName は**変えないでください。**jte の既定と揃えてあります。
ずらすと実行時にテンプレートが見つかりません。
補足
生成先は毎回まるごと消してから作ります。 消したテンプレートの生成物が残ると、消したはずのものがコンパイルを通って jar に入ります。
コンパイラ(gg.jte:jte)を持っているのはプラグインだけです。
アプリの実行時クラスパスには jte-runtime しか入りません(要件 D-27)。
io.jimble.run#
./gradlew jimbleRun
仕組みと注意は ホットリロード にあります。ここは設定の一覧です。
jimbleRun {
mainClass = "my_blog.App" // 必須
}
| プロパティ | 既定 | 何を決めるか |
|---|---|---|
mainClass |
なし(必須) | main を持つクラス |
port |
9000 |
ブラウザが見るプロキシのポート |
host |
"127.0.0.1" |
プロキシが待ち受けるアドレス。既定ではこのマシンからしか開けません(2.2.3 から。それまではすべての NIC で待ち受けていました)。スマートフォンなどから確かめるときだけ "0.0.0.0" |
appPort |
port + 100 |
アプリが待ち受けるポート(アプリはいつも 127.0.0.1 で待ち受けます) |
env |
"local" |
jimble.env として渡る |
buildTasks |
[":classes"] |
変更時に流すタスク |
watchDirs |
なし | 追加で見張る(ルートからの相対パス) |
excludeDirs |
なし | 見張らない |
watchExtensions |
.java .jte .html .js .css .conf .xml .properties .yml .sql |
見張る拡張子 |
restartMode |
"on_request" |
on_request(次のリクエストで) / immediate(保存したら) |
quietMillis |
300 |
変更が落ち着いたと見なす待ち |
startTimeoutSeconds |
60 |
起動を待つ上限 |
args |
なし | main に渡す引数 |
見張るのは src と conf(あれば)+ watchDirs です。
落とし穴
restartMode に書けない値を書くと落ちます。
前は黙って on_request に倒していたので、書き間違えても
「設定したのに効かない」だけが残っていました。
補足
debug や jvmArgs のプロパティはありません。
アプリは Gradle と同じ JVMで動くので、IDE のデバッガがそのまま効きます。
ヒープを変えたいときは gradle.properties の org.gradle.jvmargs です。
ビルドに失敗したら#
止まりません。ブラウザにそのまま出ます。
| 何が起きたか | ステータス |
|---|---|
| 作り直しに失敗した | 503(Gradle の出力をそのまま) |
| アプリに繋がらない | 502 |
jimbleRun の中で例外 |
500 |
直して保存し、リロードすれば続きができます。
補足
ビルドは gradlew を子プロセスで叩きます(動いているビルドの中から
同じプロジェクトのタスクは呼べないため)。
gradlew が壊れていたら、理由を出して PATH の gradle に逃がします。
AI 向けのタスク#
どの jimble のプラグイン(io.jimble.jte / io.jimble.run / io.jimble.db)を当てても付きます。
| タスク | すること |
|---|---|
./gradlew jimbleSkills |
.claude/skills/ を、使っている jimble の版の skill に揃える。AGENTS.md / CLAUDE.md が無ければ置く |
./gradlew jimbleCheck |
jimble の既知の落とし穴をソースと設定から見つける。見つけたものには直し方と引き先を付ける |
jimbleSkills#
jimble new は skill を写して置くので、jimble の版を上げても写しは古いままです。
アプリの AI は古い jimble の説明を読み続けます。版を上げたら流してください。
./gradlew jimbleSkills
./gradlew jimbleSkills --overwrite # 手で直したもの・控えの無いものも入れ替える
- **手で直した skill は上書きしません。**置いたときの中身のハッシュを
.claude/jimble-skills.propertiesに控えていて、 控えと同じ(jimble が置いたまま)なら入れ替え、違えば触りません - 控えの無い skill(控えを書くようになる前の
jimble newで置いたもの)も、直したかどうか分からないので触りません。 直していなければ--overwriteで入れ替えてください - **アプリ固有の決まりは skill ではなく
AGENTS.mdに書きます。**skill を直すと、次の版に揃えられなくなります AGENTS.md/CLAUDE.mdは無ければ置き、あれば触りません(CLAUDE.mdは@AGENTS.mdと書いて読み込むだけ)
jimbleCheck#
動かしても黙って間違えるものだけを見ます(起動時に落ちて直し方を言うものは見ません)。
ERROR が1つでもあれば落ち、WARN だけなら落ちません。
[J903 WARN] src/main/java/demo/PostController.java:83 isError() は 2.0 で無くなった(DB の失敗は SqlExecuteException)
直し方: db.transaction(...) の中なら確定しないことで守られる。分岐したいのは一意制約くらいなので DuplicateKeyException で受ける
詳しく: https://jimble.io/ja/migrate-2.md
| 規則 | 重さ | 見るもの |
|---|---|---|
| J101 | ERROR | context.request().getString(...) など(Request は Data ではない。bodyAll() を通す) |
| J102 | WARN | path(...) のブロックに before / after があるのに、ルートが1つも無い(フィルタは書いたブロックのルートにしか付かず、何にも効かない。path("/mcp", () -> before(...)) の形) |
| J201 | WARN | 設定の ${ENV} に ? が無い(環境変数が無い環境で起動時に落ちる) |
| J202 | ERROR | ${?ENV} の行のあとで同じキーを書き直している(環境変数が効かない) |
| J203 | WARN | application.conf や本番のファイルに cookie.secure = false(本番も引き継ぎ、Cookie が http でも送られる。application.local.conf にだけ書く) |
| J204 | WARN | server.trust_proxy = true なのに server.trusted_proxies も server.client_ip_header も無い(中継が2段以上だと、全員が手前の中継の IP に見える) |
| J205 | WARN | server.tcp_no_delay = false(分けて書く応答が、同じ接続の 2 回目から 1 回ごとに約 40ms 遅れる。Nagle に頼りたい特別な場合だけ) |
| J301 | ERROR | Migration.install() が DBUtil.load(...) のあと(マイグレーションが流れない) |
| J302 | ERROR | BatchRegistry.sync はあるのに BatchTables.install がどこにも無い |
| J303 | ERROR | BatchRegistry.sync が BatchRegistry.add より前(全部のバッチが nothing になる) |
| J304 | WARN | codegen を使っているのに、MQ の表(mq_scheduler など)を codegen.exclude_tables に書いていない |
| J401 | ERROR | 外側の before(Auth::guard) と、Auth.REALM のブロックの中の Remember.restore(覚えていても毎回 401) |
| J402 | WARN | Remember.forgetAll(...) があって、同じファイルに Auth.revoke / revokeOthers が無い(ほかの端末のセッションが残る) |
| J403 | WARN | Lockout.waitSeconds で読んでから Lockout.fail で数え、Lockout.attempt を使っていない(同時に送られると素通りする) |
| J501 | WARN | skill が使っている jimble の版のものではない(jimbleSkills で揃える) |
| J601 | WARN | error(...) のラムダが getMessage() を返していて、500 と比べていない(DB の誤りや内部のパスが相手に届く) |
| J602 | WARN | リクエストの値(request().body〜() か、それを入れた変数)を、許す列を並べずに apply / setRow / valueRow へ渡している |
| J701 | WARN | トランザクション(db.begin() / db.transaction(...) / TransactionException)を使うファイルの空の catch(確定していないのに成功を返す) |
1.x の書き方で、2.0 で消えたもの・型や意味が変わったものも出します(どれも WARN。書き換え先は 2.0 への移行)。
| 規則 | 見るもの |
|---|---|
| J801 | new DBTransaction(...) / DBTransaction.transaction(...)(2.0 で消えた) |
| J802 | DB の beginTransaction() / commitEndTransaction() / rollbackEndTransaction() / endTransaction()(2.0 で消えた) |
| J803 | Router x = router.path("/x")(2.0 で消えた。ブロックの path("/x", r -> { ... }) にする) |
| J804 | 列の subtract(...)(2.0 で消えた。割り算を出していた) |
| J805 | Dsl.or(...) / Dsl.and(...)(2.0 で消えた。Dsl.anyOf / Dsl.allOf にする) |
| J806 | cookies().put(cookie)(2.0 で消えた。署名しなかった) |
| J807 | eq(null) / not(null)(2.0 で例外。is_null() / is_not_null() にする) |
| J808 | 文字列 "now()"(2.0 ではただの文字列。Dsl.now() にする) |
| J810 | long id = db.insert(...)(2.0 の insert は値を返さない。insertKey にする)。受け手が jimble の DB / Tx のときだけ(ClientRepository.insert(db, ...) のようなアプリのクラスの呼び出しは出さない) |
| J901 | Data row = db.select(...) / selectCached(...)(2.0 は Optional<Data>。.orElse(...) などで受けているものは出さない) |
| J902 | if (!db.execute(...)) / boolean x = db.execute(...)(2.0 は件数を返し、失敗は例外) |
| J903 | db.isError()(2.0 で無くなった)。受け手が jimble の DB のときだけ(自分で作ったクラスの isError() は出さない) |
| J904 | DBUtil.load / DBLock.lock / DBLock.create の戻り値を if (!...) などで見ている(2.0 は値を返さず、失敗は例外) |
| J905 | RedisLock.tryLock(...)(2.0 は Optional<RedisLockResult>。Optional で受けているものは出さない) |
| J906 | selectOrThrow / selectListOrThrow / insertNoReturnKey(2.0 で非推奨) |
| J907 | session().data().put(...)(と putData / remove / clear / putAll。2.0 で例外) |
J8xx と J9xx はいつも出します。--target=2.0 は 1.5 のころのスクリプトがそのまま動くように受け付けますが、何もしません。
補足
Java は構文木にせず、コメントと文字列を除いてから読んでいます。誤検知は、その行か前の行に
// jimble-check:ignore J101 と書けば出なくなります(設定ファイルは # jimble-check:ignore J201)。