JJUG CCC 2017 Fallの記録

11月18日に開催されたJJUG CCC 2017 Fallに、スタッフとして参加、およびセッションでしゃべってきました。

一日中セッション部屋担当で、なんかアナウンスしたり、「N分前」の紙を出したり、Twitterのハッシュタグを賑わしたりしていました。

AsciiDocとPlantUMLでドキュメント作成 by 梅澤雄一郎さん #ccc_a1

軽量マークアップ言語であるAsciiDocと、ドキュメント生成系の基盤一式について。

Kinkのマニュアル生成にはreStructuredText+Sphinxを使っているのですが、記述の素直さという点ではAsciiDocに軍配が上がるようです。機能面で便利だと思ったのはコード内にアンカーが付けられるところ。

「GitHub Flowを使いたいがためにGitを使っている」には大賛成です。商用システムの開発において、分散SCMの利点はブランチ・マージがしやすいところにあります。ブランチ・マージを効率的・安全に回すフローを定義して、フローを支援する環境を整備しなければ、宝の持ち腐れどころか、野放図なバージョン管理(管理?)という害悪をもたらすだけだと思っています。

Apache Cassandraを使ったJavaアプリケーションの作り方 by 森下雄貴さん #ccc_a2

CassandraのサンプルアプリケーションであるKillrVideoを題材にして、今風のシステム間連携のやり方を見ていこうぜ、というセッション。

GuavaのEventBusというクラスの存在を知りました。プロセス内のPub/Sub型メッセージバスみたいです。なんか便利かも。

Spring Bootの本当の理解ポイント by 多田真敏さん #ccc_e3

Springコンテナの仕組みと、Spring Bootの役割。Spring Bootは、Springの世界でCoC (Code over Configuration, 設定よりも規約)を実現するための設定ファイル群だよ、という話でした。

スライドも話も実に分かりやすく、ツボをおさえて、さすがはプロの講師、という感じでした。直後の登壇者としては大プレッシャーです。

Java SE 9の紹介: モジュール・システムを中心に by 宮川拓 #ccc_e4

自分のセッション。Java SE 9のモジュール・システムの話+おまけ程度にその他のアップデート、という内容です。どうにもごちゃっとした話をしてしまったなあ、と思いつつ、しかし実際問題ごちゃっとした領域なわけで、もやもやしています。

モジュール・システムについて、理想形を話すのであれば綺麗にまとめられるはずなのですが、聴衆の期待を考えると、むしろ導入時につまづきの石になるだろう部分に重点を置きたい。つまづきの石について話すためには、既存の仕組みとのあいだの弥縫策や、ワークアラウンド的な道具立てについて、ひととおり触れなきゃいけない。分量も多くなるし、ややこしい話にもなる。結局、ひととおりブチ込んで頑張ってしゃべる、という方針でのぞんだのですが、果たしてよかったのかどうか。

目にする反応は総じて悪くなかったので、救われました。

また、一点スライドの補足。

p. 47に「Oracle JDKでは、外部モジュールの非公開メンバへのリフレクションが可能」とありますが、これはOpenJDKでも同じ動作です。「HotSpot系の」とすべきところでした。 *1

速いソートアルゴリズムを書こう!! 松原正和さん #ccc_m8

1. クイックソート+ワークメモリによる安定ソート化変種、 2. マージソートの分割を3分割にすることによる高速化、 3. マージソートの省メモリ化、という3つのアルゴリズムについてのお話でした。

マージソートって3分割でも計算量変わらないんですね。へーと思ったけど理屈は分かっていない。

Design Pattern in Presto source code by 曾臻さん #ccc_m9

  • スライド: まだ公開されてない?

分散クエリエンジンであるPrestoの実装を題材として、Template MethodパターンとVisitorパターンを紹介する、という内容でした。

「Visitorパターンはデータ構造があまり変わらず、適用する処理が多岐にわたる場合に有効」というような整理がされていて、なるほど、と思いました。Visitorインタフェースはデータクラスに依存するので、データ構造の変更には弱いわけですね。

Javaで使えるもう一つのコンパイル方式 - AOT by 西川彰広さん #ccc_m10

JDK9 on Linuxでは、ネイティブバイナリへのコンパイル (Ahead-of-Time Compilation) ができるよ!という話。

AWS LambdaやCloud FunctionsのようなServerless環境や、その他コンテナ環境では、プロセス起動が速いことが重要だけど、この点Javaは弱かった。AOTコンパイルであらかじめバイナリを作っとくことで、若干マシになる、ということです。モジュール・システムの導入にも、同様の問題意識が反映されているものと認識しています。

ただ、サーバサイドJavaは、エコシステム全体が常駐型プロセスを指向しているので、処理系の工夫だけでは足りない面もありそうです。

DBのTCPプロトコルとJDBC by Yohei Yamanaさん #ccc_m11

MySQLとPostgreSQLの、リモートクライアント接続プロトコルの中身の話。

SQLってそのままポコンとDBMSに送られるんですね。クエリエンジンはDBMS側にあるんだからそりゃそうか。あとでYamanaさんに伺ったところ、クライアント側でも最低限のチェックはしているけど、結局サーバ側に行かないと十分な解析・チェックはできない、とのことでした。

Java x ArduinoのIoT アーキテクチャパターン by 山本ユースケさん #ccc_m12

  • スライド: まだ公開されてない?

楽しいArduinoデモ。LEDをチカチカさせたり、プレゼン用マウス的なものを作ったり。

Arduinoのプログラムは独自言語で書くのでJavaは使えません。しかし、FirmataというプロトコルでArduinoデバイスをホストコンピュータから叩くことができ、firmata4jというJava実装もあるよ、という話でした。

