Runtime relationship between class loaders and Java SE 9 modules

The Java SE 9 module system, or Jigsaw, works at both compile-time and run-time. At run-time, Jigsaw must work well together with the old class loader mechanism, without breaking existing applications. It has resulted odd and subtle relationship between them.

The diagram represents the relationships among class loaders, modules and related components in the IDEF1X syntax.

http://d.hatena.ne.jp/miyakawa_taku/files/2017-11-08_class_loaders_and_modules.png?d=.png

Classes, packages and class loaders

These components exist from Java SE 8 and earlier, and work the same way on Java SE 9.

(1) When a class is defined in a class loader, the class is bound to a runtime package determined by the name and the defining class loader.

(2) A runtime package is determined by the pair of the name and the class loader, which defines classes beloging to the package.

Modules and module layers

(3) On Java SE 9, a runtime package is bound to a runtime module.

(4) There is two subtypes of modules: named and unnamed *1.

(5) When a ModuleLayer is created invoking ModuleLayer.defineModules, a set of named modules are crated in accord with the configuration, and bound to the ModuleLayer.

(6) A named module has an immutable set of packages, which is specified in its ModuleDescriptor.
(7) And the named module is bound to a class loader specified by the invocation of ModuleLayer.defineModules.

A class loader cannot be bound to by multiple named modules which contain an overlapping set of packages, because a package cannot split across multiple modules. Hence ModuleLayer.defineModules checks invariants to avoid such situation, and throws LayerInstantiationException if any violation is detected. For example, if a class loader has already defined the class org.example.Foo, ModuleLayer.defineModules cannot map a module which contains the package org.example to the class loader.

(8) A class loader has an unnamed module. When a class is defined in a class loader, and its package is not bound to any named module bound to the class loader, the package is bound to the unnamed module of the class loader. Note that unnamed modules are not bound to any module layer.

There is no direct restrictions for the relationship between class loaders and module layers. For example, a class loader may define classes in multiple module layers (JVMS9 §5.3.6), though there is no practical use case for such a configuration.

*1:The term “named module” is implicitly defined in Javadoc of ModuleDescriptor.

エミール・クストリッツァ『夫婦の中のよそもの』

大好きな映画監督エミール・クストリッツァが短編集を出しました。

夫婦の中のよそもの

夫婦の中のよそもの

雑然として騒々しい世界の中で、みんなが怒ったり笑ったり歌ったり踊ったりしんみりしたりしていて、まさにクストリッツァ映画でした。

「蛇に抱かれて」は、映画のプロットみたいな書きっぷりだと思ったら、日本で公開されたばかりの映画『オン・ザ・ミルキー・ロード』の原作みたいです。おなじみのロバも出てくるよ。

伊豆高原ひとり合宿

伊豆高原でひとり合宿をしました。

成果です。

かんぽの宿に泊まりました。部屋も施設も行き届いており、スタッフは親切で、たいへん居心地の良い宿でした。夜遅くに入浴する人が少なかったみたいで、露天風呂を連日独占していました。

Handling auto-generated keys on Bootiful SQL Template

About the library

Bootiful SQL Template is an O/R mapper library which acts as a thin wrapper to Spring JdbcTemplate. The library adds following functionalities.

  • SQL files
  • Moderate facade methods such as forObject, forList and update
  • Limited support for Date and Time API
  • etc.

I think this library is a good option for Spring applications, especially if you are already familiar with JdbcTemplate.

Handling auto-generated keys

To handle auto-generated keys on Bootiful, you can use DBMS specific functions, such as LAST_INSERT_ID() on MySQL.

Spring provides a generic way to fetch generated keys via GeneratedKeyHolder, but Bootiful lacks the support of it. The library author @cero_t told me that the lack is intentional. Because handling of generated keys is inherently DBMS specific, there is no use in abstraction.

Here I exhibit how to handle generated keys on MySQL with Bootiful SQL Template. View the full source code on the BitBucket repository.

The example program uses the purchase table and the purchaseDetail table. The purchase table has a primary key purchaseId which is declared as AUTO_INCREMENT. The DDL is provided as below:

