JSONパーサにクラスとの相互変換機能を付けた

ここで作ったJSONパーサに、通常のクラスのインスタンスとの相互変換機能を追加しました。
kmaebashi.hatenablog.com
上記の時点では、JSONのテキストをJsonElementという独自クラス(インタフェース)にパースし、そのサブインタフェースのJsonElementやJsonObjectやJsonValueから値を取り出す、というところまでしか実装していませんでした。この実装でもまあ役には立つでしょうし、「任意のJSONをパースしなければいけない」という状況では特に便利でしょうが、たいていのケースではJSONで送ったり受け取ったりするデータ形式は決まっているので、それ用のクラスを作ってそこにマッピングしてくれた方が便利です。元の実装では、JsonObjectはMapを保持しているので、オブジェクトのプロパティの値を取得するためにはキーを文字列で指定するようになっていたのですが、これだとキーの名前をミスタイプしてもコンパイルエラーになってくれません。私は「動的型付け言語という間違ったアイディアでこの業界は20年遠回りした」と思っている人間なので、すべてのバグはできるだけ早く、可能ならコンパイルの段階で機械に見つけてほしいと思っています。その意味でも、静的型付け言語(Java)のクラスにマッピングするほうが仕様として「正しい」と思います。
修正版のプログラムは例によってGitHubにも上げています。
github.com

具体的な使い方としては、たとえば以下のようなクラスHogeがあったとして、

public class Hoge {
    public int intValue;
    private int privateInt; // privateフィールドは対象外
    @JsonIgnore
    public int intValueIgnore; // @JsonIgnoreを付けるとJSON化されない
    public Integer intObj; // Wrapperオブジェクトも対象。nullも使える。
    public float floatValue;
    public double doubleValue;
    public boolean booleanValue;
    public String stringValue;
    public int[] intArray;
    public Integer[] intObjArray;
    public Hoge[] hogeArray;
    public List<Hoge> hogeList;

    public void setPrivateInt(int value) {
        this.privateInt = value;
    }
}

このクラスのインスタンスJSONに変換するには、ClassMapper.toJson()メソッドを使います。

        Hoge hoge = new Hoge();
        hoge.intValue = 1;
        hoge.setPrivateInt(999);
        hoge.intValueIgnore = 9999;
        hoge.intObj = 2;
        hoge.floatValue = 3.1f;
        hoge.doubleValue = 4.1;
        hoge.booleanValue = true;
        hoge.stringValue = "abc";
        hoge.intArray = new int[] {10, 20, 30};
        hoge.intObjArray = new Integer[] {11, 21, null};
        hoge.hogeArray = new Hoge[2];
        hoge.hogeArray[0] = new Hoge();
        hoge.hogeList = new ArrayList<>();
        hoge.hogeList.add(new Hoge());
        hoge.hogeList.add(new Hoge());

        System.out.println("json.." + ClassMapper.toJson(hoge));

結果はこうなります。floatに誤差が出るのは、まあしょうがない*1

json..{
    "intValue":1,
    "intObj":2,
    "floatValue":3.0999999046325684,
    "doubleValue":4.1,
    "booleanValue":true,
    "stringValue":"abc",
    "intArray":[
        10,
        20,
        30
    ],
    "intObjArray":[
        11,
        21,
        null
    ],
    "hogeArray":[
        {
            "intValue":0,
            "intObj":null,
            "floatValue":0.0,
            "doubleValue":0.0,
            "booleanValue":false,
            "stringValue":null,
            "intArray":null,
            "intObjArray":null,
            "hogeArray":null,
            "hogeList":null
        },
        null
    ],
    "hogeList":[
        {
            "intValue":0,
            "intObj":null,
            "floatValue":0.0,
            "doubleValue":0.0,
            "booleanValue":false,
            "stringValue":null,
            "intArray":null,
            "intObjArray":null,
            "hogeArray":null,
            "hogeList":null
        },
        {
            "intValue":0,
            "intObj":null,
            "floatValue":0.0,
            "doubleValue":0.0,
            "booleanValue":false,
            "stringValue":null,
            "intArray":null,
            "intObjArray":null,
            "hogeArray":null,
            "hogeList":null
        }
    ]
}

