ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

読んだ : さわって学ぶクラウドインフラ Amazon Web Services 基礎からのネットワーク & サーバー構築

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

インフラ周りちゃんと学ぶぞー! と言いつつ時間ばかり経ってしまっているので、いい加減ちゃんとやろうと思って本書を読んだ。

易しめの内容でわかりやすく書かれているので、「インフラ周りをちゃんと学びたいと思いつつも苦手意識を持っている」 という人 *1 や、「SSH コマンドなどもろくに使ったことがないけどインフラ周りを学んでみたい」 という人におすすめの書籍だった。

感想

  • AWSWordPress を動かす」 ために必要な内容を順番に説明していくという、わかりやすい内容だった。
  • AWS だけでなくネットワーク周り (TCP / IP とか) についても初心者向けの内容になっており、インフラ周りについてほぼ何も知らない状態でもとっつきやすい。
  • AWS についても リージョンアベイラビリティゾーン (AZ) といったことから説明がある。
  • そして、Amazon Virtual Private Cloud (VPC)Amazon Elastic Compute Cloud (EC2) といった、AWS 上で web サービスを運用する場合によく使われるであろうサービスについての知識が得られる。
  • わかっている人にとっては簡単すぎる内容だと思うが、初心者にとってはこれぐらい簡単な方がとっつきやすくてありがたい。
    • 実際に本書の内容に従って手を動かしていくと、だいぶ理解が進むと思う。
    • 自分は仕事でも AWS を使っているし、同僚の勉強会で Terraform を触りながら AWS の概念を学ぶ、ということもやってきたが、改めて本書の内容に従って学びなおすことでだいぶ定着したと思う。 (やはり一から自分で手を動かすというのが大きい。)
  • お金のことを考えるとなかなか個人で AWS を触ろうという気にならなかったりするのだけど、本書の内容は AWS のアカウント作成後に付与される無料枠の中でできることばかりなので、気軽に試していけるのも良い。

本書の内容

9 章構成になっている。

  • 1 章 : ネットワークの基礎的なところから AWS の基礎的な部分まで解説される。 また、本書で構成するシステムの全体像も描かれる。
    • 一般的な web アプリケーションに必要なアプリケーションサーバーと DB サーバーが、それぞれパブリックサブネットとプライベートサブネットに配置されているようなネットワーク。
  • 2 章 : ネットワークを作る。
    • VPC を用意し、パブリックサブネットを作る。
  • 3 章 : サーバー構築。 EC2 インスタンス起動。
  • 4 章 : Web サーバーソフトをインストール。 Apache
  • 5 章 : HTTP の動きの確認。
  • 6 章 : プライベートサブネットを作る。
  • 7 章 : NAT を構築。
  • 8 章 : DB を用いたブログシステムの作成。
  • 9 章 : TCP/IP による通信の説明。

上記のような感じで、手順としても SSH コマンドなどについても丁寧に説明しているので、本当に初心者にもわかりやすいと思う。

さらに学ぶために

上で何度か言ったように、本書は初心者にもわかりやすいような内容になっている。 逆に、自動化などの応用的な話はあまりないので、さらに学びたい人は本書の内容を Terraform で書きながら読み進める、というようなことをしてもいいかもしれない。

私は本書を読み終わった後に Terraform の Getting Started を読んでいく、という感じで学んでいった。

*1:私です

Java (JVM 言語) におけるコードカバレッジの計測方法

Java と Kotlin で書かれたアプリケーションのコードテストのカバレッジを取りたいなーと思って、コードカバレッジ計測ツールについて調べてみてる。

JVM 言語におけるコードカバレッジ計測の方法

JaCoCo のドキュメントにわかりやすくまとまっている。

Coverage information has to be collected at runtime. For this purpose JaCoCo creates instrumented versions of the original class definitions. The instrumentation process happens on-the-fly during class loading using so called Java agents.

