torutkのブログ

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

Java読書会「Javaコードアンチパターン」を読む会(第6回)開催

Java読書会「Javaコードアンチパターン」を読む会(第6回)開催

Java読書会BOF 主催のJava読書会「Javaコードアンチパターン」を読む会(第6回)を昨日開催しました。

Javaコードアンチパターン 初級からプロまで知っておきたいミスと回避策 (impress top gear) | Tagir Valeev, 柴田 芳樹 |本 | 通販 | Amazon

オーバーフローを避けて数値を比較するときは、自前で実装するよりも Integer.compare(int, int) など各プリミティブ型のラッパークラスに用意されているcompareメソッドを使うのがよいですね。double値の比較において、NaN値や+0.0、-0.0の扱いも考慮されています。

配列・コレクションのソートでは、要素数が32個未満か以上かで使用するソートアルゴリズムが変わり、32個以上で使用するTimSortではソートの不変条件検査があり Comparator の実装にバグがあると(場合によって)不変条件エラーで例外スローされ検知できるとあります。なのでテスト時は要素を32個以上で行うのがよいです。

HashSet/Mapでhashの衝突が多いと同じhash値のバケットに多数の要素が連なります。要素が Comparable のときは、バケットに連なる要素をTreeSetで保持する最適化が行われるということを本書で知りました。

Map#mergeメソッドがあり、すでにキーと値が保存されているときに、mergeメソッドで新しい値をセットすると引数で指定したラムダ式で前の値と新しい値とを引数にとり演算(結合等)をした結果をMapに保存することができます。これも本書で知りました。

コレクションの要素に、継承関係のない2つの型を格納したい場合、共通の型(マーカーinterface)で定義する方法が紹介されていました。

sealed interface Entry { }
record IntEntry(int value) implements Entry { }
record StringEntry(String value) implements Entry { }

void processList(List<Entry> list) {
    for (Entry element : list) {
        switch (element) {
            case IntEntry(int number) -> processNmber(number);
            case StringEntry(String text) -> processString(text);
        }
    }
}

同一ファイルにsealedした型とその派生型を記述するならば、permitsを省略できます。 switch では、elementがEntry型でsealedされ、IntEntryとStringEntryに限定されるので 全ての型が網羅されることがコンパイラで検査できます。

宿題

宿題2 doubleの+0.0と-0.0を==で比較した結果は?

これは、trueとなります。

if (+0.0 == -0.0) {
    IO.println("equals!");
}

浮動小数点数の規格 IEEE 754 で、比較では0の符号を無視するので、==演算はtrueとなり、compareではビットパターンまで見るので+0.0 > -0.0 と判定しています。

一方、Double.compare(+0.0, -0.0)の結果は1と大なりの結果となりました。

宿題3 permitsを省略できる記載をJava言語仕様で調べる

The Java Langauge Specification Java SE 25 Edition を確認しました。

Java SE Specifications

8.1.6 Permitted Direct Subclasses の項に

If the declaration of a sealed class c lacks a permits clause, then the permitted direct subclasses of c are as follows:
* If c is not an enum class, then its permitted direct subclasses are those classes declared in the same compilation unit as c which have a canonical name and whose direct superclass is c.

とあり、sealedクラスにpermits句がない場合、同一コンパイルユニット内にあるcをスーパークラスとするクラスがpermitされているものとなります。

Redmine 7.0.0がリリース、更新の調査

Redmine 7.0.0がリリース

2026年6月30日付で、Redmine 7.0.0がリリースされていました。 VPSで運用しているRedmineを7.0.0に更新しようと思い、OS、Rubyバージョン、プラグイン互換性状況などを整理します。また、ステージング環境でRedmine 7.0.0に更新しての動作状況を見てみました。

Redmine 7.0.0の動作条件

RedmineInstall - Redmine * Rails 8.1をベース * Ruby 3.2, 3.3, 3.4, 4.0 に対応 ** Ruby 4.0.0~4.0.3ではWiki描画が著しく遅い不具合があるため、4.0.4以降を使用推奨 * MySQL 8.0~8.4 に対応

Linux OS系の現状

現在、VPSには Rocky Linux 8をインストールしています。そろそろ更新が必要になりそうなので、Redmineを動作させるためのLinux基盤のバージョンやサポート終了予定時期など確認しておきます。

Red Hat Enterprise Linuxおよび互換OS

  • Red Hat Enterprise Linux
  • Rockey Linux