逆にこのJSONHogeクラスに変換もできるのですが、制限として、JSONからクラスに変換するほうではListは使えません。JavaではGenericコレクションクラスを使っても実行時には要素のクラスが特定できないためです。だからJavaGenericsってのは……(以下自粛)
上のJSONから、hogeListに相当する部分を削除すれば、以下のようにClassMapper.toObject()でJSONからクラスのオブジェクトに変換できます。ClassMapper.toObject()の第2引数は、変換対象のクラスのClassです。

        String jsonStr = """
                {
                    "intValue":1,
                    "intObj":2,
                    "floatValue":3.0999999046325684,
                    "doubleValue":4.1,
                    "booleanValue":true,
                    "stringValue":"abc",
                    "intArray":[
                        10,
                        20,
                        30
                    ],
                    "intObjArray":[
                        11,
                        21,
                        null
                    ],
                    "hogeArray":[
                        {
                            "intValue":0,
                            "intObj":null,
                            "floatValue":0.0,
                            "doubleValue":0.0,
                            "booleanValue":false,
                            "stringValue":null,
                            "intArray":null,
                            "intObjArray":null,
                            "hogeArray":null,
                            "hogeList":null
                        },
                        null
                    ]
                }
                """;

        Hoge hoge = ClassMapper.toObject(jsonStr, Hoge.class);

JSONでは、「ただのintの文字列("5"とか)」も立派なJSONなので、そのあたりの変換にも対応しています。

        Integer int1 = ClassMapper.toObject("5", int.class);
        assertEquals(5, int1.intValue());

ライセンスは私の知る限りもっとも緩いライセンスであるNYSL(煮るなり焼くなり好きにしろライセンス)なので、よければ使ってやってくださいませ。

*1:Float.toString()を使うとfloatの値をきれいに文字列に変換できるようですが、このtoJson()は内部的にJsonElementを経由しているので、いったんdoubleに変換されます。

正月休みの最終日、JSONパーサを作りました

コピペで使おう! ライブラリ」とか始めたものの現在内容がしょぼいので、正月休みの最終日である今日、JSONパーサを作りました。
朝から(といっても11:00頃ですが)からかかって20:00頃まで、途中昼食を作って食べたりおやつを食べたりとかはしたものの、割とがっつりコード書きして、現状時間切れでテストが足りないところはちょっとあるものの、以下くらいのJSONは読み込めるようになりました。

{
  "array": [1, 2.0, 123, 123.456, -1, -2.0],
  "array2": [3e5, 3E5, 3.1e5, 3.1E5, 3.1e+5, 3.1e-5, true, false, null],
  "string": "abcあいうえお",
  "string2": "\"\\\/\t",
  "string3": "\u3042",
  "string4": "\u3042\u3044\u3046",
  "string5": "\uD867\uDE3D",
  "string6": "\uD867\uDE3D\r\n",
  "string7": "\uD867\uDE3D\u3042\u3044\u3046",
  "objSub": {
    "sub1": 1,
    "sub2": "abc"
  },
  "objInArray": [
    {
      "objInArray1": 1,
      "objInArray2": "abc"
    }
  ],
  "arrayInArray": [
    [
      1,
      true,
      {
        "arrayInArray1": 1,
        "arrayInArray2": "abc"
      }
    ]
  ]
}

コードは例によってGitHubに上げてあります。
github.com
もうちょっとちゃんとテストしたら、「コピペで使おう! ライブラリ」にも上げます。

