torutkのブログ

ソフトウェア・エンジニアのブログ

Javaプログラムへの初期設定の与え方を悩む

Javaプログラムに初期設定を外部から渡す方法として、プログラム起動時のコマンドラインオプション、設定ファイル、システムプロパティ、リソースバンドル、プリファレンスAPIjava.util.pref)辺りが候補に挙がります。
今回、PCからシリアル通信でセンサー機器と通信し、センサー機器の状態をGUIで表示するスタンドアロンプログラムを作成しています。プログラムの構造は、GUIに纏わる部分、シリアル通信に纏わる部分とを疎結合しています。ここで、疎結合ゆえにプログラムの初期設定をどのように渡すかを悩むこととなりました。

候補となる方法

コマンドライン引数

GUIJavaFXで作成しています。JavaFXコマンドラインを解析し、JavaFXのParametersクラスにキー・バリュー形式で保持する仕組みを持っています。しかし、このParametersクラスをシリアル通信部分に渡してしまうと、シリアル通信部分がJavaFXのライブラリへ依存してしまい、結合を疎にする観点では後退してしまいます。

設定ファイル

プログラムの起動時に所定のパスにあるProperties形式の設定ファイルを読み込み、これを参照する方法であれば、Java SEの標準APIjava.util.Properties)への依存で済むので、疎結合をそれなりには保てます。しかし、設定ファイルから生成したPropertiesインスタンスGUIの各所とシリアル通信の各所に持ち回す必要が生じます。
JavaFXでは、通常上位のApplication派生クラスからコントローラクラスへは直接参照を持たないので、プロパティを渡すためにコントローラーの参照を取得するコードをApplication派生クラスに追加し、またコントローラーにはプロパティを渡すメソッドを追加し、コントローラーからさらに各クラスに渡すといった持ちまわしが生じます。
シリアル通信においても、そのプロパティを各クラスに渡す持ちまわしが生じます。
この持ちまわしを避けるとしたら、プロパティをシングルトンで実装するといったプログラム全体のグローバル参照が発生します。
今回は小さなプログラムなので、1つのプロパティでもそう煩雑にはなりませんが、大きなプログラムになると、1つのプロパティに数百個以上のキー・バリュー定義が格納されることになり、あまり望ましい姿ではありません。

システムプロパティ

Java起動時のコマンドラインJVMオプション)でシステムプロパティを定義することができます。
システムプロパティは、Java SE標準のAPIjava.lang.SystemクラスのgetPropertyメソッド)で取得できるので、疎結合の点で問題はなく、持ちまわしも不要ですが、多数の項目があるとJava起動時のコマンドラインが膨大となってしまいます。

リソースバンドル

Javaには、主に国際化対応の仕組みとしてロケールに応じて切り替える文字列等のリソースをプロパティファイルまたはクラスに切り出し、実行時にロケールに応じたファイルを読み込むリソースバンドルの仕組みがあります。このファイルに初期設定を書く方法がありますが、リソースファイルは通常JARファイルの中に収められるので、インストール後に設定を変更する場合、JARから取り出し修正したファイルを再度JARに戻すという手順が必要になり、これも好ましくありません。また、リソースバンドルは主にGUIの表示で使うので、シリアル通信などGUIから切り離した内容を記述するのは不適切と思われます。

プリファレンスAPI

Javaには、プリファレンスAPIjava.util.prefs)があり、ユーザー毎に異なる設定を保存・読み出して使用することと、ユーザー共通の設定を保存・読み出して使用することができます。
これはJava SE標準APIなのでシリアル通信からJavaFXへの依存はなくて済みます。
プリファレンスAPIを通して設定する内容は、Windows OSの場合はレジストリに格納され、UNIX系OSの場合はユーザー毎の設定はユーザーのホームディレクトリ下のファイルに、ユーザー共通の設定はシステムで一意の場所にファイルで置かれます。
レジストリやファイルは、プログラムをインストールして最初にプリファレンスを保存するまで生成されないので、インストール直後から初期設定を置くにはプリファレンスAPIを使って保存操作を最初にする必要があります。また、実行するユーザーの権限ではユーザー共通の設定を書き込むことができないことがあります(通常このケースが多いかと)。プリファレンス設定ツールが必要になるかもしれません。