-- src/main/resources/db/migration/V1__create_purchase.sql

CREATE TABLE purchase (
  purchaseId INTEGER NOT NULL AUTO_INCREMENT,
  purchaseTimestamp TIMESTAMP NOT NULL,
  PRIMARY KEY (purchaseId)
)
CHARACTER SET utf8mb4
COLLATE utf8mb4_bin;

CREATE TABLE purchaseDetail (
  purchaseId INTEGER NOT NULL,
  purchaseDetailNumber INTEGER NOT NULL,
  itemName VARCHAR(100) NOT NULL,
  PRIMARY KEY (purchaseId, purchaseDetailNumber),

  CONSTRAINT fk_purchaseDetail_purchase
  FOREIGN KEY (purchaseId) REFERENCES purchase(purchaseId)
)
CHARACTER SET utf8mb4
COLLATE utf8mb4_bin;

The entry point of Bootiful is SqlTemplate class, which can be instantiated with Spring JdbcTemplate and NamedParameterJdbcTemplate.

// src/main/java/org/kink_lang/sqltemplate/autokeys_example/AppConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import ninja.cero.sqltemplate.core.SqlTemplate;

...

@Bean
public SqlTemplate sqlTemplate(
        JdbcTemplate jdbcTemplate,
        NamedParameterJdbcTemplate namedJdbcTemplate) {
    return new SqlTemplate(jdbcTemplate, namedJdbcTemplate);
}

...

Entity classes can be defined as plain Java Beans. No annotations are needed (@Nullable annotation in the list does not affect Bootiful).

// src/main/java/org/kink_lang/sqltemplate/autokeys_example/entity/Purchase.java

package org.kink_lang.sqltemplate.autokeys_example.entity;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.annotation.Nullable;

public class Purchase {

    @Nullable private Integer purchaseId;

    private Date purchaseTimestamp;

    public Purchase() {
        this(null, new Date(0));
    }

    public Purchase(@Nullable Integer purchaseId, Date purchaseTimestamp) {
        this.purchaseId = purchaseId;
        this.purchaseTimestamp = new Date(purchaseTimestamp.getTime());
    }

    @Nullable
    public Integer getPurchaseId() {
        return this.purchaseId;
    }

    public void setPurchaseId(@Nullable Integer purchaseId) {
        this.purchaseId = purchaseId;
    }

    public Date getPurchaseTimestamp() {
        return new Date(this.purchaseTimestamp.getTime());
    }

    public void setPurchaseTimestamp(Date purchaseTimestamp) {
        this.purchaseTimestamp = new Date(purchaseTimestamp.getTime());
    }

    ...
}

The DAO class for Purchase entities is defined as below. It provides distinct fetchLastId method to execute a query to fetch the last generated key.

// src/main/java/org/kink_lang/sqltemplate/autokeys_example/dao/PurchaseDao.java

package org.kink_lang.sqltemplate.autokeys_example.dao;

import java.util.Optional;
import org.springframework.stereotype.Service;
import ninja.cero.sqltemplate.core.SqlTemplate;
import org.kink_lang.sqltemplate.autokeys_example.entity.Purchase;

@Service
public class PurchaseDao {

    private final SqlTemplate sqlTemplate;

    public PurchaseDao(SqlTemplate sqlTemplate) {
        this.sqlTemplate = sqlTemplate;
    }

    /**
     * Inserts the entity to the table.
     * If purchaseId is null, the column value is auto-generated.
     */
    public void insert(Purchase purchase) {
        sqlTemplate.update("db/sqltemplate/PurchaseDao/insert.sql", purchase);
    }

    /** Fetches the last purchaseId which is auto-generated in the current session. */
    public int fetchLastId() {
        return sqlTemplate.forObject(
                "db/sqltemplate/PurchaseDao/fetchLastId.sql", Integer.class);
    }