以前、同じような休日コーディング(Twitter実況付き。ほんのちょっとですが)で、samplanというプログラミング言語を作ったのですが、
kmaebashi.hatenablog.com
プログラミング言語といえば当然パーサを含むので、今回作ったものもまあ似たようなプログラムではあります。ただ、ちゃんとJSONに対応しようとすると「1.2e3」のような指数表記やら「\uXXXX」のようなunicodeエンコードやらにも対応しなければいけなくて、その辺は結構面倒でした……

今回のJSONパーサは、JSONを読み込んでJsonElementというインタフェースを返します。これのサブインタフェースにJsonObject、JsonArray, JsonValueがあり、それぞれがJSONのオブジェクト、配列、値を表します。つまり、これは「任意のJSONを読み込むためのJSONパーサ」であり、「あらかじめ決められた形式のJSONを読み込むためのJSONパーサ」ではありません。もちろんあらかじめ決められた形式のJSONを読んでもよいのですが、どうせ形式が決まっているなら、クラスにマッピングするところまでやりたいですよね。その辺はまた後日。

今回、1日でテストコード除いて785行(空行込み、コメントなぞ書いてない)ばかり書いてなかなかに疲れましたが、やっぱりコードを書くのは楽しいです。コードを書き始める前に、ティーポット1杯(カップ3杯分)の紅茶を淹れたのに1杯だけ飲んで残りは忘れてしまったりとか、トイレに行く間も惜しんでコード書いてたのでトイレ行くのはいつもぎりぎりだったりとか、ひさびさに集中したコード書きができました。

しかし疲れた。明日も休みになんないかな。

「コピペで使おう! ライブラリ」なるページを作ってみました

私のWebサイトkmaebashi.comに、「コピペで使おう! ライブラリ」というページを作ってみました。

kmaebashi.com

能書きはリンク先に散々書いてあるので、ここではあまり繰り返しませんが(つまり一部は繰り返すのですが)、プログラマのお仕事が、コードを読んだり書いたりではなく、「ググってライブラリを見つけ、ググって使い方を調べ、エラーが出たらググって解決策を探す」になってしまっているのは悲しいと私は思っています。

世間に山ほどあるプログラムでも、たとえ車輪の再発明でも、コードを書くのは楽しいですし、本人の力になると思っています。私は趣味で書いたプログラム(のうち他人でも使いそうなもの)をここに公開していきますので、読んだ方は、コピペして使うもよしいじりたおすもよし、あるいはこれを参考にまったく別のプログラムを作るもよし、ご自由にお使いください。

まあなんだ、既存のフレームワークやライブラリをぺたぺた組み合わせて動かすパズラーのような仕事じゃなくて、ガリガリとスクラッチからコードを書こうぜみんな。

現状の内容は以下の通り。

Java

  • CSVパーサ
    シンプルなCSVパーサです。
  • JDBCのPreparedStatementに名前付きパラメタ
    JDBCのPreparedStatementでは、名前付きパラメタが使えず「?」しか指定できないので、何番目の?かを数えなければいけません。名前付きのパラメタが使えたら、ソースが読みやすくなります。
    Spring FrameworkならNamedParameterJdbcTemplateなんてのが使えたりしますが、何もそれを使わなくても自作できますよ。
  • JDBCのResultSetからクラスにマッピング
    JDBCでDBを検索した検索結果ResultSetの内容をDTOとなるクラスに転記する機能です。DTOクラスの方には、DBの列と対応付けるためのアノテーションを付けておきます。

JavaScript

  • ※にマウスオーバーで脚注を表示する
    文中の「※2」とかにマウスオーバーすると、対応する脚注が読めるという機能です。はてなブログには最初からある機能ですが。そう、こんなの*1
  • <pre>要素内のコードブロックに行番号を表示する
    以前はkmaebashi.comではソースを貼るときは事前に(大昔に書いた)Cのプログラムで行番号を付与していましたが、その方法だとコピペ時に行番号が付いてきてしまうので。

*1:ほら、こんなの

ホームページのアクセスカウンタを作りました。2023年に。