結局どれにしよう?

用途によりトレードオフすることとなりそうです。

Java読書会BOFは6月から「Java 11 and 12 - New Features」を読み始めます

Java読書会BOFは、6月から新しい書籍「Java 11 and 12 - New Features」(洋書)を読み始めます。

Java SE 9が2017年9月にリリース後、この2019年5月末時点までに、Java SE 10(2018年3月)、Java SE 11(2018年9月)、Java SE 12(2019年3月)とリリースされ、Java SE 11については、長期間サポート版(LTS: Long Term Support)となっています。

ところが、この2年内のJavaのアップデートについてしっかり書かれた日本語書籍はほとんどなく、洋書で勉強するとなった次第です。

Java読書会BOFでは、過去洋書を扱ったときは事前に分担を決め日本語訳をして、それを当日配布して日本語訳を朗読するという方法を取ってきました。
これは、精読できるのですが事前の準備負担が大きいので対応し難くなっています。今回は、事前訳はせずにその場で粗い日本語で朗読を進めようかと思っています。

さて、今回の書籍の章題を列挙すると次になります。

  • 型推論(Type Inference)
  • アプリケーション・クラス・データ共有(Application Class-Data Sharing: AppCDC)
  • ガーベージコレクター最適化(Garbage Collector Optimizations)
  • JDK 10の細々な改善(Miscellaneous Improvements in JDK 10)
  • ラムダ式の引数のローカル変数文法(Local Variable Syntax for Lambda Parameters)
  • イプシロンGC(Epsilon GC
  • HTTPクライアントAPI(The HTTP Client API
  • ZGC
  • フライトレコーダーとミッションコントロール(Flight Recorder and Mission Control)
  • JDK 11の細々な改善(Miscellaneous Improvements in JDK 11)
  • switch式(Switch Expressions)
  • JDK 12の細々な改善(Miscellaneous Improvements in JDK 12)
  • プロジェクトAmberにおける列挙型の改善(Enhanced Enums in Project Amber)
  • データクラスとその使用(Data Classes and Their Usage)
  • 生文字列リテラル(Raw String Literals)
  • ラムダの食べ残し(Lambda Leftovers)
  • パターンマッチング(Pattern Matching)

JDKの新しい機能としてちらっと見聞きしたことのある、でも良くわからない機能が並んでいます。
今回の読書会を通じて、これらの新機能について一定の理解をしておくことができるのはなかなかに期待度が高いです。

今月のJava読書会は、6月15日(土)に川崎駅近くで開催します。

Java読書会6月からの本の投票

Java読書会BOFの書籍選定

Java読書会BOFでは、月1回の読書会を開催しており、5月で「Effective Java第3版」をほぼ読了し、6月からは新しい本の読書会を開始します。現在読書会のWebサイトでWeb投票実施中(5月26日〆切)です。

Javaの技術動向は変化が大きくなってきている

Javaは、2017年9月に Java SE 9がリリースされ、そこではModule Systemが導入されています。また、Java SE 9以降はこれまでの2~3年に1回のメジャーバージョンアップをリリースする形態から、半年毎にメジャーバージョンアップをリリースする形態に変更され、新機能が早い段階でリリースされ利用可能になっています。2018年3月にJava SE 10、2018年9月にJava SE 11、2019年3月にJava SE 12と半年毎にバージョンアップされています。

一方で安定的に利用したいユーザー向けには3年に1回、長期サポート版(LTS)を提供するようになりました。最初のLTS版は、2018年9月のJava SE 11です。これは少なくとも次のLTS版まで(則ち最低3年間)はセキュリティパッチを含む修正版が提供されます。LTS版以外は、次のバージョンがリリースされるまでの間(則ち半年間)だけは修正版が提供されます。

余談、有償化騒ぎについて

このLTS版については、Oracleが提供するOracle JDKについては商用利用(個人利用および商用利用でも開発・試験・デモは除く)が有償となっているので、すわJavaが有償化だと一部で騒ぎになっていました。しかし、Javaはずいぶん前からオープンソース開発(OpenJDK)となっており、特に最初のLTS版であるJava SE 11ではOracle JDKに搭載されていた商用機能(Flight Recorder等)がOpenJDKに搭載され、Oracle以外にもいくつかの組織からLTS版が無償提供されています。

読書会で読みたい本が・・・

このようにJavaの進化は短い時間で次々行われてきていますが、日本語での書籍でJava SE 9以降の新しい機能を詳しく解説しているものはほとんど出版されていません。

となると、Javaの新しい技術を読みたいとすると洋書を選択することになります。過去読書会でも幾度か洋書を読んできたので、それほど敷居が高いということはありませんが、洋書ウォッチャーが少ないのでこれぞという本を選ぶのが大変です。

Java Platform Module System(JPMS)の本

Java SE 9で導入され、なかなか骨のある(移行が大変、理解が大変)割にプログラミングの改善にはあまり寄与していない(APIの実装隠蔽や、ソフトウェアのライフサイクル上はとても重要な)技術がJPMSではないでしょうか。

JPMSを扱った書籍を調べてみると、次が見つかりました。

最初の本はまだ未完です。2番目以降はJava SE 9リリース時期(2年前)に出版されているので、ちょっと古いなというのと、ページ数がやや多いが難です。
(洋書の読書会の場合、事前に粗く訳してくるので、ページ数が多いとそれだけ大変)

ということで、JPMS本は今回は推薦せずにおこうかと思います。

さくらVPSのOS更新

さくらVPSのOSをCentOS 6からCentOS 7へ更新

さくらVPSのサーバーは利用開始時から長らくCentOS 6でしたが、このGWで時間が取れたのでやっとCentOS 7へ更新してみました。

OSの再インストールをするので、データのバックアップ、設定のバックアップを取ってから作業に着手します。Let's EncryptのSSL証明書(無料)を利用していたので、再インストール時には、スタートアップスクリプトとして用意されているLetsEncryptを追加しています。

とくに問題なくCentOS 7になりました。IPv6が無効なので有効にして、root以外の作業用アカウントを作成し、SSHポートを標準から変更し、rootのsshログインを無効にしました。

Redmineの稼働にむけて

Ruby 2.4のインストール

RedmineはOS更新前はバージョン3.4系で稼働していましたが、そろそろ4.0系も動かしたいところです。Redmine 4.0は、Ruby on Rails 5.2を用いているため、rubyのバージョンは2.2.2以降となります。しかし、CentOS 7の標準rubyは2.0のため、rubyを別途更新する必要があります。

rubyenvは開発者個人の環境で使うのはまだしも本番環境で使うには気持ち悪いので、以前は自前でRPMビルドしていました。

今回は手元にCentOS 7環境がないので、他の手段を探してみたところ、Red Hat Software Collections というyumリポジトリRed Hatから提供されており、これを利用するのがよさそうです。

$ sudo yum install centos-release-scl-rh 

rubyについては、rh-ruby22、rh-ruby23、rh-ruby24、rh-ruby25とrubyの新しバージョンが用意されています。今回は、Redmine 3.4が対応する最新のrh-ruby24を入れます。

$ sudo yum install rh-ruby24

Red Hat Software Collectionsパッケージは、/opt/rh以下にインストールされます。結構パスが深いのですが、パスを通すenableというファイルが提供されているので、これを/etc/profile.d下にリンクします。

$ sudo ln -s /opt/rh/rh-ruby24/enable /etc/profile.d/rh-ruby24.sh
ruby develのインストール
$ sudo yum --enablerepo=centos-sclo-rh install rh-ruby24-ruby-devel

rubygem bundlerのインストール
$ sudo yum --enablerepo=centos-sclo-rh install rh-ruby24-rubygem-bundler
rubygem rakeのインストール
$ sudo yum --enablerepo=centos-sclo-rh install rh-ruby24-rubygem-rake
  • メモ)rakeは事前インストール不要

Development Toolsのインストール

Redmineインストール手順では、yum groupinstallでDevelopment Toolsをインストールしますが、これが次の結果となりました。

$ sudo yum groupinstall "Development Tools"
Loaded plugins: fastestmirror, langpacks
There is no installed groups file.
Maybe run: yum groups mark convert (see man yum)
Loading mirror speeds from cached hostfile
 * base: ftp.iij.ad.jp
 * epel: ftp.iij.ad.jp
 * extras: ftp.iij.ad.jp
 * updates: ftp.iij.ad.jp
Warning: Group development does not have any packages to install.
Maybe run: yum groups mark install (see man yum)
No packages in any requested group available to install or update

これは最初エラーかなと思いましたが、実は必要なパッケージはインストール済みであるときに出るようです。

MariaDBのインストール

最新のRedmineインストール手順(redmine.jp)は、データベースにPostgreSQLを使うものになっています。今回は、MySQLMariaDB)のダンプを使うので、MariaDBをインストールします。