There are several different approaches to collect coverage information. For each approach different implementation techniques are known. The following diagram gives an overview with the techniques used by JaCoCo highlighted:

f:id:nobuoka:20180228230512p:plain

JaCoCo - Implementation Design

カバレッジ情報は実行時に収集されるものなので、情報収集のための仕組みが必要。

大きく分けると Runtime Profiling と Instrumentation の 2 つの仕組みがある。 前者は JVM の仕組み (JVMTI や JVMPI) を使うもの。 後者は実行対象のプログラムの方に収集用の仕組みを搭載する (これを instrumentation というらしい?) もの。

Runtime Profiling

JVM TI は Java Virtual Machine Tool Interface の略。 Java SE 5 で導入された。

JVM TI は JVM 上で動くアプリケーションの状態を検査したり、実行を制御したりするためのプログラミングインターフェイスらしい。

JVMPI は JVM TI よりも古くからある同じようなインターフェイスらしい。 Java SE 5 で (JVM TI ができたことで) 非推奨になり、Java SE 6 で廃止された模様。

これらを使ったコードカバレッジは、アプリケーション側に何も手を入れなくて良いことが利点だと思われる。 が、詳細はわからない。

Instrumentation

上でも説明したように、計測対象のアプリケーションコードに情報収集のための仕組みを搭載する方法が、この Instrumentation である。 Runtime Profiling と異なり、Android アプリの実行環境のような非 JVM 環境でも使用できるという利点がありそう *1

上の図を見るとわかるように、様々な方法がある。

  • ソースコードを変更するもの。
  • バイトコードを変更するもの。
    • オフライン (offline) で変更するもの。 (= JVM に読み込まれる前の状態、例えばクラスファイルそのものを変更する。)
      • Replace と Inject って書かれてるけどそれぞれの意味はわからない。
    • オンザフライ (on-the-fly) で変更するもの。 (= JVM 読み込まれる際などにオンメモリで変更する。)
      • クラスローダで変更する方法と、Java Agent を用いる方法がある。

JaCoCo は、Java Agent を用いてオンザフライでバイトコードを変更する方式である。 *2

Java Agent について

Java Agent については java.lang.instrument パッケージの Javadoc に書かれている。

-javaagent:[=] というコマンドラインオプションで指定して使用できるものである。 次のページも参考になる。

エージェントの背景にある基本概念は、「JVM がクラスをロードする場合、エージェントはそのクラスのバイトコードを修正できる」 という考え方です。

それぞれの特徴

現時点で自分がわかっている範囲で特徴を書いておく。

  • ソースコードを変更する方式は、言語によって使用できるかどうかが変わる。 例えば Java 言語に対応しているツールでも Kotlin には対応していなかったりする。
    • 一方でバイトコードを変更する方式は、JVM 言語であればどれにでも対応できるはず。
  • オフラインで変更する方式は、ビルド時にクラスファイルが書き換えられてしまうので、それをそのまま本番アーティファクトのビルド時に使用できない。 (使用したらダメというわけではないが、パフォーマンスが落ちるなどの問題が起こる。)

様々なコードカバレッジツール

JVM 言語用の様々なコードカバレッジツールについて、Clover のブログで比較紹介してくれている。 (最終更新が 2017 年春なので情報はちょっと古いかもしれない。)

有名どころとしては JaCoCoOpenClover (Atlassian Clover がオープンソース化されたもの)、JCov といったところだと思う。

Instrumentation 方式に着目すると、JaCoCo と JCov はオフラインおよびオンザフライのバイトコード instrumentation に対応しており、Clover はソースファイル instrumentation に対応している。 なので JaCoCo や JCov は (JVM 言語なら何でも対応できるので) Kotlin にも対応するが、Clover は (対応言語に入っていない) Kotlin には対応しない。