    /** Returns the entity of the purchaseId if any. */
    public Optional<Purchase> find(int purchaseId) {
        Purchase purchase = sqlTemplate.forObject(
                "db/sqltemplate/PurchaseDao/find.sql", Purchase.class, purchaseId);
        return Optional.ofNullable(purchase);
    }
}

The queries are placed in distinct SQL files.

-- src/main/resources/db/sqltemplate/PurchaseDao/insert.sql
INSERT INTO purchase(purchaseTimestamp) VALUES(:purchaseTimestamp);
-- src/main/resources/db/sqltemplate/PurchaseDao/fetchLastId.sql 
SELECT LAST_INSERT_ID();
-- src/main/resources/db/sqltemplate/PurchaseDao/find.sql
SELECT purchaseId, purchaseTimestamp
FROM purchase
WHERE purchaseId = ?

The client of the DAO class calls insert method and fetchLastId method within a transaction boundary.

// src/main/java/org/kink_lang/sqltemplate/autokeys_example/service/PurchaserService.java

package org.kink_lang.sqltemplate.autokeys_example.service;

import java.time.Clock;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.kink_lang.sqltemplate.autokeys_example.dao.PurchaseDao;
import org.kink_lang.sqltemplate.autokeys_example.dao.PurchaseDetailDao;
import org.kink_lang.sqltemplate.autokeys_example.entity.Purchase;
import org.kink_lang.sqltemplate.autokeys_example.entity.PurchaseDetail;

@Service
public class PurchaserService {

    private final Clock clock;
    private final PurchaseDao purchaseDao;
    private final PurchaseDetailDao purchaseDetailDao;

    public PurchaserService(
            Clock clock,
            PurchaseDao purchaseDao,
            PurchaseDetailDao purchaseDetailDao) {
        this.clock = clock;
        this.purchaseDao = purchaseDao;
        this.purchaseDetailDao = purchaseDetailDao;
    }

    @Transactional
    public void purchase() {
        Purchase purchase = new Purchase(null, Date.from(clock.instant()));
        purchaseDao.insert(purchase);
        int purchaseId = purchaseDao.fetchLastId();

        List<PurchaseDetail> details = Arrays.asList(
                new PurchaseDetail(purchaseId, 1, "アジ"),
                new PurchaseDetail(purchaseId, 2, "大根"),
                new PurchaseDetail(purchaseId, 3, "醤油"));
        details.forEach(detail -> purchaseDetailDao.insert(detail));
    }
}

Conclusion

Bootiful SQL Template enables direct mapping between SQLs and true POJOs, with minimal boilerplate code. This minimalist design seems to work well in most cases.

Terraformのnull_resourceで、変数の初期化子内の変数展開を代用

Terraformでは、変数の値を決める部分(default句)で変数を展開することはできない。つまり、変数の値を、他の変数を使った計算によって決めることはできない。

計算された値を複数の箇所で使うためには、null_resourceのtriggers属性を値のマッピングとして使うことで、変数の代用とすることができる。cf. issue #4084 / Github hashicorp/terraform

variable "msg" { type = "string" }

resource "null_resource" "computed" {
  triggers {
    startup_script = <<EOS
      echo Hello!
      echo '${msg}'
EOS
  }
}

resource "google_compute_instance" "web"  {
  metadata_startup_script = "${null_resource.computed.triggers.startup_script}"
  ...
}

resource "google_compute_instance" "app"  {
  metadata_startup_script = "${null_resource.computed.triggers.startup_script}"
  ...
}

とはいえ、あまり調子に乗って複雑なことをしない方が良い気はする。

コクソン

「アシュラ」の翌日に下高井戸シネマで観劇。連日の韓国映画

白いリボン」に「エクソシスト」とゴールディング蝿の王を加えてグチャグチャに突き混ぜた上からユッケジャンをぶっかけたような映画でした。これは相当にものすごい作品です。脚本も映像も圧倒的でした。

伝奇ものにゾンビものに怪しいよそ者、そしてゴアと、ありとあらゆる怖い要素をぶっこんだお話。初手からいきなり直接的に怖いのに、時が経つごとに状況が混迷して、なにを信じていいか分からなくなり、疑心暗鬼の恐怖に追い込まれるという展開。ボルヘス推理小説について「謎は超自然的なもの、そして神的なものにさえ関与する。解決は手品師の手練だ」 *1 と述べていますが、その逆を行って成功しているわけです。ホラーのお手本だと思います。