項目 RHEL 8 RHEL 9 RHEL 10
Kernel 4.18.0 5.14.0 6.12.0
Ruby 2.5, 2.6, 2.7, 3.0, 3.1, 3.3 3.1, 3.3, 4.0 3.3*1
MySQL 8.0 8.0 8.4
メンテナンスサポート終了 2029-05-31 2032-05-31 2035-05-31

RHEL 8でもRuby 3.3を利用可能なので、Redmine 7.0の動作条件を満たします。

Ubuntu Linux

ついでなのでUbuntuではどうかなと調査したメモです。

項目 22.04 24.04 26.04
Kernel 5.15 6.8 7.0
Ruby 3.0 3.2 3.3
MySQL 8.0.46 8.0.46 8.4.10
EOL 2027-04 2029-04-30 2031-04-30

Ubuntuは、OS公式リポジトリを使うなら Ruby 3.2を利用できる24.04以降が必要です。

Debian Linux

Debianではどうかなと調査したメモです。

項目 12 Bookworm 13 Trixie
Kernel 6.1.0 6.17
Ruby 3.1 3.3
EOL 2028-06-30 2030-06-30

Debianは、OS公式リポジトリを使うなら、Ruby 3.3を利用できる 13 (Trixie)が必要です。

プラグイン互換状況

VPSで運用しているRedmineと同じプラグインを、ステージング環境に入れてRedmine 7.0.0の動作状況を見てみました。

プラグイン名 動作状況 対処など
redmica ui extension MermaidマクロでER図表示 OK
redmine banner ブラウザでアクセスするとInternal Error発生
redmine cozy wiki macros 未検証
redmine github 未検証
redmine glossary 動作OK
redmine issue template チケット作成時 Internal error発生
redmine latex mathjax 数式表示 OK
redmine theme changer 動作 OK
redmine wiki extensions サーバー起動エラー発生 Redmine 7.0対応版リリース(2026-07-23)
redmine wiki lists 動作OK
view customize 動作OK

Redmine Wiki Extensions

2026-07-23のv1.3.0でRedmine 7.0.0対応となりました。

古い記述

redmine:plugins:migrate を実行時にエラーとなりました。

NameError: uninitialized constant Redmine::WikiFormatting::Textile::Formatter::RULES (NameError)

  Redmine::WikiFormatting::Textile::Formatter::RULES << :inline_smiles
                                             ^^^^^^^

Redmine 7.0 で Redmine::WikiFormatting::Textile::Formatter::RULESが削除されたため、エラーになっています。Wiki ExtensionsプラグインのRedmine 7.0対応が必要です。2026-07-18時点ではGithubリポジトリ上のdevelopブランチでRedmine 7.0対応の実施中のようです。

developブランチお試し
redmine$ cd plugins/redmine_wiki_extensions
redmine_wiki_extensions$ git checkout develop

エラーなくサーバー実行できています。

Redmine Banner

redmine:plugins:migrate を実行時にエラーとなりました。

Route resources :banner - :only and :except must include only [:index, :create, :new, :show, :update, :destroy, :edit], but also included [:preview, :off].
Redmine Banner のフォーク

Redmine Bannerプラグインをフォークして機能拡張しているプラグインが、7.0に対応しているようです。 Redmine Banner拡張版 - ロールベースバナーとマクロ機能を実装|田波霞一

ロールによってメッセージ有無を設定できるほか、バナーメッセージ内にマクロを入れ込み動的な表示に対応しているそうです。

Redmine 7.0.0でのお試し

Redmine Bannerプラグインのアンインストールを実施

redmine$ bundle exec rails redmine:plugins:migrate NAME=redmine_banner VERSION=0 RAILS_ENV=production
== 5 ChangeColumnBannerDescription: reverting =================================
  :(中略)
== 1 CreateBanners: reverted (0.0043s) ======================================== 
redmine$ rm -rf plugins/redmine_banner/
redmine$

フォーク版のインストール

redmine$ cd plugins/
plugins$ git clone https://github.com/seraph3000/redmine_banner.git
  :
plugins$ bundle exec rails redmine:plugins:migrate RAILS_ENV=production
== 1 CreateBanners: migrating =================================================
  :

軽く触ってみた限りでは、Redmine 7.0.0では動作している感じです。

*1:dnf module機能廃止によりOS公式リポジトリからは1バージョンのみ提供

Java読書会「Javaコードアンチパターン」を読む会(第1回)開催