うちのWebサイトkmaebashi.com、Niftyのメンバーズホームページに最初に開設したのが1998年、その後メンバーズホームページ閉鎖に伴い2004年にドメイン取ってレンタルサーバに引っ越して、その後もその会社が潰れて再販元に引き取られたりとか色々あったのですが、今年の10月末に、いよいよレンタルサーバ業者が事業を撤退するとのことで、さくらインターネットVPSを借りて全面的に移行しました。
その際、以前はレンタルサーバのものを借りていたアクセスカウンタも、スクラッチで作ったので今回はそのお話です。
大昔、個人の「ホームページ」が流行っていた頃は、「ホームページ」にはアクセスカウンタを付けるのがお約束のようなものでした。うちのサイトもその頃からあるので、ずっと継続してアクセスカウンタを付けています。外見としてはこんな感じ。

アクセスカウンタの画像
kmaebashi.comのアクセスカウンタ

(「あなたは○○人目のお客様です!」とか付けない程度の理性は当時からあったらしい)

この手のアクセスカウンタは、たいてい画像で表示します。そして、HTMLで画像を表示するには、img要素を使用します。kmaebashi.comの現行のアクセスカウンタでは、該当の箇所に以下のようにimg要素が入っています。

<img src="/accesscounter/show?counterid=kmaebashi" alt="アクセスカウンタ">

この「/accesscounter/show」に対するリクエストをJavaサーブレットで受けて、カウンタの画像を生成して返す、というのが、アクセスカウンタのプログラムがやっていることです。昔のアクセスカウンタは、Perlなんかで書いてあって、現在の閲覧数はテキストファイルなんかで持っていたりしましたが、今回はデータはPostgreSQLで保持するようにしました(掲示板等でどうせ使いますので)。
構成を図にするとこんな感じになります。

アクセスカウンタのシステム構成
アクセスカウンタのシステム構成

こうやって、img要素に対してのレスポンスで画像を返す、という方式にすると、サーバ側でプログラムが動かせず、ただのHTMLしかアップロードできないような昔ながらの「ホームページ」でも、外部のアクセスカウンタサービスを使ってアクセスカウンタを設置することができます。HTMLのimg要素は昔からドメインが違う別サーバの画像も取得できたからです。当時は、そうやって使うための「無料アクセスカウンタサービス」が多く公開されていました*1。昔ながらのアクセスカウンタがどれもこれも画像なのは、見た目をよくしたい、という目的以外にそういう理由があったのです。
アクセスカウンタのカウントアップの仕様自体もいろいろあって、同じブラウザで何度もF5を押しても増えないように、同一IPアドレスからの連続アクセスは無視するとか、そういうカウンタもありますが、今回私が作ったカウンタはその手の制御はしていません。なのでリロードするたびにカウンタがどんどん回ります。そのほうが嬉しい! ――確か当初、Niftyのメンバーズホームページで提供されていたアクセスカウンタはF5連打では増えなかったのですが、その後レンタルサーバに移ったときに移行したそのレンタルサーバのアクセスカウンタはF5連打で増えるタイプだったので、今回もそれを踏襲しました。やっぱりいっぱい増えた方が嬉しいですし。

その他、プログラムとかの細かい話は以下のページに書いておきます。
https://kmaebashi.com/programmer/accesscounter/index.html

せっかくアクセスカウンタを作ったことでもありますし、kmaebashi.com、今後ともたまには見てやってください。キリ番踏んだら掲示板に報告すること。踏み逃げ禁止!!

以下は広告です。2016年に出した本ですが、アクセスカウンタのような「レスポンスに画像を返すプログラムの作り方」含め、Webアプリケーションの基礎の基礎がわかる本と自負しております。
amazonアソシエイトプログラム、画像を含むリンクが貼れなくなっているようなので、自力で画像を上げました…

Webサーバを作りながら学ぶ 基礎からのWebアプリケーション開発入門
20231229235707

