聞こえないJavaエンジニアが適当に書き連ねていく

つらつらとメモしたり日頃の溜まっている想いを吐き出す場所です。

Javaの今後のLTSバージョンを調べた

これは何

件名の通りの小ネタです。

調べ方

www.oracle.com をベースにJavaで書いてみただけです。

ソース

import java.time.LocalDate;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class App {

    private static final int LTS_CYCLE = 3;

    public static void main(String[] args) {

        var javaVersion = 11;
        var ltsRelease = LocalDate.of(2018, 9, 1);
        var ld = LocalDate.of(2019, 2, 1);
        var f = DateTimeFormatter.ofPattern("yyyy/MM");

        while (true) {
            if (javaVersion == 110) {
                break;
            }

            ld = ld.plusMonths(1);
            if (isNewVersionRelease(ld)) {
                javaVersion++;

                String msg = ld.format(f) + "の時点のJavaのバージョンは" + javaVersion + "です。";
                if (isLtsVersionRelease(ltsRelease, ld)) {
                    ltsRelease = ld;
                    msg += "LTSの対象です。";
                    System.out.println(msg);
                }

//             System.out.println(msg);

            }

        }

    }

    private static boolean isNewVersionRelease(LocalDate nowDate) {
        return nowDate.getMonth() == Month.MARCH || nowDate.getMonth() == Month.SEPTEMBER;
    }

    private static boolean isLtsVersionRelease(LocalDate ltsDate, LocalDate nowDate) {

        long diffYear = ChronoUnit.YEARS.between(ltsDate, nowDate);
        return (diffYear == LTS_CYCLE);

    }

}

毎年3月と9月のリリース時点のバージョンも出すと小ネタとして公開するにはページが長くなるので止めました。

実行結果

2021/09の時点のJavaのバージョンは17です。LTSの対象です。
2024/09の時点のJavaのバージョンは23です。LTSの対象です。
2027/09の時点のJavaのバージョンは29です。LTSの対象です。
2030/09の時点のJavaのバージョンは35です。LTSの対象です。
2033/09の時点のJavaのバージョンは41です。LTSの対象です。
2036/09の時点のJavaのバージョンは47です。LTSの対象です。
2039/09の時点のJavaのバージョンは53です。LTSの対象です。
2042/09の時点のJavaのバージョンは59です。LTSの対象です。
2045/09の時点のJavaのバージョンは65です。LTSの対象です。
2048/09の時点のJavaのバージョンは71です。LTSの対象です。
2051/09の時点のJavaのバージョンは77です。LTSの対象です。
2054/09の時点のJavaのバージョンは83です。LTSの対象です。
2057/09の時点のJavaのバージョンは89です。LTSの対象です。
2060/09の時点のJavaのバージョンは95です。LTSの対象です。
2063/09の時点のJavaのバージョンは101です。LTSの対象です。
2066/09の時点のJavaのバージョンは107です。LTSの対象です。

『球児に響く言葉力』を読んだ

拓大紅陵高校の野球部監督を長年勤めておられた、小枝守氏の本、『球児に響く言葉力』読書記録です。

 

強豪校の監督を務めておられた方の著書を読むと大体一貫しているのは、球児に対しての言葉遣いであったり、態度が球児に対して上からガツンと押し付けるのではなく、丁寧に指導するのが良い、という結論に最後は達しているところにあるのではと思います。

 

昔は上からガツンと言う、体で覚えさせるような指導だったようですが、時代は変わり、今はそんな教育だと誰もついてこない状況になりました。

 

ではどうやって理解してもらうかと言うと、まず監督自身が先行して行動して示す、次に良いところをまずは褒めて伸ばす、という過程になっており、親としてもこの監督なら安心だという気持ちになります。

 

今、長男が少年野球をやっているのでお手伝いに行くことがあるのですが、その時も私自身は下手なりに一緒に動き、見本を示しながら、いいプレーをしたら褒める、ちょっとまずいプレーが出た時も良かったところを見出してそこを褒めるように心がけて行きたいと改めて感じる一冊でした。