2月28日(土)、Java読書会BOF主催のJava読書会「Javaコードアンチパターン」を読む会の第1回を開催しました。

著者のTagir氏は、IntelliJ IDEAなどのプログラミングツールを開発・販売しているJetBrains社のエンジニアでJavaチームに所属、IntelliJ IDEAの静的解析機能の開発をしています。

Java読書会BOFの読書会恒例で、書籍の表紙、表紙のそで(折り返し部分の記述)から、推薦の言葉、目次、序文、まえがき、謝辞、著者紹介、本書についてをしっかりと朗読して第1章に入りました。

本書の第1章コード品質の管理 では、コードスタイル、静的解析ツール、テスト、カバレッジなどに触れられており、最近の主要な静的解析ツールの紹介も記載されています。カバレッジではミューテーションカバレッジにも言及しています。

読書会の議論では、IntelliJ IDEAを使っているなら個人環境で静的解析ツールを入れなくても強力なサポートが得られるから、CD・CIでサーバーでビルドするときに静的解析ツールのチェックを入れるのではといった話や、ミューテーションカバレッジとはどういう振る舞いをして、どのような効果が得られるのかという話、動的解析で言及のあるjavapathfinderは、名前から察したとおりNASA謹製で、利用経験のある参加者からはものすごい計算機リソース(メモリ)と時間を費やすとの経験談もありました。

1.3.2項 静的解析ツールの活用 では、int値に対するビットシフトで32bit左にシフトするという誤った記述の検査について、

int y = x << 32; 本文では クイックフィックスとして x << 0 に変更することが提案されています。これはコードの動作は変えないものの、問題の解決にはならないでしょう。

とあり、また訳注18では

int値を32ビット、・・・シフトさせようとすると元の値自身が返される

とありますが、Java言語仕様で、int値のビットシフトにおいては、右オペランドの値の下位5ビットしか使われないとの規定があることに気づかされました。非常に含蓄ある書籍と皆感じていました。

1.4節 自動テスト では、Clonableインタフェースの実装について例示していましたが、普段ほとんどClonableの実装を書くことはしていないので、ちょっと理解に時間を要しましたが、ここであらためてJavaの基本的な仕様を学ぶことができました。

とても些細なことですが、参加者の一人から、p.7の図1.2 Eclipseの画面キャプチャにおいてタイトルバーのアイコンがIntelliJのものになっているように見えるね、と気づきがありました。MacOS上の画面キャプチャですが、拡張子.javaがIntelliJに結びついている場合にあり得るのではということのようです。

と、この1章だけでもとっても濃い内容でした。

第2章では、Apache Avroという割と著名なオープンソース製品のコード記述でビットシフト演算子を用いて値を25%(4分の1)とするコード例が演算子の優先順位で誤っている例の紹介がありました。CPUの命令実行では除算演算よりもビットシフトが圧倒的に速いので、最適化を意図して記述したもののバグを持ち込んでしまったということです。ここで書籍では

最新のJVMで使われるJIT(Just-In-Time)コンパイラやAOT(Ahead-Of-Time)コンパイラは、機械コードを生成する際に乗算や除算をシフト演算子に書き換えられるので

とありましたが、本当にそうなの?ということで本読書会ではこれを実際に確認することが宿題となりました。

次回は、3月28日(土)開催です。 参加案内は、Webページ Java読書会BOF に掲載されております。

http://www.javareading.com/bof/

Java読書会「Spring徹底入門 第2版」を読む会(第15回)を終えて

Java読書会BOF 主催「Spring徹底入門 第2版」を読む会(第14回)を12月20日(土)に開催しました。

今回は、12章 Thymeleafの残りと14章チュートリアルの途中、エンティティの実装まで読み進めました。14章のチュートリアルは、参加者がそれぞれ持参のPCで実際にPostgreSQLのユーザー・データベース作成、Spring Bootのプロジェクトを作成してソースコードの打ち込みをしながら進めました。

チュートリアルにおいて引っかかったことなど

実際に手を動かしながら実施すると、書籍を読むだけでは気づかないことや引っかかる事項がいろいろと出てきます。これらを調べ議論しながら進めたことで知識が深まり知見が広がりました。

Spring Framework、Spring Bootのバージョン