$ sudo yum install mariadb-server mariadb-devel
  :

MySQL/MariaDBでutf8を指定した場合、UTF-8のうち4バイト文字を扱えません。例えば絵文字が該当します。そこで、UTF-8の4バイト文字を扱えるようにするにはutf8mb4を指定します。

/etc/my.cnf.d/server.cnf
[mysqld]
character-set-server = utf8mb4
/etc/my.cnf.d/mysql-clients.cnf
[mysql]
default-character-set = utf8mb4
show-warnings
MariaDB自動起動設定
$ sudo systemctl enable mariadb.service
Created symlink from /etc/systemd/system/multi-user.target.wants/mariadb.service to /usr/lib/systemd/system/mariadb.service.
mysql_secure_installation実行

セキュアな初期設定を行うツール mysql_secure_installationを実施します。ツールを実行すると次の有無を尋ねられ、基本Yesを設定していきます。

  • MariaDBのrootユーザーのパスワードを設定
  • 匿名ユーザーを削除
  • MariaDBのrootユーザーのリモート接続禁止
  • testデータベースとそのアクセス設定を削除
redmineのユーザーアカウントとデータベース作成
$ mysql -uroot -p
MariaDB [(none)]> CREATE DATABASE redmine CHARACTER SET utf8mb4;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> CREATE USER 'redmine'@'localhost' IDENTIFIED BY '********';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> GRANT ALL PRIVILEGES ON redmine.* TO 'redmine'@'localhost';
Query OK, 0 rows affected (0.00 sec)