映像と音響の面での白眉は祈祷のシーン。あまりの禍々しさに、ただ呆然と圧倒されていました。

舞台は韓国の田舎なのですが、風景が日本の農村に酷似している。田畑や山林や建物はもとより、納屋に雑然とプラスチックケースが詰まれてるところとか、そんなとこまで見たことがある感じ。変なところで感心していました。

タイトルは、よそ者を演じた國村隼に引っ掛けてるのかしら。エンドロールで気づきました。

*1:ホルヘ・ルイス・ボルヘス『不死の人』白水Uブックス、189ページ、 「アベンハカーン・エル・ボハリー おのれの迷宮にて死す」より。

アシュラ

下高井戸シネマで観劇。

仁義なき戦い 広島死闘篇」に「フレンチ・コネクション」を加えてグチャグチャに突き混ぜた上からユッケジャンをぶっかけたような映画です。

汚職市長と検察、二重スパイの主人公が無茶苦茶な暴力をふるったりふるわれたり巻き込まれたり巻き込んだりするお話。途中一段落ついたところで「あれ、まだ続くのか」って感じになるけど、それでダレるということはなく、血糊ふんだんに最後まで引っ張っていきます。

JJUG CCC 2017 Spring

5月20日の土曜日に開催されたJJUG CCC 2017 Springの運営スタッフをやってきました。

Java SE 9もぐだぐだだし、さすがに今回は人減るんじゃねーの、と予想していましたが、ピーカンの晴天も手伝って、1000人以上が参加する大盛況でした。とはいえこれは会場の容量ギリギリで、セッションの満員札止めも続出し、今後の開催に課題を残しました。

当日は、朝から夕方までべったりセッション部屋を担当しました。担当した10セッションすべてが、質の高い良いセッションでした。おそろしいことです。

とりわけガツンと来たのは、大中浩行さんの20分セッション「Java8移行は怖くない〜エンタープライズ案件でのJava8移行事例〜」でした。

元からバージョン管理やCIなど変更に耐えうる足回りを整えており、また小規模な改修やミドルウェアのアップデートなど、システムに手を入れ続けていた。したがって、Java 8移行に関する個別の課題は同じようにふつうに片付けられた、という話でした。

大中さんはメッセージを「技術的に新しいことに取り組まなくなった組織は緩やかに衰退していく」(p. 13)と要約しています。これを肯定形にすると、「新しいことに取り組み続けることでのみ、組織を堅固に保ち続けられる」、ということになろうかと思います。その理由は、

  • メンバーの士気が高まる。
  • 変更を加え続けるための技術的・人材的・組織的地盤が整備される。
  • 個々の改定が、取り組みやすい小さなステップにできる。

というわけです。

実際のところ、あらゆるシステムは協調システムであり、その度合は日増しに強くなっている(Web, クラウド, マイクロサービス, etc.)のだから、「動くコードに触」らない(p. 57)ことによって維持できるという信念は、合理性を着実に失っているのだと思います。

大相撲2017年五月場所中日

● 3-5里山(叩き込み)安美錦4-4 ○

里山見るような立ち合い。安美錦突っ張りながら引き、里山押してついていくが、安美錦押し上げて叩き込み。安美錦うまかった。

○ 4-4佐田の海(押し出し)山口4-4 ●

佐田の海頭で当たって左差し。山口が右に開いて引くのに付いて行って押し出し。これぞ佐田の海の相撲。

● 5-3阿武咲(押し出し)宇良6-2 ○

宇良が低く当たるのを阿武咲突き起こして突き起こして攻める。間合いが空いて、阿武咲が両手突きで飛び込んでくるところを宇良右にいなし、向き直ったところを押し出し。

宇良大きくなった。もう小兵って感じでもない。

● 2-6豊響(押し出し)輝6-2 ○