総括

一緒に部屋担当についたボランティアスタッフの方々の活躍により、かなりスムースに運営できたと思います。ありがとうございます。おかげで、スタッフとしての参加でありながら、上述のとおり聴講者としても満喫してしまいました。知恵熱が出そうです。

それから、朝晩ちょっとした荷物を運んだだけで、上半身がえらい筋肉痛です。ふだんいかに運動していないか思い知らされます。

*1:Slideshareがスライド再アップロードできない仕様になっているため、訂正できませんでした。

自動モジュールと無名モジュールのアクセス権とアクセシビリティ

Java SE 9のモジュール・システムには、古いコードベースからの移行のために、二種類の特殊なモジュールが存在します。自動モジュール (automatic module) と、無名モジュール (unnamed module) のふたつです。

自動モジュールは、典型的には、モジュール・レイヤーを構成するモジュールのうち、module-info.classを持たないものです。たとえば、モジュールパス上に存在するJARファイルのうち、module-info.classを持たないものは、自動モジュールとして読み込まれます。JARのマニフェスト (META-INF/MANIFEST.MF) にAutomatic-Module-Name属性が存在する場合、その値が自動モジュールの名前となります。Automatic-Module-Name属性がない場合は、JARファイル名にしたがってモジュール名が決定されます(cf. ModuleFinder.ofのJavadoc)。 Automatic-Module-Name属性の有無は、モジュール名以外には影響がありません。

自動モジュールのアクセス権とアクセシビリティは次のように定義されています(cf. ModuleDescriptorのJavadoc)。

  • 同一モジュールレイヤー上のすべてのモジュールをreadする。
  • すべての無名モジュールをreadする。
  • すべてのパッケージをexportする。
  • すべてのパッケージをopenする。

無名モジュールは、モジュール・レイヤーに属さないモジュールです。たとえば、クラスパス上のクラスファイルは、無名モジュールに属します。

無名モジュールのアクセス権とアクセシビリティは次のように定義されています (cf. JVMS9 §5.3.6)。

  • すべてのモジュールをreadする。
  • すべてのパッケージをexportする。

module-info.javaでは、自動モジュールに対するrequires文を含むことはできますが、無名モジュールに対するrequires文は不可能です。したがって、たとえば、クラスパス上のクラスをコンパイル時に触ることはできません。実行時に、たとえばリフレクションを通じて触るためには、たとえばModule.addReadsを用いたアクセス権の変更が必要です。

実行時におけるJava SE 9モジュールとクラスローダーの関連

Java SE 9のモジュール・システム(Jigsaw)はコンパイル時と実行時の両方で機能します。実行時には、Java SE 8以前から存在するクラスローダーの機構とうまく帳尻を合わせて、既存のアプリケーションを壊さないようにする必要があります。結果として、両者の関係は微妙で奇妙なものとなっています。

下記の図は、クラスローダー、モジュールその他の関連をIDEF1X記法で示したものです。

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

クラス、パッケージ、クラスローダー

これらの部品は、Java SE 8以前と同様に、Java SE 9でも機能します。

(1) クラスローダーがクラスを定義すると、そのクラスは、クラス名とクラスローダーにもとづいて、実行時パッケージに紐付けられます。

(2) 実行時パッケージは、パッケージ名と、パッケージ内のクラスを定義するクラスローダーの対によって指し示されます。

モジュール、モジュールローダー

(3) Java SE 9では、実行時パッケージは実行時モジュールに紐付きます。

(4) モジュールにはふたつの種類があります。名前付きモジュール *1 と無名モジュールです。

(5) ModuleLayer.defineModulesの呼び出しによってModuleLayerが作られると、引数として渡されたConfigurationにもとづいて、名前付きモジュール群が作られます。名前付きモジュール群はモジュールレイヤーに紐付けられます。

(6) 名前付きモジュールは、ModuleDescriptorにもとづくパッケージ群を持っていて、両者の関連は互いに不変です。

(7) 名前付きモジュールは、ModuleLayer.defineModulesメソッドで指定されたクラスローダーに紐付けられます。

同名のパッケージを含む複数の名前付きモジュールを、ひとつのクラスローダーに紐付けることはできません。パッケージは複数のモジュールにまたがれないためです。このような状況を防ぐため、ModuleLayer.defineModulesメソッドは不変条件をチェックして、違反があればLayerInstantiationExceptionを投げます。たとえば、クラスローダーがorg.example.Fooクラスを既に定義している場合、ModuleLayer.defineModulesメソッドは、このクラスローダーにorg.exampleパッケージを含む名前付きモジュールを紐付けられません。

(8) クラスローダーには、ひとつの無名モジュールが紐付いています。クラスローダーがクラスを定義するとき、そのパッケージが、クラスローダーに紐付けられたどのモジュールにも紐付けられていない場合、そのパッケージはクラスローダーの無名モジュールに紐付けられます。ここで、無名モジュールはどのモジュールレイヤーにも属さないことに注意してください。

クラスローダーとモジュールレイヤーの間の関連には、直接の制限はありません。たとえば、ひとつのクラスローダーが、複数のモジュールレイヤーにまたがってクラスを定義することも可能です (JVMS9 §5.3.6) 。ただし、そのような構成に実際上の利点はなさそうです。

*1:「名前付きモジュール」という用語は、ModuleDescriptorのJavadocで暗黙的に定義されています。

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ページ、 「アベンハカーン・エル・ボハリー おのれの迷宮にて死す」より。

アシュラ

下高井戸シネマで観劇。

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

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