『甲子園、連れて行きます!』を読んだ

横浜高校野球部の寮母さんを長年勤めておられた方の本、『甲子園、連れて行きます!』読書記録です。

 

横浜高校の野球部監督を長年勤めておられた監督の娘さんの著書で、読んでて強豪校である所以が少しだけわかったかもしれません。

寮母さんとしてのサポートだけではなく、学生マネージャーや寮当番になった部員の働き、選手同士の声がけなど、学生として大切なことも自然と学べる環境にあるようで、そりゃ強いですわ、と言う感想を持ちました。

 

著書の方も最初から栄養の知識があったわけではなく、途中で自分の知識不足に気づかされて、仕事の後に学校に通って学んだと書かれていました。もちろん、周囲の方のサポートも得られていたから出来ることなのだけど、知識不足を素直に認めて勉強しようと行動された、その点は私も見習わないとと反省しました。

 

野球部員の方々がホワイトボードにご飯の感想を書いたり、メッセージを書いたりしていたと言うエピソードも載っているし、ちょっと前まで中学生だった子が親元を離れて寮に入った時のサポートについても載っていて、大変面白かったです。

 

高校野球に関する本というと、監督の自伝だったり、ライターさんが色々な学校を回って取材した本が殆どという印象がある中、寮母を務めていた方が書かれた本というのは初めて読んだのもあると思うが、新しい視点から書かれた高校野球の本で非常に興味深く読ませて戴きました。

 

『失敗から学ぶRDBの正しい歩き方』#そーだい本 を読んだ

これは何

Twitterでフォローさせていただき、毎日の金言を楽しみにしている曽根 壮大さん@soudai1025が書かれた本、『失敗から学ぶRDBの正しい歩き方』の読書記録です。

システムの開発、維持をやっていると、どうしても負の遺産が積み重なったデータベース設計を目の当たりにして辛い思いをするわけですが、 この本にはその辛さが面白おかしく書かれていて、辛い内容なのにさらっと読めてしまいました。

PostgreSQLMySQLの設計と運用を見直す、とキャッチコピーには書かれていますが、ほとんどの章は大抵のデータベースに対して適用できる内容になっています。

目次が公開されているので、章ごとの読んだ一言感想を書いてみます。

一言感想

第1章 データベースの迷宮

古いシステムをリプレースしようとしたときに、なんだこのコード?と困るやつ。

第2章 失われた事実

履歴として残すの大事。

第3章 やり過ぎたJOIN

対象データを絞り込んでからJOINするの大事。

第4章 効かないINDEX

INDEXのお気持ちはサッパリワカラナイ。

第5章 フラグの闇

なんたらフラグ、安易に持ちすぎるけどJOIN時に辛くなるよね~

第6章 ソートの依存

ソート難しい。

第7章 隠された状態

こういう設計はしたことなかった(と思う)けど、こんな設計した人がいたらお説教間違いなし。

第8章 JSONの甘い罠

JSONをテーブルに突っ込むのやったことないけど、これ読んでやめとこうと思った。

第9章 強過ぎる制約

テーブルに制約入れたくなるけど、この例で取り上げられてる制約のシステムを引き継いだら絶対最初分かんねぇなこれ。

第10章 転んだ後のバックアップ

復元テストをするの大事!絶対大事!

第11章 見られないエラーログ

今までデータベースのエラーログ、データ量がパンクしそうというやつしか見たことなかったなぁ。

第12章 監視されないデータベース

モニタリングする必要があるほどのシステムを触ったことなかったな…

第13章 知らないロック

ロックの仕組みって、データベースによって違うの…知らなかった。

第14章 ロックの功罪

ロックの仕組みについてわかりやすく書かれていて勉強になる。 でも、ファジーリードの図14.2と図14.3の違いが判らない…

第15章 簡単過ぎる不整合