*1:今も探せばそこそこありますが、全盛期より数は減っていると思います……

サーブレットのweb.xmlで静的ファイルを除外する方法

たとえば生サーブレットで作ったオレオレフレームワークで、掲示板のひとつも作ろうと思ったとする。
オレオレフレームワークは、すべてのリクエストをひとつのサーブレットで受け付け、その先は独自のオレオレルータがルーティングして、各種処理を行って、HTMLを返す、ということをやりたいとする。
その場合、すべてのリクエストをOreOreFrameworkServletに渡せばいいんだな、ということでweb.xmlに以下のように書くと、

  <servlet>
    <servlet-name>OreOreFramework</servlet-name>
    <servlet-class>com.kmaebashi.framework.OreOreFrameworkServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>OreOreFramework</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

cssとかアイコン画像とかの静的ファイルへのリクエストまでOreOreFrameworkServletに流れてしまう。これは困る。

たとえば拡張子で判定して、*.cssだけ除外する、といった書き方は、web.xmlでは書けないらしい。気が利かない。
「"web.xml" "url-pattern"」でGoogle検索しようとしたら、「除外」ってサジェストが出たぐらいで、普通に需要はありそうなのだけど。

で、いろいろ検索すると、Stack overflowのページがいくつか見つかって、サーブレットフィルタを使えば行けるらしい……のだが。

https://stackoverflow.com/questions/8658949/in-a-web-xml-url-pattern-matcher-is-there-a-way-to-exclude-urlsstackoverflow.com

https://stackoverflow.com/questions/50056316/how-to-exclude-specific-path-from-web-xmlstackoverflow.com

サーブレットフィルタで、拡張子なりディレクトリなりで静的ファイルを判定した後どうすればよいか、書いてない。上のふたつの質問の解答はどちらも肝心のところが「// do something」とか「//do something else」になってる。しかもこのふたつの解答は、if文の条件判定がそれぞれ逆になっている。なんだこりゃ。

利用者から見えるパスに影響を与えてよいのなら、たとえば掲示板のアプリケーション名がbbsとして、一覧表示のURLを本来はこうしたいところ、
https://<ホスト>/bbs/list
サブディレクトリを挟んで
https://<ホスト>/bbs/app/list
とか、拡張子.doを付けて
https://<ホスト>/bbs/list.do
とかに変えれば、url-patternに"/app/*"や"*.do"を書いてそういうリクエストだけをOreOreFrameworkServletに渡せばよい。
その上で、webapps/bbs直下にbbs.cssを置けば、https://<ホスト>/bbs/bbs.cssCSSは取得できる。
でも、利用者から見えるところなのでやっぱり避けたい。/appとか入れるとCSSとかとの相対パスが狂うし、.doなんて懐かしのStrutsアプリみたいだ。

サーブレットフィルタは、サーブレットとかJSPとかの手前に挟まる。サーブレットコンテナ(Tomcatとか)が最初のサーブレットフィルタのdoFilter()を呼び出すので、次のフィルタとかサーブレットとかに処理を渡すなら、引数として与えられたchainのdoFilter()を呼べばよい。ではdoFilter()を呼ばなかったらどうなるかと言うと、そこでそのリクエストに対する処理は終わってしまう。拡張子が.cssでなければdoFilter()を呼んで(OreOreFrameworkServletに処理を渡して)、.cssだったら何もしない、というフィルタにすると、CSSは取得できない。戻り値か何かで「このurl-patternにマッチしなかったことにする」という指定ができればよいのだが、あいにくそんな機能はないし、doFilter()の戻り値はvoidだ。静的ファイルだと判定したら、自分でそのファイルを開いて、拡張子からContent-Typeを指定してファイルの中身をレスポンスに流し込めばよいのだろうが、さすがにそこまでやるのも嫌だ。

さらにいろいろ探して見つけたページがこちら。

https://stackoverflow.com/questions/13521946/how-to-prevent-static-resources-from-being-handled-by-front-controller-servlet-wstackoverflow.com