先月2025年11月に、Spring Framework 7.0 および Spring Boot 4.0がリリースされました。 書籍で言及されているSpring Bootの構築は、WebサービスSpring Initializr を利用して作成します。Spring Initializrで選択可能なSpring Bootバージョンは、最新バージョンと一つ前のマイナーバージョンで本日時点では 4.0.1または3.5.9 となっています。書籍のチュートリアルは Spring Boot 2.7を前提としていますが、事前に試した限りでは 3.5でも 一部修正をすればビルド・実行は可能でした。

修正:依存関係でthymeleaf-extras-springsecurity4 をthymeleaf-extras-springsecurity6 に修正

PostgreSQL ユーザー作成時のパスワード指定

書籍では、次のように mrs ユーザーを作成するPostgreSQLコマンドで、パスワードにmd5ハッシュコード化したものを指定しています。

CREATE ROLE mrs LOGIN
  ENCRYPTED PASSWORD 'md586082399b5082acb54472ee195a57ce8' ...
                                                 

PostgreSQLは、バージョン10あたりから ENCRYPTEDの指定は意味をなさなくなり(後方互換性のために残されている)、パスワード文字列がハッシュコードの場合はそのまま(再ハッシュせずに)パスワードとして保存されます。md5ハッシュを使用するときは、先頭にmd5を付け、その後ろにmd5ハッシュ文字列を指定します。指定可能なハッシュは、md5およびSCRAM-SHA256です。

ここで発生した問題は、書籍ではユーザーのパスワードを mrs と記載しているのですが、上述のCREATE ROLE文で指定しているパスワードのmd5ハッシュ文字列が、mrsをmd5ハッシュ化したものと一致しないことでした。書籍の通りユーザーを作成すると認証ができません。 世の中には、md5のハッシュから元の文字列を逆生成するWebサイトがあり、書籍のmd5ハッシュを入れたところ、mrsmrs と結果が十秒足らずで得られました。md5はかなり脆弱ですね。

ところで、PostgreSQLサーバーとはSSH接続でコマンドを実行するなら、パスワードを平文で指定してもいいのかなと思いましたが、この関連でインターネットを調べていたときに、コマンド実行履歴(history)で見れてしまうという点を見かけ、なるほどと思いました。とはいえ、平文をハッシュにするときをどうするか、もあるので平文パスワードを扱う場所をどこに限定するかは別途検討すべき事項と思いました。

LC_COLLATE と LC_CTYPE

データベースを作成するときのパラメータで、書籍ではどちらも "C" を指定していました。 LC_COLLATEは、文字列の並び順(照合順)を決める設定で、Cは、Unicodeの文字(コードポイント)のバイト順で判断するが、高速に動作し、ja_JP.UTF-8 を指定すると日本語の文字も考慮された並びとなるが、動作が遅くなるという模様です。

LC_CTYPEは、文字の種類を分類するための設定で、文字、数字、大文字、小文字などの区別をするものになります。Cは、ASCII文字範囲(英字)を分類します。

テーブル名 usr

なぜかテーブル名に usr と省略形を使っています。userではいけないのかな? と疑問に思いました。 これは、userとしてしまうと、SQL予約語(CREATE USER ... のUSER)と衝突してエラーになってしまいます。 推奨の回避方法は、名前をuser以外にする(推奨は複数形のusersですが)、あまりよくない回避方法は"user"とクォートする、ただしこの場合、JPAが生成するSQLでもuserをクォートさせる設定を別途行う必要があり、筋が悪い回避方法となってしまいます。

複合キーのクラスReservableRoomId でequalsとhashCodeのオーバーライド

複合キーを使用するエンティティ(テーブル)で、複合キーを表すクラスは包含するキーの値が同じ場合はインスタンスが違っていても同値とする(equalsメソッドがtrueを返し、hashCodeメソッドが同じ値を返す)実装が必要です。

書籍では、次のようなequalsメソッドとhashCodeメソッドの実装をしています。

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        ReservableRoomId other = (ReservableRoomId) obj;
        if (reservedDate == null) {
            if (other.reservedDate != null) {
                return false;
            }
        }  else if (!reservedDate.equals(other.reservedDate)) {
            return false;
        }
        if (roomId == null) {
            if (other.roomId != null) {
                return false;
            }
        } else if (!roomId.equals(other.roomId)) {
            return false;
        }
        return true;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((reservedDate == null) ? 0 : reservedDate.hashCode());
        result = prime * result + ((roomId == null) ? 0 : roomId.hashCode());
        return result;
    }

Javaでは、同値の判定をするオブジェクト(値オブジェクト)を実装する場合は、equalsとhashCodeをオーバーライドして同値判定の実装をするのが定石です。