Clover と JaCoCo を軽く使ってみたところ、設定の簡単さはどちらも同じ。 出力される HTML を見ると JaCoCo は単純な内容で、Clover の方はプロジェクトリスクの高いものを表示したり、視覚的だったりと、結果表示については高機能さを感じた。

今回は Kotlin でのカバレッジも取りたいので、(対応していない) Clover は選外で、JaCoCo か JCov のどっちかを使うことになりそう。

*1:憶測です

*2:オプションでオフラインでの instrumentation も可能。

Ktor の自動再読み込み (Automatic Reloading) 機能

JetBrains 製の Kotlin 用 web アプリケーションフレームワークKtor の自動再読み込み機能について。 開発時にサーバー全体を再起動しなくても、ソースコードを変更してビルドした後のクラスファイルの再読み込みをしてくれる機能である。

設定方法

上記ドキュメントにあるように、下記のように設定ファイルの ktor.deployment.watch に監視対象のモジュールを指定してやればよい。

ktor {
    deployment {
        port = 8080
        watch = [ module1, module2 ]
    }
    
    …
}

モジュールの値としてどんな文字列を指定してやればよいのか?

ドキュメントには、以下のように書かれている。

For now watch keys are just strings that are matched with contains against classpath entries of the loaded application, such as a jar name or a project directory name. These classes are then loaded with special ClassLoader that is recycled when change is detected.

Autoreload - Servers - Ktor

すなわち、JAR 名やプロジェクトのディレクトリ名などの、クラスパスのエントリに含まれるような文字列を書けばよい。

例えば Gradle を使っているプロジェクトを IntelliJ IDEA 上で実行する場合は、クラスパスに 「file:/C:/Users/nobuoka/Documents/projects/ktor-sample/app/out/production/classes/」 というようなファイル URL が含まれる。 この文字列の一部分にマッチするような文字列を指定してやる。 IntelliJ IDEA 用での実行だけを考えるなら、ぶっちゃけ 「/out/」 あたりを指定しておいてやればいい気がする。 それだけですべてのサブプロジェクトに自動再読み込みが適用される。

自動再読み込みの仕組みを追う

ドキュメントだけを読んでもあんまり理解できないので内部を追いかけたい人用。 (自分もドキュメントだけではわからなくて Ktor のソースコードを見ながらデバッグした。)

ソースコードを見ると、自動再読み込みのためのクラスローダの生成が ApplicationEngineEnvironmentReloading クラスで行われていた。 メソッドとしては createClassLoader メソッドである。 IntelliJ IDEA などの IDE を使っているならデバッグしやすいので、このクラスを IDE 上で開いてブレークポイントをはってデバッグ実行すると処理の流れを追える。

『No ktor.deployment.watch patterns specified, automatic reload is not active』 とか 『No ktor.deployment.watch patterns match classpath entries, automatic reload is not active』 といったメッセージもこのクラスで出力されている。

参考になれば。

Gradle の build scans 機能でビルドの情報を収集してブラウザ上で確認・共有できるぞ

Gradle に build scans 機能が追加されていることに気づいた。

Build scans are an important tool for developing and maintaining Gradle builds. They provide insights into exactly what your builds are doing, helping you identify problems with the build environment, dependencies, performance, and more. They can also help you understand and improve the build more generally, and make collaborating with others easier.

Build Scan Plugin User Manual

Gradle ビルドの情報を収集して、Gradle Enterprise (有償製品) か scans.gradle.com (Gradle が運営している無償の build scan サーバー) に送信し、web ブラウザ上で閲覧できるようにする、というもの。

試してみたところ、以下のような感じで閲覧できた。

Gradle の中身について詳しくなくても、なんとなくパフォーマンスの情報がわかったりしてなかなか便利。 (上のビルドは単純なものなのでそこまで有効活用できる気はしないけど、大規模なプロジェクトだったり、プラグインなどの開発をしていると有効活用できるのではないか。)

使い方

ドキュメントを見ればすぐに使える。