そこからリンクされているページ。内容は基本的に上記と同じ。
https://stackoverflow.com/questions/870150/how-to-access-static-resources-when-mapping-a-global-front-controller-servlet-on/3593513#3593513stackoverflow.com

結局、CSSなりを返したいと思ったら、フィルタではdoFilter()を呼べばよくて、逆にサーブレットに渡したい場合は/app/なり.doなりをくっつけてフォワードして、web.xmlのurl-patternにはそれをくっつけたのを書いておく、という方法でどうやらできた。下の例では、サーブレットに.doをくっつけているが、これは利用者には見えない(OreOreFrameworkServletには渡ってしまうが、まあ我慢する)。

web.xml

  <filter>
    <filter-name>staticresourcefilter</filter-name>
    <filter-class>com.kmaebashi.framework.StaticResourceFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>staticresourcefilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <servlet>
    <servlet-name>OreOreFramework</servlet-name>
    <servlet-class>com.kmaebashi.framework.OreOreFrameworkServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>OreOreFramework</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

StaticResourceFilter.java

import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;

import java.io.IOException;

public class StaticResourceFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest)request;
        String path = req.getRequestURI().substring(req.getContextPath().length());
        if (path.endsWith(".css") || path.endsWith(".html")) {
            chain.doFilter(request, response);
        } else {
            request.getRequestDispatcher(path + ".do").forward(request, response);
        }
    }
}

サーブレットなんて20年以上前からあるものだろうに、「url-patternに除外のルールを書く」くらいの機能、標準で付けておいてほしいよなあ。

PHPでCSVをダウンロードする方法(一時ファイルを作らない)

kmaebashi.comのサーバ移転に伴い、掲示板のデータも移行したわけですが、その際、PHP + MySQLで作った旧掲示板のデータをCSVにしてダウンロードする、という作業を行いました。
当方PHPは2004年だか2005年だかに旧掲示板を作って以来、PHPはほとんど使ってなくてすっかり忘れておりますが、PHPCSVをダウンロードするなんてよくある話だろうから、ちょっとGoogle検索すればサンプルが山ほど出てくるだろう、と思って検索してみたところ、サンプルは山ほど出てきたものの、どれもこれも「fputcsv()」というPHPの組み込み関数を使ってCSVを一時ファイルに書き出し、それをダウンロードさせている。いや別に一時ファイル作ってもよいのだけれど、こんなの作らなくてもできるだろストリームにダブルクオートだけ変換した文字列を吐くだけだろ、ということで、使わない形で作ったものをここに書き残しておきます。うちの掲示板のテーブル設計に依存したものなので、その辺は適当に読みかえてください。

  • 2005年から使っているものなのでDBへのアクセス方法が古いことはまあ大目に見てください。
  • boardidがtestbbsの投稿は、テスト掲示板への投稿で、広告だらけだったので除外しました。
  • DBの各列をいったんすべて変数に入れているのは――なぜだろう? 直接$row["serialid"]とか書いてもよさそうですが。数か月前の意図が思い出せない……
  • DBはEUCだったので、UTF8に変換してダウンロードしよう、と思ったのですが、「①」のような機種依存文字が化けるようで、結局EUCのままダウンロードしてクライアントのWindows用エディタ(xyzzy)でUTF8に変換しました。
<?php
require 'connect_db.php';

$sql_str = "select * from message where boardid <> 'testbbs' order by boardid, serialid";
$result = mysql_query($sql_str) or die('SQLエラー'.$sql_str);

header("Content-Type: application/octet-stream");
header('Content-Disposition: attachment; filename="bbs.csv"');