開発者Aさんは伝える情報量が足りなさすぎじゃないですかね?(自戒)

第16章 キャッシュ中毒

キャッシュ、ほとんど使ったことなかったけどこれからも使わなくていいなぁ。

第17章 複雑なクエリ

ややこしいクエリは分解していくと、仕様の勉強にもなっていいよ。

第18章 ノーチェンジ・コンフィグ

冒頭のテーブル設定がデフォルトなのはデータ量がどのくらいなのか、発注時にちゃんと伝わっていなかったんじゃないかなぁ。 設定の見直しは実データを見ながら調整していくよね。

第19章 塩漬けのバージョン

データベースに限らず、バージョンは開発側としては上げたいけど、運用している側としてはお金がかかるならやりたくないよね。 受託開発だとこの辺が辛い。

第20章 フレームワーク依存症

フレームワークに合わせた設計なんてやったことなかったなぁ。 テーブル設計に合わせてフレームワーク側を近づけるのが当たり前なのでは?

まとめ

名著である、『SQLアンチパターン』と合わせて持っておくと、自分のデータベース設計が悪手になっていないか迷ったときに読んで見つめなおせるのでお勧め。

新しいメンバーがチームにやってきた時に行うこと

これは何

devlove-kansai.doorkeeper.jp

っていう面白そうなイベントを見かけたので、参加できないけど 「新しいメンバーがチームにやってきた時」に自分は何をやるかを書いてみようと思った。

何をやるか

思いついたことをざっと書いてみた。

来る人の人数や名前確認

何人来るのか、を確認する。 また、ユーザー登録時に必要になることもあるので名前をローマ字表記も含めて確認する。

サポートメンバーの確認

人数に応じて、誰がサポートするのかを明確に割り振っておく。

プロジェクトの暗黙のルール洗い出し

暗黙のルールになっているものがないか確認し、存在したら明文化しておく。 明文化されていないルールはルールではない。

端末の手配

至極当然の事実として、端末が無いと作業が出来ない。 プロジェクトによって、必要な端末のスペックも異なるので 現在使われている端末のスペックは確認する。

必要なIDの手配

ソース管理ツールやサーバーのID、必要なら既存画面へのログインIDの発行などをやっておく。 メールアドレスも必要なら手配を依頼する。

環境構築手順の確認

ソースの取込手順や、テストDBの作り方、あるいはテストDBへの接続手順など確認しておく。 手順書の内容が既に古文書になっている可能性もあるので、手順書の中身はちゃんと確認する。 手順書の内容が古文書だったら手直しも行う。

大炎上してて、環境構築は既に終わっている状態にして着任即作業、として欲しいと 天の声が下りてきたときは環境も作っておく。

最初に見てもらうドキュメントの確認

最初にプロジェクト概要は説明しないといけないし、 どういう仕組みのシステムなのかも説明が必要なので、 説明するためのドキュメントは確認しておく。

既に古文書になっている可能性もあるので、古文書だったら手直しもする。

最初にお願いする作業の確認

着任はしたけど、実際の作業が全く用意できていない、というのはNGなので お願いできる作業は何があるのか、事前に洗い出しておく。

顔合わせの時間を確保

今いるメンバーと、新しく来た人、それぞれの簡単な自己紹介の場は設定しておく。 あの人いつの間にか来てるけど誰だ?なことが無いようにする。

やらないこと

僕が呑むの好きじゃないので、親睦と称した仕事終わりの呑み会は企画しない。

まとめ

新しい人が着任して即、なんだこのプロジェクトは!とならないように、作業をある程度進められるように 事前準備はちゃんとしておきたい。

JavaでHTMLファイルをpdfに変換する

これは何

HTMLファイルをPDFファイルに変換する方法のメモです。

前提

htmlファイルは以下の手順で作成したものをそのまま使う想定です。

tadashi.hatenablog.com

ライブラリなど

iTextとflying-saucer-pdfが今回のpdf変換で使うライブラリです。