ImageMagickのインストール

$ sudo yum install ImageMagick ImageMagick-devel ipa-pgothic-fonts
  :

Redmine実行ユーザー作成

Redmineを実行する際、root権限ではなくredmine実行ユーザー権限とします。

$ sudo groupadd -g 1000 redmine
$ sudo useradd -u 1000 -g redmine redmine
$ sudo passwd redmine
  :

Redmineのダウンロード

$ sudo mkdir /var/lib/redmine-3.4-stable
$ sudo chwon redmine:redmine /var/lib/redmine-3.4-stable
$ su redmine
$ cd /var/lib
$ svn co http://svn.redmine.org/redmine/branches/3.4-stable redmine-3.4-stable
  : 

database.ymlの作成

production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: "********"
  encoding: utf8mb4

configuration.ymlの作成

production:
  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      address: "localhost"
      port: 25


imagemagick_convert_command: /usr/bin/convert
rmagick_font_path: /usr/share/fonts/ipa-pgothic/ipagp.ttf

Redmineが依存するgemパッケージのインストール

$ bundle install --without development test --path vendor/bundle
  :

セッション秘密鍵生成

$ bundle exec rake generate_secret_token

バックアップしたデータベースを復元

$ mysql -uredmine -p redmine < /tmp/redmine_mysql_www.torutk.com-20190430T110540.dump
Enter password:********
データベースのマイグレート
$ RAILS_ENV=production bundle exec rake db:migrate