IDEIntelliJ IDEAを使っていたので、試しに IntelliJのコード生成機能で equalsとhashCodeメソッドの実装を生成したところ、次のようなコードとなりました。

    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) return false;
        ReservableRoomId that = (ReservableRoomId) o;
        return Objects.equals(roomId, that.roomId) && Objects.equals(reservedDate, that.reservedDate);
    }
    @Override
    public int hashCode() {
        return Objects.hash(roomId, reservedDate);
    }

java.util.Objectsクラス(JDK 7から導入されたユーティリティクラス)の equals(Object, Object)メソッドとhash(Object...)メソッドを使って、とっても簡潔な実装となっています。

読書会参加メンバーでEclipseを使用している人に、同様に equalsとhashCodeのメソッドをコード生成してもらったところ、java.util.Objects を使用せず、書籍と同じような長いコードが生成されていました。

ということで、IntelliJ IDEAが コード生成機能をきちんと洗練していることが分かりました。

schema.sql

Spring Boot で、application.propertiesに、spring.sql.init.mode=always と指定すると、Spring Bootアプリケーションを起動したときに、src/main/resources/ ディレクトリの下にある schema.sql ファイルと data.sql ファイルを実行します。

schema.sqlには、DDL文を、data.sql には初期データをインサートするSQLを記述します。 書籍では、schema.sql の中に、外部参照の制約をCONSTRAINTで定義しますが、CONSTRAINTに、ハッシュのような長い文字列が付いています。

ALTER TABLE reservable_room ADD CONSTRAINT FK_f4wnx2qj0d59s9tl1q5800fw7 FOREIGN KEY (room_id) REFERENCES meeting_room;

これはどうやら Hibernateスキーマ生成ツール hbm2ddl がJPAアノテーション付きエンティティからSQLDDL文を生成しているもののようです。

シンプルに外部参照を指定するなら、CREATE TABLEの中に記述することもできます。