<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.lowagie/itext -->
<dependency>
    <groupId>com.lowagie</groupId>
    <artifactId>itext</artifactId>
    <version>2.1.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.xhtmlrenderer/flying-saucer-pdf -->
<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf</artifactId>
    <version>9.1.16</version>
</dependency>

ディレクトリ構成

outputディレクトリの下にhtmlファイルとpdfを出力する想定です。 html出力先にfontsディレクトリ及び、フォントファイルが無いとpdf変換時に日本語が出力されません。

src
  └ main
        ├  java
        │     └ sample
        │           └ Main.java
        └ resource
               └ sample.html
output
  └ fonts
        └  ipag.ttf

Javaプログラム

package sample;

import java.io.*;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.context.IContext;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

public class Main {

    public void execute(String... args) throws Exception {
        // テンプレートエンジンを初期化する
        final TemplateEngine engine = initializeTemplateEngine();
        // コンテキストを生成する
        final IContext ctx = makeContext(args);
        // 今回はWriter経由で結果を出力するのでWriterも初期化
        final Writer writer = new FileWriter("output/sample.html");
        // テンプレート名とコンテキストとWriterを引数としてprocessメソッドをコール
        engine.process("sample", ctx, writer);
        // Writerをクローズ
        writer.close();

        // ここからがpdf出力用
        File html = new File("output/sample.html");
        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocument(html);
        renderer.layout();

        String outputFile = "output/report.pdf";
        try (OutputStream os = new FileOutputStream(outputFile)) {
            renderer.createPDF(os);
        }

    }

    private TemplateEngine initializeTemplateEngine() {
        // エンジンをインスタンス化
        final TemplateEngine templateEngine = new TemplateEngine();
        // テンプレート解決子をインスタンス化(今回はクラスパスからテンプレートをロードする)
        final ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
        // テンプレートモードはXHTML
        resolver.setTemplateMode("XHTML");
        // クラスパスのtemplatesディレクトリ配下にテンプレートファイルを置くことにする
        resolver.setPrefix("templates/");
        // テンプレートの拡張子はhtml
        resolver.setSuffix(".html");
        // テンプレート解決子をエンジンに設定
        templateEngine.setTemplateResolver(resolver);
        return templateEngine;
    }

    private IContext makeContext(String... args) {
        final IContext ctx = new Context();
        // 変数マップにテンプレート変数を設定

        String[] values = {"aaa", "bbb"};

        ((Context) ctx).setVariable("args", values);

        SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
        Calendar cal = Calendar.getInstance();
        ((Context) ctx).setVariable("today", dateFormat.format(cal.getTime()));

        return ctx;
    }

    public static void main(String... args) throws Exception {
        new Main().execute(args);
    }
}

htmlファイル

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org" xml:lang="ja" lang="ja">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Sample</title>
    <!-- スタイルシートの中でフォントを明示的に指定しないとpdf出力時に日本語が出ない -->
    <style>
        @font-face{
            font-family: "IPAGothic";
            src: url("fonts/ipag.ttf");
            -fs-pdf-font-embed: embed;
            -fs-pdf-font-encoding: Identity-H;
        }
        body{
            font-family: "IPAGothic";
        }
    </style>

</head>
<body>
<h1>Arguments</h1>
<p>
    The sample application received
    <span th:text="${#arrays.isEmpty(args)} ? 'no' : 'some'">...</span>
    arguments.
</p>

<p>今日 is: <span th:text="${today}">13 February 2011</span></p>

<ul>
    <li th:each="a : ${args}"><span th:text="${a}">...</span></li>
</ul>
</body>
</html>

出来上がったpdf

こんな感じのpdfがhtmlファイル経由で出力できます。

f:id:su_zu_ki_1010:20190222172810p:plain
出来上がったpdf

感想

JasperReportsでGUIで帳票のレイアウトを決めるのもいいけど、Thymeleafでhtmlを出力して、iTextでpdfに変換する方が可読性がいいかもしれない。