unicornのインストール

Gemfile.local新規作成
  • /var/lib/redmine-3.4-stable/Gemfile.local
gem "unicorn"
gemパッケージのunicornインストール
$ bundle update
unicorn.rb

動作確認
$ bundle exec unicorn_rails -c config/unicorn.rb -E production

エラー発生

I, [2019-04-30T21:28:36.880592 #27919]  INFO -- : Refreshing Gem list
ArgumentError: wrong number of arguments (given 0, expected 2)
  /var/lib/redmine-3.4-stable/vendor/bundle/ruby/2.4.0/gems/unicorn-5.5.0/lib/unicorn.rb:49:in `block in builder'

unicorn 5.5.0だと発生するとのこと。
stackoverflow.com

unicorn 5.4.1で止めおくのが回避策とあるので、

redmine-unicorn.service
[Unit]
Description=Redmine Unicorn Server
After=mariadb.service

[Service]
WorkingDirectory=/var/lib/redmine
Environment=RAILS_ENV=production
SyslogIdentifier=redmine-unicorn
PIDFile=/var/lib/redmine/tmp/pids/unicorn.pid

ExecStart=/opt/rh/rh-ruby24/root/bin/bundle exec "unicorn_rails -c config/unicorn.rb -E production"
ExecStop=/usr/bin/kill -QUIT $MAINPID
ExecReload=/bin/kill -USR2 $MAINPID

User=redmine
Group=redmine

[Install]
WantedBy=multi-user.target

実行するとエラー

$ sudo systemctl start redmine-unicorn.service

原因を調べるため、suコマンドでユーザーを切り替えてunicornを実行するとエラーとなりました。
エラー内容は

/opt/rh/rh-ruby24/root/usr/bin/ruby: error while loading shared libraries: libruby.so.2.4: cannot open shared object file: No such file or directory

suコマンドで(-なしに)ユーザーを切り替える場合は、profile.dの設定を読み込まないので、rubyのパスが参照できないためです。
これは、systemdで実行ユーザーを指定して実行する場合と同じと考えられます。

systemdのunitファイル記述では、EnvironmentFile指定が可能ですが、このファイル内では変数展開ができないため、先に設置した/etc/profile.d/rh-ruby24.shを指定することができません。

回避策として、実行コマンドをsclコマンドで指定します。

  • /usr/lib/systemd/system/redmine-unicorn.service (修正箇所)
ExecStart=/usr/bin/scl enable rh-ruby24 -- bash -c 'bundle exec unicorn_rails -c config/unicorn.rb -E production'

Red Hat Software Collectionsのコマンドsclを使ってコマンドを実行すると、必要な環境設定が反映されます。

nginxとの連携

  • バックアップしていたredmine.confを/etc/nginx/conf.d/に配置
  • /etc/nginx/conf.d/default.confを削除、空のdefault.confに置き換え
  • /etc/nginx/conf.d/https.confを削除、空のhttpd.confに置き換え

nginxのアップデート時にdefault.confとhttps.confが生成されないよう、削除だけではなく空のファイルを置いておきます。

テーマのインストール

gitmike
$ cd /var/lib/redmine/public/themes
$ git clone git://github.com/makotokw/redmine-theme-gitmike.git gitmike

プラグインのインストール

$ cd /var/lib/redmine/plugins
Issue Template

現時点でmasterブランチはredmine 4.0以降のみ対応となっています。redmine 3.xで使うときは、ブランチ名にv0.2.x-support-Redmine3 を指定します。

$ git clone -b v0.2.x-support-Redmine3 https://github.com/akiko-pusu/redmine_issue_templates.git
Banner

現時点でmasterブランチはredmine 4.0以降のみ対応となっています。redmine 3.xで使うときは、ブランチ名にv0.1.x-support-Redmine3 を指定します。

$ git clone -b v0.1.x-support-Redmine3 https://github.com/akiko-pusu/redmine_banner.git
Wiki Extensions

現時点で存在するブランチdevelopおよびmasterはともにredmine 4.0以降のみ対応となっています。redmine 3.xで使うときは、タグ名に0.8.2を指定します。

$ git clone https://github.com/haru/redmine_wiki_extensions
XLS Export
$ git clone https://github.com/two-pack/redmine_xls_export.git
$ bundle install

gemパッケージのspreadsheet を必要とするので、bundle installを実行します。

Clipboard Image Paste
$ git clone https://github.com/peclik/clipboard_image_paste.git
Wiki Lists
$ git clone https://github.com/tkusukawa/redmine_wiki_lists.git
LaTeX MathJax Macro
$ git clone https://github.com/mboratko/redmine_latex_mathjax.git
$ bundle install
  • gemパッケージのrender_parentを必要とするので、bundle installを実行します。
Sidebar Hide
$ git clone https://gitlab.com/bdemirkir/sidebar_hide.git
Glossary
$ git clone https://github.com/torutk/redmine_glossary.git
Github hook
$ git clone https://github.com/koppen/redmine_github_hook.git

設定の復旧は後日実施。

View Customize
$ git clone https://github.com/onozaty/redmine-view-customize.git view_customize
$ bundle install
  • ディレクトリ名はview_customizeでないといけない
  • gemパッケージのactiverecord-compatible_legacy_migrationを必要とするので、bundle installを実行します
Startpage
$ git clone https://github.com/gatATAC/redmine_startpage.git

添付ファイルの復元

バックアップを取っていたマシンからrsyncで転送します。

リポジトリSVN、Git)の復元

SVNは、ダンプファイルをインポートします。
Gitは、git clone --mirror でクローンしたリポジトリから配置したい場所へpushします。

NginxからUnicorn使用

Nginxの設定ファイルを戻します。

およそ復元完了

4月30日の昼頃から作業を開始して、5月1日の昼頃までかかってしまいました。
平成のうちには更新が終わらなかったです。

次はRedmine 4.0へのアップデートをする予定です。

GPSの高度と標高

GPSの高度と標高

GPSを使った測位では、地球を回転楕円体(WGS84回転楕円体)としたときの緯度・経度および高度を取得します。

ここで、GPS測位で得られる高度は回転楕円体面からの距離として算出されるので、実際の標高(海面からの距離)とは異なります。回転楕円体の表面と海面(ジオイド面)とは場所によりおよそプラスマイナス100mの範囲で差異があります。

東京付近では、回転楕円体表面から40mほど高い位置にジオイド面があります。したがって、GPSの高度から40mを減算すると標高(日本の標高は、東京湾の平均海面を0mとしたときの高さ)となります。

ジオイド高は、国土地理院ジオイド高計算サイトで計算させることができます。
https://vldb.gsi.go.jp/sokuchi/surveycalc/geoid/calcgh/calcframe.html

JDKのアップデート(2019年4月)

Java SE Development (JDK) の2019年4月アップデート

JDKは、現在年に2回のバージョンアップと年に4回のセキュリティとバグ修正のアップデートが行われています。バージョンアップの時期は3月と9月、アップデートは1月、4月、7月、および10月です。

そして、この4月のアップデートでは和暦対応(来月から元号が「令和」となる対応)も含まれています。

JDKのバイナリは現在、Oracleから提供されるOracle JDKとOpenJDKの他、AdoptOpenJDK、Zulu、Liberica、Red HatAmazon Correttoなどがあります。

Oracle JDK

Oracle JDK は、次のアップデートが公開されています。

Oracle JDKJava SE 8は、2019年1月で無償提供するアップデートは終了となっていますが、今回は Oracle Java SE OTNライセンス(Oracle Technology License Agreement for Java SE)の範囲で無償提供されました。(OTNライセンスでは、個人利用・開発用途で無償利用可)

Oracle JDKJava SE 11 はLTS(長期サポート版)なのでJava SE 12がリリースされた後も更新版が提供されています。一方、非LTSのJava SE 9、Java SE 10はアップデートは提供されていません。Java SE 11は商用ライセンス(Java SE Subscription)か若しくはOTNライセンスでの提供です。

OTNのダウンロードサイトからOracle JDKJava SE 11.0.3をダウンロードしようとすると、「Oracleプロファイルへのサインイン」が要求されました。Java SE 12.0.1のダウンロードでは要求されませんでした。無償で登録できますが、OTNアカウントと同じ模様です。

Oracle JDKJava SE 12 は非LTSで、次のJava SE 13がリリースされるまでの期間アップデートが提供される予定です。

Oracle OpenJDK

Oracleが無償提供するOpenJDKは、最新バージョンのJava SE 12のアップデートが提供されています。

AdoptOpenJDK

AdoptOpenJDKファウンデーションが無償提供するOpenJDKは、4月20日時点で次のアップデートが提供されています。

  • OpenJDK 8u212
  • OpenJDK 11.0.3

Zulu JDK

Azul Systemsが提供するOpenJDKは4月20日時点で次のアップデートが提供されています。

  • Zulu OpenJDK 7u222
  • Zulu OpenJDK 8u212
  • Zulu OpenJDK 11.0.3
  • Zulu OpenJDK 12.0.1

ZuluFX JDK

Azul Systemsが提供するOpenJDK(JavaFX同梱版)は、4月20日時点ではまだアップデートが提供されていません。前回(2019年1月のアップデート)も少し遅くリリースされたので、推測では1か月以内にはリリースされるかと思われます。

5月4日時点で次がリリースされています。

  • ZuluFX 8u212
  • ZuluFX 11.0.3

Liberica JDK

BellSoft社が提供するOpenJDK(JavaFX同梱版)は、4月20日時点で次のアップデートが提供されています。

  • Liberica JDK 8u212
  • Liberica JDK 11.0.3
  • Liberica JDK 12.0.1

AWS Corretto

Amazonが提供するOpenJDKは、4月20日6月1日時点で次のアップデートが提供されています。

JDK 8のアップデートも近日リリースされると推測します

OpenJDKのダウンロード先等の情報

次のWikiに、各種OpenJDKの情報をまとめています。
www.torutk.com

Java読書会BOF「Effective Java第3版」を読む会(第5回)を開催して

Java読書会開催のデータ

昨日4月13日(土)は、Java読書会BOF主催の「Effective Java第3版」を読む会(第5回)を開催しました。

1998年12月にJava読書会が始まってから通算241回目、36冊目の書籍となります。
Effective Javaについては、2002年に第1版を、2008年に第2版をJava読書会で読んでいます。

今回の読書範囲からのメモ

オーバーロード

同じパラメータ数の二つのオーバーロードされたメソッドを提供しないこと

これに尽きますね。オートボクシングがあるので、引数の型がObjectとintでオーバーロードしていると落とし穴に陥りがちです。既存APIにあるので注意が必要です。

可変長引数

引数の個数が0個のことも考慮して実装する必要があります。引数を最低1個必須とする場合は、必須の引数と可変長引数と2つの引数を要求するようメソッドを定義します。

nullをメソッドの戻り値として返さない

nullを返すメソッドは呼び出し側にnullに対応する余分なコードが必要となる上、利用者がそのコードを書き忘れてプログラムのエラーを招く原因となります。

配列またはコレクションを戻り値型とする場合は、nullではなく空コレクションか空配列を返すようにします。

Java SE 8から導入されたOptionalを戻り値型とすると、メソッドが値を返さなかったときにデフォルト値を使う(orElse)、例外をスローする(orElseThrow)、別な処理を呼び出す(orElseGet)を簡潔に実装できます。isPresentはOptionalが提供する他のメソッドでは対応できないときの「安全弁」として使うという解説があり、なるほど~と思いました。

Optionalの使い方は読書会で議論となり、Serializableではないのでフィールドには使うべきではないねとなりました。

ドキュメントコメント

いわゆるJavadocコメントですが、Java SE 8以降も進化していました。

  • @implSpec
  • @index
  • @summary
  • 検査有効(-Xdoclintがデフォルトで有効)
  • HTML5を生成させる-html5オプション

@throwsに、可能性のある非チェック例外を書くのはよいが、メソッド内で外部ライブラリを呼び出しているとすべての非チェック例外を把握しているわけではないのでどうしよう?と議論に。

ライブラリを知り、ライブラリを使う

車輪を再発明しないでください

で、どのようにライブラリを探して使うかについて議論となりました。

  • 検索スキルが必要
  • こういうライブラリがあるはず(必要)、という意識がないとライブラリを探せない
  • ライブラリの更新頻度、ダウンロード数を参考にする
  • 大きなライブラリは避ける
  • 複数のライブラリを使うと、それらのライブラリがたまたま同じライブりに依存するがバージョンが違うときにはまる
  • google検索エンジンでトップに出てくる、内容が薄いQiita記事は何だろうね
  • 類似ライブラリを横並べて比較する、APIの違いを見る
Randomクラスについて

Java 7の時点で、Randomをもはや使うべきではありません。今日ではほとんどの場合、選択すべき乱数生成器はThreadLocalRandomです。それは高品質な乱数を生成し、きわめて速いです。

InputStreamにtransferToメソッドが追加

InputStreamに、long transferTo(OutputStream out) メソッドが追加され、入力ストリームをそのまま出力ストリームに流し込むことが簡単にできるようになりました。

    public static void main(String[] args) throws IOException {
        try (InputStream in = new URL(args[0]).openStream()) {
            in.transferTo(System.out);
        }
    }

おおー、これは便利。java.io.Readerとjava.nio.channels.FileChannelにもtransferToメソッドが追加されています。

toArrayのパフォーマンス

Effective Javaで次の記事が参照されていました。
Arrays of Wisdom of the Ancients

これは、ListインタフェースのtoArrayメソッドを呼ぶ際に指定する引数の値を、長さ0の配列を生成するか、Listの要素数に相当するサイズの配列を生成するかでどちらが速いかを調査した内容です。

List<Cheese> cheeseInStock = ...
return cheeseInStock.toArray(new Cheese[0]);  // (1)
return cheeseInStock.toArray(new Cheese[cheeseInStock.size()]); // (2)

Effective Javaでは、参照記事に基づいて(1)を用い、(2)はパフォーマンスに悪影響があるので使わないことを推奨しています。

(1)はコレクションcheeseInStockの要素数が1以上のときは新しい配列を生成します。(2)は渡した配列が使われ、新しい配列の生成はありません。そこで、(2)の方が性能がよいように見えます。

参照記事ではパフォーマンスを計測した結果、(1)の方がArrayListでは顕著に早く、HashSetではほぼ同じとのデータが得られ、それを考察しています。パフォーマンス計測にはJMHを使っているのでそこは信頼に足ります。

記事をざっと読んだところ次のとおり

  • 配列の生成にかかるコストよりも要素を配列に詰めるコストが支配的
  • ArrayListの場合は、内部の配列の要素をtoArrayで返却する配列にSystem.arraycopyでコピー
  • (1)ではネイティブコードのcheckcast_arraycopy_uninit関数が実行されている
  • (2)ではネイティブコードのcheckcast_arraycopy関数が実行されている。(1)のコードにはないrepz stosb命令(memset to zero)に時間を要している

ちなみに、引数なしのtoArray()では、AVX命令を使ったさらに高速なコピーが行われています。