豊響の当たりを輝両おっつけで止める。押して出るが足がそろって押しきれず、豊響左喉輪から突っ張って逆襲。輝も突き返して右にいなして押し出し。

○ 5-3徳勝龍(押し出し)勢5-3 ●

徳勝龍頭で当たって突っ張り猛然と勢を粉砕。

○ 5-3貴ノ岩(寄り切り)蒼国来 3-5 ●

貴ノ岩左踏み込んで右差し左上手前廻し。蒼国来半身でねばるところを貴ノ岩上手引きつけて、外掛けを掛けながら寄り切り。貴ノ岩、星以上によく見える。

● 2-6松鳳山(寄り切り)正代6-2 ○

松鳳山の両喉輪で正代そっくり返るが、足は下がらずに左抱えて寄り切り。相変わらず無茶な相撲。

○ 6-2北勝富士(寄り切り)宝富士3-5 ●

北勝富士右おっつけから押すが宝富士用意に下がらない。右を差し、左も差した?左の投げから引きつけて出るところ、北勝富士左足一本で踏ん張って、左ハズ押しで逆転寄り切り。いい相撲!

○ 2-6千代の国(寄り切り)大栄翔0-8 ●

大栄翔の突っ張りをかいくぐって千代の国右差し、大栄翔巻き替えて左四つ。しかし四つなら千代の国のほうが上手。両廻し引きつけて寄り切り。大栄翔ストレートの負け越し。

○ 6-2玉鷲(押し出し)隠岐の海1-7 ●

玉右喉輪左ハズ押しで圧倒。

● 4-4嘉風(叩き込み)高安7-1 ○

高安左踏み込んでぶちかましから突っ張っておいて叩き込み。難敵を簡単に料理した。

○ 6-2照ノ富士(極め出し)御嶽海3-4 ●

御嶽海両差しで出るが、照ノ富士閂から引っこ抜いて極め出し。

○ 3-5遠藤(寄り切り)豪栄道5-3 ●

遠藤右踏み込んで低く当たると豪栄道左に開いて叩く。土俵を伝って回り込む豪栄道を、遠藤なおも追撃して両差し、腰を落として寄り切り。遠藤良い相撲。

● 1-7琴奨菊(右上手出し投げ)白鵬8-0 ○

白鵬呼吸をずらして琴奨菊の出足を遅らせる。あたって左四つ、琴奨菊がぶるが白鵬土俵中央で捕まえて、上手を切って右上手投げ。

○ 7-0日馬富士(押し出し)千代翔馬1-7 ●

日馬富士鋭い出足、両手突きから右喉輪で圧倒。

○ 6-2稀勢の里(寄り切り)碧山2-6 ●

稀勢の里右張って左差し、右上手引いて寄るが腰の重い碧山、右だけでは寄りきれない。結局左も下手を引いて、碧山が右おっつけで対抗するところ、両廻し引きつけて寄り切り。

大相撲2017年五月場所二日目

● 0-2北太樹(左肩透かし)里山2-0 ○

今日は左をすぐ差したが、北太樹右ギュウギュウに極める。北太樹小手から振り回して、右に全体重を掛けてねじ伏せようとするところ、里山左ひじを耐えながら膝が土俵に付くスレスレまで粘って、右で頭を押さえつけて左肩透かし。里山珍しく相手を睨みつけた。

見るだに怖かった。さすがに左ひじを気にしていたけれど、大怪我ではないようで良かった。

● 1-1(寄り切り)千代丸1-1 ○

突っ張りを反りながら止めて佐田の海右差し。しかし動きが止まり、佐田の海巻き替えるところ千代丸すぐに出て押し出し。

横綱

稀勢の里は1-1だが肝心の左が使えない。さすがに優勝争いまでは厳しいか。

日馬富士は連勝のいずれも良い相撲。出足鋭い。

白鵬の連勝は省エネ。初日は足がそろってちょっと危なかった。

鶴竜は連敗。間合いの妙が持ち味の横綱が、間合いの妙で負けてるので、調子は良くないんだろう。