Thymeleafテンプレートエンジンを単体で使う(Thymeleaf 3版)

これは何

m12i.hatenablog.com このサイトの Thymeleaf 3版 です。要はSpringを使わずに、Thymeleaf 3 だけを使ってhtmlファイルを書きだすサンプルです。

参考

Thymeleaf 3ドキュメントの日本語版ページも確認しました。 www.thymeleaf.org

翻訳していただき、ありがとうございます。

bufferings.hatenablog.com

masatoshitada.hatenadiary.jp

バージョン情報

  • Java 11
  • Thymeleaf 3.0.11

ディレクトリ構造

src
  └ main
        ├  java
        │     └ sample
        │           └ Main.java
        └ resource
               └ sample.html

Javaソース

参考にさせていただいたソースとの差異は以下の通り。

  • org.thymeleaf.context.VariablesMap と org.thymeleaf.templateresolver.TemplateResolver が削除されている。
    • VariablesMap は使わずに、ContextクラスのsetVariableを直で呼び出している。
    • TemplateResolver は ClassLoaderTemplateResolver を直で使うようにした。

Javaソースはこちら。

package sample;

import java.io.FileWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.context.IContext;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;

public class Main {

    public void execute(String... args) throws Exception {
        // テンプレートエンジンを初期化する
        final TemplateEngine engine = initializeTemplateEngine();
        // コンテキストを生成する
        final IContext ctx = makeContext(args);
        // 今回はWriter経由で結果を出力するのでWriterも初期化
        final Writer writer = new FileWriter("sample.html");
        // テンプレート名とコンテキストとWriterを引数としてprocessメソッドをコール
        engine.process("sample", ctx, writer);
        // Writerをクローズ
        writer.close();
    }

    private TemplateEngine initializeTemplateEngine() {
        // エンジンをインスタンス化
        final TemplateEngine templateEngine = new TemplateEngine();
        // テンプレート解決子をインスタンス化(今回はクラスパスからテンプレートをロードする)
        final ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
        // テンプレートモードはXHTML
        resolver.setTemplateMode("XHTML");
        // クラスパスのtemplatesディレクトリ配下にテンプレートファイルを置くことにする
        resolver.setPrefix("templates/");
        // テンプレートの拡張子はhtml
        resolver.setSuffix(".html");
        // テンプレート解決子をエンジンに設定
        templateEngine.setTemplateResolver(resolver);
        return templateEngine;
    }

    private IContext makeContext(String... args) {
        final IContext ctx = new Context();
        // 変数マップにテンプレート変数を設定

        String[] values = {"aaa", "bbb"};

        ((Context) ctx).setVariable("args", values);

        //公式ガイドの日本語訳がこれだったのでそのままコピーした…
        SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
        Calendar cal = Calendar.getInstance();
        ((Context) ctx).setVariable("today", dateFormat.format(cal.getTime()));

        return ctx;
    }

    public static void main(String... args) throws Exception {
        new Main().execute(args);
    }
}

htmlソースはこちら。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org" xml:lang="ja" lang="ja">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Sample</title>
</head>
<body>
<h1>Arguments</h1>
<p>
    The sample application received
    <span th:text="${#arrays.isEmpty(args)} ? 'no' : 'some'">...</span>
    arguments.
</p>

<p>Today is: <span th:text="${today}">13 February 2011</span></p>

<ul>
    <li th:each="a : ${args}"><span th:text="${a}">...</span></li>
</ul>
</body>
</html>

これで動かすと、プロジェクト直下にsample.html が生成される。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xml:lang="ja" lang="ja">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Sample</title>
</head>
<body>
<h1>Arguments</h1>
<p>
    The sample application received
    <span>some</span>
    arguments.
</p>

<p>Today is: <span>22 2月 2019</span></p>

<ul>
    <li><span>aaa</span></li>
    <li><span>bbb</span></li>
</ul>
</body>
</html>