最初に試すときには、build.gradle ファイルはいじらずに ./gradlew --scan build という感じで --scan オプションを付けてやると自動的に build scan プラグインが適用された状態になる。 送信先は scans.gradle.com で、送信前には scans.gradle.com terms of service への同意確認がある。

収集される情報

上記部分に書かれている。 公開したくない情報が含まれないかどうかは注意が必要。

Gradle のマルチモジュールプロジェクトで maven-publish プラグインを使う場合の依存関係

Gradle でビルド成果物 (build artifact) を Maven リポジトリに公開するためのプラグインとして、maven-publish プラグインがある。

maven-publish プラグインとマルチプロジェクトの依存関係

簡単な例

Java ライブラリのプロジェクトがあったとして、JAR ファイルを Maven リポジトリに公開するための build.gradle の記述は以下のようになる。

// Java ライブラリのビルド用の設定などは省略

apply plugin: 'maven-publish'

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }
}

これで publishToMavenLocal タスクなどが使えるようになるので、例えばローカルホスト上の Maven リポジトリに公開するには ./gradlew publishToMavenLocal コマンドを実行すればよい。

依存関係

上の例では、ソフトウェアコンポーネントとして java コンポーネントを指定している。 このとき、依存関係としては runtime コンフィギュレーションのものが使われる。

Name Provided By Artifacts Dependencies
java The Java Plugin Generated jar file Dependencies from 'runtime' configuration
web The War Plugin Generated war file No dependencies
Maven Publishing (new) - Gradle User Manual

マルチモジュールプロジェクトでの依存関係

例えば :foo プロジェクトと :bar プロジェクトからなるマルチモジュールプロジェクトで、:bar プロジェクトから :foo プロジェクトに依存している場合に、出力される pom.xml に記載される依存関係はどうなるのか?? 具体的には bar/build.gradle に以下のように書かれている状況である。

dependencies {
    compile project(':foo')
}

これはドキュメントには書かれていないが、maven-publish プラグインソースコードを確認したところ、依存先プロジェクトの設定に応じて自動的に依存先ライブラリの指定がなされるようになっていた。

例えば、依存先プロジェクトで maven-publish プラグインが使われていない場合は、プロジェクトのグループやプロジェクト名、バージョンが使われるようである。 依存先プロジェクトで maven-publish プラグインが使われており、特定の条件を満たす publication が存在する場合は、その publication で指定されているグループ・artifactId・バージョンが使われる。

publishing {
    publications {
        maven(MavenPublication) {
            groupId 'org.example'
            artifactId 'foo-sample'
            version '1.1'

            from components.java
        }
    }
}

foo/build.gradle に上のように書かれていたら、./gradlew :bar:publishToMavenLocal タスクで公開される成果物の pom ファイルには以下の内容が含まれることになる。

    <dependency>
      <groupId>org.example</groupId>
      <artifactId>foo-sample</artifactId>
      <version>1.1</version>
      <scope>compile</scope>
    </dependency>

気を付ける必要があること

依存先プロジェクトの中で maven-publish プラグインが使用されて、publication が定義されている必要がある。

下記のように、親プロジェクトで子プロジェクトの publication をまとめて定義することもできるのだが、そうすると依存関係の解決時に 「依存先プロジェクトでは publication がない」 という扱いになってしまって、デフォルト値が使用される。

publishing {
    publications {
        mavenFoo(MavenPublication) {
            groupId 'org.example'
            artifactId 'foo-sample'
            version '1.1'

            from findProject(':foo').components.java
        }

        mavenBar(MavenPublication) {
            groupId 'org.example'
            artifactId 'bar-sample'
            version '1.1'

            from findProject(':bar').components.java
        }
    }
}

私はこれでハマってしまった。 (Exposed の build.gradle を見て、親の build.gradle で子プロジェクトの publication を宣言していたので、それを参考にした、という。) 注意されたし。