CREATE TABLE reservable_room (
    room_id INT4 NOT NULL REFERENCES meeting_room (room_id),
    :

schema.sql と data.sql に記載のSQL文は、1文ずつJDBC経由でデータベースに投入されているようです。

読書会の冒頭、アフターでの話題

パッケージ構成(ディレクトリ構成)

Spring のパッケージ構成は、書籍のサンプル・チュートリアルのように レイヤーの観点でまずパッケージを構成し、それぞれの中で必要に応じて機能ごとのサブパッケージを構成する方法(package by layer)と、アプリケーションの業務(機能)の観点でパッケージを構成、それぞれの中で必要に応じてレイヤーごとのサブパッケージを構成する方法(package by feature)とがよく適用されています。

src/main/java/com/torutk/hello
  +- controller/
  |    +- login/
  |    +- search/
  |    +- edit/
  +- model/
  +- service/
  +- repository/

レイヤー観点の分割が上位のパッケージにあると、一つのアプリケーションを構成するソースファイルがかなり広範囲に散らばるので、まとまりという点では機能観点での分割が良さそうに思いました。

T.B.D.

MavenXMLで設定を書かなくても良い

pom.xmlは、XMLなので人の目には少し厳しい(タグの記述がコンテンツよりも目立つ・多い)ですが、最近(しばらく前から?)はXMLではなくYAML形式のpom.yml で記述できるようです。

Maven使っている読書会参加者も知らなかったようです。 Gradleを使っているので、どれだけ利便性が向上するかは分かりませんが、反応はよさそうでした。

Redmineのバージョンアップ(6.0→6.1)作業メモ

はじめに

Linuxサーバー(Rocky Linux 8)上で、Unicornアプリケーション(Rack)サーバーとNginx Webサーバーを用いた環境でRedmineを稼働しています。

この環境での Redmineバージョンアップ手順と問題対処などをメモしておきます。

Redmineのバージョンは、セマンティックバージョンに基づいて3桁の数値で指定されます。

バージョン番号の付与 - ソフトウェアエンジニアリング - Torutk

バージョンアップの際、どの桁の数値が上がるかで作業が変わります。今回は、稼働中のRedmine 6.0.2を先月リリースされた Redmine 6.1.0にバージョンアップするので、2桁目のマイナーバージョンが上がるマイナーバージョンアップとなります。

アップデート作業

Rubyバージョンの更新

Redmine 6.1のリリース情報から、Rubyの対応バージョンを確認します。Ruby 3.2、3.3および3.4に対応とあります。現在稼働しているサーバーのRubyバージョンを調べると

~$ ruby -v
ruby 3.1.7p261 (2025-03-26 revision 0a3704f218) [x86_64-linux]

残念ながら、対応バージョンから外れていました。というかRuby 3.1自体が 2025年3月でEOL(サポート終了)となっていました。

Rocky Linux 8で対応しているRubyのバージョンを確認すると、次のとおり Ruby 3.3を入れることが可能です。

~$ dnf module list ruby
Name  Stream   Profiles    Summary                                              
ruby  2.5 [d]  common [d]  An interpreter of object-oriented scripting language 
ruby  2.6      common [d]  An interpreter of object-oriented scripting language 
ruby  2.7      common [d]  An interpreter of object-oriented scripting language 
ruby  3.0      common [d]  An interpreter of object-oriented scripting language 
ruby  3.1 [e]  common [d]  An interpreter of object-oriented scripting language 
ruby  3.3      common [d]  An interpreter of object-oriented scripting language 

ヒント: [d]efault, [e]nabled, [x]disabled, [i]nstalled

Rubyバージョンを変更すると、Rubyのgemパッケージ群も更新が必要になります。

まず Rubyの更新を実施します。

 ~$ sudo dnf module reset ruby -y
   :
~$ sudo dnf module enable ruby:3.3 -y
   :
 ~$ sudo dnf install ruby
   :

rubyに関係するOSパッケージの更新を実施します。

 ~$ sudo dnf update

Redmineの更新

更新前のRedmine 6.0は、git cloneにより /var/lib/redmine-6.0-stable に展開しています。Redmineの更新方法としては、cloneしたローカルディレクトリ上でブランチを6.1に切り替えるか、あらたに ブランチ6.1をgit cloneするかの方法があります。 今回は、Rubyも更新しているので、新たにgit cloneする方法とします。

~$ sudo git clone -b 6.1-stable https://github.com/redmine/redmine.git /var/lib/redmine-6.1-stable
  :
~$ sudo chown -R redmine:redmine /var/lib/redmine-6.1-stable/

新たにcloneしてきたので、設定ファイルをコピーしてきます。

redmine-6.1-stable$ cp ../redmine-6.0-stable/config/database.yml config/
redmine-6.1-stable$ cp ../redmine-6.0-stable/config/configuration.yml config/
redmine-6.1-stable$ cp ../redmine-6.0-stable/config.ru .
redmine-6.1-stable$ cp ../redmine-6.0-stable/config/unicorn.rb config/
  • config/routes.rb は、後で修正

Redmineが必要とするgem群をインストールします。

redmine-6.1-stable$ bundle config set --local path vendor/bundler
redmine-6.1-stable$ bundle config set --local without 'development test'
redmine-6.1-stable$ bundle config set --local force_ruby_platform true
  • config set で、/var/lib/redmine-6.1-stable/.bundle/config に設定が書かれます。
  • Nokogiri gemのインストールはデフォルトでglibc 2.29へのリンクを含みますが、Locky 8はglibc 2.28のためリンクエラーとなります。そこでgem インストール時にビルドを行うよう設定しています。

gemパッケージのインストールにネイティブビルドをするようになったら、commonmarker gemのインストールがエラーとなってしまいました。clang関係パッケージが必要となっています。

~$ sudo dnf install clang
  :
redmine-6.1-stable$ bundle install
  :

セッションデータ用クッキーの暗号化キー生成

redmine-6.1-stable$ bundle exec rails generate_secret_token

稼働中のRedmineを停止

~$ sudo systemctl stop redmine-unicorn

データベーススキーマの更新

redmine-6.1-stable$ bundle exec rails db:migrate RAILS_ENV=production

Unicornインストール

/var/lib/redmine-6.1-stable/Gemfile.local を作成

gem "unicorn"
gem "unicorn-worker-killer"
redmine-6.1-stable$ bundle update
~$ sudo ln -s /var/lib/redmine-6.1-stable /var/lib/redmine

/usr/lib/systemd/system/redmine-unicorn.serviceの修正

- WorkingDirectory=/var/lib/redmine-6.0-stable
+ WorkingDirectory=/var/lib/redmine-6.1-stable

- PIDFile=/var/lib/redmine-6.0-stable/tmp/pids/unicorn.pid
+ PIDFile=/var/lib/redmine-6.1-stable/tmp/pids/unicorn.pid
~$ sudo systemctl daemon-reload

添付ファイルの格納ディレクトリの移動

redmine-6.1-stable$ rm -rf file
redmine-6.1-stable$ mv ../redmine-6.0-stable/files/ .

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

プラグインを各リポジトリからクローン

redmine-6.1-stable$ cd plugins/
plugins$ git clone https://github.com/agileware-jp/redmine_banner.git
  :
plugins$ git clone https://github.com/agileware-jp/redmine_issue_templates.git
  :
plugins$ git clone https://github.com/process91/redmine_latex_mathjax.git
  :
plugins$ git clone https://github.com/haru/redmine_theme_changer.git
  :
plugins$ git clone https://github.com/haru/redmine_wiki_extensions
  :
plugins$ git clone https://github.com/tkusukawa/redmine_wiki_lists.git
  :
plugins$ git clone https://github.com/onozaty/redmine-view-customize.git
  :
plugins$  git clone https://github.com/torutk/redmine_cozy_wiki_macros.git
  :
plugins$ git clone https://github.com/torutk/redmine_glossary.git
  :

必要なgemをインストール

redmine-6.1-stable$ bundle update
  :

データベースのマイグレーション実施

redmine-6.1-stable$ bundle exec rails redmine:plugins:migrate RAILS_ENV=production

テーマのインストール

redmine-6.1-stable$ git clone https://github.com/farend/redmine_theme_farend_bleuclair.git themes/bleuclair

デフォルトのページ設定

config/routes.rb

-  root :to => 'welcome#index'
-  root :to => 'welcome#index', :as => 'home'
+  root :to => 'wiki#show', :project_id => 'swe', :as => 'home'

Java SE 24/JDK 24のリリース

JDK 24のリリースと耐量子暗号

2025-03-18に、JDK 24がリリースされました。半年毎の定期バージョンアップで、非LTSです。 JDK 24

ざっと新機能を眺めてみると、Quantum-Resistant で始まる機能が2つほど正式採用されていました。

  • Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism (JEP 496)
  • Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm (JEP 497)

なんだろうなと思いつつ、JEPのサマリを読んでもピンときませんでした。そんな時、日経NETWORK 2025年3月号の包装を開き、ペラペラ読んでいたら、特集記事「耐量子暗号は正解か 暗号の2030年問題」に、RSA-2048の移行先として耐量子暗号が候補に上がっており、JEPのサマリに登場した略語 ML-KEM、ML-DSA が出てきました。

記事を要約すると

現在の暗号方式であるRSAECCは暗号方式として素因数分解や楕円上の離散対数に基づいpており、今後量子コンピュータの性能向上で解読されることが予想されています。そこで、量子コンピュータでも解読が難しい耐量子暗号として、格子暗号、符号暗号、多変数多項式暗号に基づく暗号方式の標準化をNISTが進めており、2024年8月に鍵交換・データ暗号化に使うML-KEM、デジタル署名に使うML-DSA、SLH-DSAの3つが標準化されました。残るデジタル署名のFN-DSAはまだ標準化が公開されていません。

ここで登場する ML-KEMがJEP 496、ML-DSAがJEP 497でJDK 24に取り入れられたということです。

ただ、記事では、耐量子暗号の安全性の検証に十分時間がかけられておらず、今後脆弱性が見つかる可能性も指摘されていました。

Java読書会「Spring徹底入門 第2版」を読む会(第1回)を終えて

Java読書会BOF主催「Spring徹底入門 第2版」を読む会(第1回)が10月20日(日)に開催されました。

www.shoeisha.co.jp

前回の書籍(基礎からのサーブレット/JSP入門 第5版)は、参加人数が2-3人と少し寂しい読書会でした。今回は7人の参加で開催となり、前回の書籍の読書会に比べると賑わった会となりました。

Springの書籍

今回の課題図書となった「Spring徹底入門 第2版」(2024年5月刊)のほかに、類書で「プロになるためのSpring入門 ――ゼロからの開発力養成講座:書籍案内|技術評論社(2023年7月刊)」があります。書籍の対象とするSpring Frameworkのバージョンが、「Spring徹底入門 第2版」が5系 である一方「プロになるためのSpring入門」が6系となっています。

書籍のボリュームは、「Spring徹底入門 第2版」が752ページに対して「プロになるためのSpring入門」が384ページとほぼ倍のボリュームとなっています。

目次構成を見ると、「Spring徹底入門 第2版」は、Springの概要紹介の薄い1章の後は、2章でDIとAOPの詳細な説明が開始され、頭から読んで手を動かしながら徐々に詳しく解説していくる体裁ではなく、リファレンス的な構成になっています。開発環境についてはAの付録で記載、Hello World的なサンプルは13章の Spring Bootの章で初めて出てきます。 「プロになるためのSpring入門」は、第1部基本編と第2部詳細編と2部構成で徐々に詳しく解説していくスタイルのようです。

入門としての読みやすさは「プロになるためのSpring入門」で、独学でも読めそうです。一方、読書会向き(議論が活発になる書籍)ということではSpring徹底入門なのかなとの感触です。入門本で700ページ越えもサイズが強烈です。

PDFの入手

書籍サイトでは電子書籍の購入で、一見PDF版が見当たらず、KindleKobo、Hontoに誘導されるように見えますが、PDF版を翔泳社から購入可能です。今回、電子書籍購入して参加の方の多くが、PDF版がないと思いKindle版を購入したと言っていました。次の図の操作でPDF版の購入に辿れました。

電子書籍(PDF版)購入の流れ

Spring Boot開発環境

書籍で解説されるコード断片を実行しようとする場合、DIコンテナを含めて各フレームワーク・ライブラリが動く環境が必要です。簡単にSpringアプリケーションを動かすのがSpring Bootの機能なので、まずはSpring BootでSpringアプリケーションを作って動かせるようにします。

書籍では、A.付録において、Spring Frameworkを使うアプリケーションの開発環境として、EclipseベースのSTS(Spring Tool Suite)の準備が説明されています。

Spring Bootの開発では、WebサービスのSpring Initializr へアクセスし、設定を入力するとプロジェクトの雛形がダウンロードできるので、これを展開してIDEに読ませるという流れになります。設定ではビルドツールにMavenかGradleを選択できます。

IntelliJ IDEAの場合、有償のUltimate版なら Spring Initializrにアクセスしなくてもプロジェクトの作成からSpring Bootを選択して雛形を生成できます。と言っても、プロジェクトの作成では裏でSpring Initializrをアクセスしているようです。無償のCommunity版の場合は、Spring Initializrにアクセスして生成した雛形を読み込んで使用する方法となります。

Spring Bootを使ってビルドしたアプリケーション(JAR形式)は、Webサーバーが組み込まれるので、JARファイルを実行するだけでWebサーバーを用意しなくてもWebブラウザからアクセスが可能になります。デフォルトではembedded TomcatらしきクラスファイルがJARに含まれていました。

IntelliJ IDEA(Ultimate版)でSpring Bootプロジェクト生成

新規プロジェクトを生成します(File > New > Project)

プロジェクト名、言語、ビルドツール、パッケージ名などを選択・入力します。

Dependencies で、Spring Webを選択します。これを選択することで、SpringのWeb機能の基本的な振る舞いを利用できるようになります。ブラウザにHelloメッセージを表示する最初のプログラムはこのライブラリを使用します。

アプリケーションのmainクラス(エントリーポイント)とテストクラスの雛形が生成されました。 生成されたmainクラスを実行すると、Webサーバー機能が動作し、デフォルトではデフォルトでは 8080ポートで待ち受けます。Webブラウザからlocalhost:8080に接続すると次のエラーが発生します。これは、ルート/にマッピングされる処理が未定義のため 404エラーとなっている場合のメッセージです。

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Mon Oct 28 00:00:55 JST 2024
There was an unexpected error (type=Not Found, status=404).

Hello World! とメッセージを表示するWebアプリケーションとするために、生成されたmainクラスに@RestControllerアノテーションを付与し、HTTPのGETメソッドでルート/を指定された際にメッセージを返却するメソッドを@GetMappingアノテーションを付与して定義します。

package com.torutk.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {

    @GetMapping("/")
    String hello() {
        return "Hello World!";
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

Hello World的なWebアプリとして単一クラスで完結させるのに、REST APIを扱う@RestControllerを使用しました。 コントローラのメソッドにREST APIのメソッド(GET)をマッピングし、メソッドの戻り値をAPIの戻り値にラップします。戻り値の型をStringとしているので単純な文字列が返却されますが、通常はリソースクラスを定義して返却します。

通常のWebアプリケーション用の@Controllerは、別途Viewに相当する仕組みを用意するか、@ResponseBodyを使うなどひと手間かかります。