while ($row = mysql_fetch_assoc($result)) {
  $serialid=$row["serialid"];
  $boardid=$row["boardid"];
  $date = $row["posteddate"];
  $name = $row["name"];
  $mailaddress = $row["mailaddress"];
  $url = $row["url"];
  $subject = $row["subject"];
  $message = $row["message"];
  $altermessage = $row["altermessage"];
  $password = $row["password"];
  $salt = $row["salt"];
  $preformatted = $row["preformatted"];
  $deleted = $row["deleted"];
  $admindeleted = $row["admindeleted"];
  $parent = $row["parent"];
  $top = $row["top"];
  $ipaddress = $row["ipaddress"];
  $remotehost = $row["remotehost"];
  $useragent = $row["useragent"];

  echo $serialid . ",";
  echo $boardid . ",";
  echo $date . ",";
  echo quote_field($name). ",";
  echo quote_field($mailaddress). ",";
  echo quote_field($url). ",";
  echo quote_field($subject). ",";
  echo quote_field($message). ",";
  echo quote_field($altermessage). ",";
  echo quote_field($password). ",";
  echo quote_field($salt). ",";
  echo $preformatted. ",";
  echo $deleted. ",";
  echo $admindeleted. ",";
  echo $parent. ",";
  echo $top. ",";
  echo $ipaddress. ",";
  echo $remotehost. ",";
  echo $useragent. "\r\n";
}

function quote_field($src) {
//  $replaced = mb_convert_encoding($src, "utf-8", "euc-JP-win");
  $replaced = str_replace('"', '""', $src);

  return '"' . $replaced . '"';
}
?>

kmaebashi.comをSSL化しました

先日サーバ移転した私の個人サイト「K.Maebashi's home page」(kmaebashi.com)について、前々からやりたいと思っていたSSL化を実施しました。これにより、URLが

http://kmaebashi.com

から

https://kmaebashi.com

に変更になります。

今まで通り、http://kmaebashi.com~でアクセスしても、https://kmaebashi.com~に自動的に切り替わるように設定したので、閲覧者の皆様には特に影響ないかと思います。

SSL(Secure Sockets Layer)は通信の暗号化の技術です*1。もとより世界中に制限もなく公開しているWebサイトで、別にクレジットカード番号や個人情報を入力するところもないのに*2、なんで通信の暗号化をする必要があるのか、とも思わなくもないですが、一応うちのサイトではプログラムを載せたりしているので身元保証は必要でしょう。ドメイン認証の証明書なので「kmaebashi.comを運営している人は信用できる」と思っていただける方にしか意味はありませんが、送信元がkmaebashi.comであるということは保証できます。

こういう個人サイトでは、Let's Encryptという無料の証明書を使うのが定番のようです。これは無料はいいのですが3か月で期限が切れるので、普通はcertbotというツールを使って自動更新するように設定します(最初の導入にもcertbotを使います)。で、certbotをインストールするにはEPELリポジトリとかいうのを入れなければいけなくて、EPELリポジトリを入れた状態でdnfでインストールしようとしたらdnfがkilled.と言われて常に落ちるようになってしまった(certbotに限らず、何を入れようとしても、check-updateするだけでも落ちる)。慌てて戻して調べると、どうやらメモリ不足のようで、私が見つけたページでは512MBくらいのメモリのサーバを使っている人が「最低1GBは必要」とか書いていたけど要件が上がったのかうちの1GBのサーバでも動かないようだ。それなら一時的にサーバのメモリを2GBに上げて、インストールが終わったら戻すか、と思ったら、どうも一時的にサーバのメモリを上げるだけでも1年分の料金が取られるようで、それなら有償の証明書――年間980円のJPRSの証明書を買った方がマシだな、ということでそっちにしました。certbotで入れるより手間はかかりましたけれども。

今後ともよろしくお願いいたします。

*1:SSLは古い規格で、今実際に使われているのはTLS(Transport Layer Security)という技術ですが、「SSL化しました」とか言う時には今でもSSLと言うことが多いようなので、ここでもそう書いています

*2:一応掲示板には削除用のパスワードを設定できますが、こんな掲示板に、大事なパスワードを使い回す人はいないと信じたい。