JavaのSwingアプリケーションをWindowsのDPIスケーリングに対応する

JavaのSwingアプリケーションをWindowsのDPIスケーリングに対応するには、次のコードを呼び出す。

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

これで、DPIの設定に合わせてコントロールが表示される。らしい。

DPI100%のとき

dpi100

DPI150%のとき

dpi150

Java・.Net Framework・DelphiのZIPファイル中のファイル名の文字コードを調べる

ZIPファイルに格納するファイル名の文字コードはWindowsとMacでは異なり、日本語版Windowsの圧縮フォルダはShiftJISで、Mac OS XのFinderはUTF-8で格納する。
そのため、Macで作成したZIPファイルをWindowsで解凍すると、ファイル名が文字化けすることがある。

ZIPファイルに格納するファイル名の文字コードをUTF-8とする仕様が2007年9月にようやくリリースされた。
仕様ではファイル名はUTF-8で格納するのが正しいことになる。
しかし、すでにファイル名をShiftJISで格納したZIPファイルが広く使われている。

参考:ZIP (ファイルフォーマット) – Wikipedia

主要なプログラム言語では、ZIPファイルのファイル名をどのように扱っているのだろうか。
標準ライブラリがファイル名をどのように格納するのかを調べてみた。

■Java 6のjava.util.zip

public static void compress() throws FileNotFoundException, IOException {
    File file = new File("C:\\test\\sample.zip");
    final ZipOutputStream zipOutStream = new ZipOutputStream(
        new BufferedOutputStream(new FileOutputStream(file)));
    ZipEntry doc = new ZipEntry("ドキュメント.txt");
    zipOutStream.putNextEntry(doc);
    zipOutStream.write(new String("テスト").getBytes());
    zipOutStream.closeEntry();
    zipOutStream.finish();
    zipOutStream.close();
}

java.util.zipではファイル名の文字コードを指定できず、ファイル名はUTF-8で格納する。
そのためjava.util.zipで作成したZIPファイルをWindowsで解凍すると文字化けが発生することがある。

■.NET Framework 4.5のSystem.IO.Compression.ZipArchive

static void compress()
{
    using (ZipArchive zip = ZipFile.Open(@"C:\test\sample.zip", 
        ZipArchiveMode.Create, Encoding.GetEncoding(932)))
    {
        ZipArchiveEntry readmeEntry = zip.CreateEntry("テスト.txt");
        using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
        {
            writer.WriteLine("テスト");
        }
    }
}

ZipFile.Openメソッドの引数にEncodingを指定することで、ファイル名の文字コードを指定することができる。
適切な文字コードを指定することで、文字化けを回避できる。

■Delphi XE3のSystem.Zip.TZipFile

procedure TForm1.Button1Click(Sender: TObject);
var
  zip: TZipFile;
  bytes: TBytes;
begin
  zip := TZipFile.Create;
  zip.Open('C:\test\sample.zip', zmWrite);
  zip.Add(TEncoding.Default.GetBytes('テスト'), 'ドキュメント.txt');
  zip.Close;
  zip.Free;
end;

System.Zip.TZipFileもJavaのjava.util.zipと同様、ファイル名の文字コードを指定することはできない。
やはり文字化けの可能性がある。

■Python 2.7のzipfile.ZipFile

#!/usr/bin/python
# -*- coding: Shift_JIS -*-
import zipfile

zip = zipfile.ZipFile('C:/test/sample.zip', 'w', zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo('ドキュメント.txt')
zip.writestr(info, 'テスト')
zip.close()

文字化けは発生しない。

試しにソースコードファイルの文字コードをUTF-8に変更すると、文字化けが発生した。

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import zipfile

zip = zipfile.ZipFile('C:/test/sample.zip', 'w', zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo('ドキュメント.txt')
zip.writestr(info, 'テスト')
zip.close()

ソースコードファイルのエンコーディングはShiftJISのまま、ZIPファイル中のファイル名をユニコード文字列で指定すると、文字化けが発生した。

#!/usr/bin/python
# -*- coding: Shift_JIS -*-
import zipfile

zip = zipfile.ZipFile('C:/test/sample.zip', 'w', zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo(u'ドキュメント.txt')
zip.writestr(info, 'テスト')
zip.close()

zipfile.ZipFileには、ファイル名の文字コードを指定する機能はない。
ファイル名がバイト列ならそのまま、ユニコード文字列ならUTF-8で格納しているようだ。
ファイル名を適切に指定すれば、文字化けを回避することができる。

デザインパターン検出ツール Tsantalis

Tsantalisというデザインパターン検出ツールが「edubase Stream ソフトウェアパターン第9回ツール活用」で紹介されていた。

デザインパターン検出ツールとは、既存のソフトウェアについて既知のパターンが適用されているかどうかを検出するツール。
既存のプログラムの理解を容易にし、プログラムを修正しやすくなる。

TsantalisはJavaのクラスファイル集合から12のGoFデザインパターンを検出する。
複数のデザインパターンの重ね合わせを扱うことができ、軽快で精度がいい。
割り切れば使える、とのこと。

Tsantalisが検出する仕組みは、対象ソフトウェアをグラフ化し、既知デザインパターンのグラフが含まれるかを判定する。
部分グラフの同型判定問題である。

規模の大きいJavaプログラムを読む時には、試してみる価値がありそうだ。

増補改訂版Java言語で学ぶデザインパターン入門』は、今まで読んだデザインパターンの入門書の中でもっともおすすめできる本。

最短経路の本』は、グラフ理論の入門書として有名な本です。
グラフ理論を全く知らない人が初めて読む本として最適です。

世界を股にかける数十億ドル規模の巨大航空会社をストップさせたバグ

Release It! 本番用ソフトウェア製品の設計とデプロイのために』で紹介されていた話。

数百の旅客機と数万人の社員を擁し、世界を股にかける数十億ドル規模の巨大航空会社をストップさせたのが次のコード。
さて、どこに問題があったかわかるだろうか。

package com.example.cf.flightsearch;
…
public class FlightSearch implements SessionBean {
  private MonitoredDataSource connectionPool;
  private List lookupByCity(…) throws SQLException RemoteException {
    Connection conn = null;
    Statement stmt = null;
    try {
      conn = connectionPool.getConnection();
      stmt = conn.createStatement();
      // 検索のロジックを実行して、
      // 結果のリストを返す
    } finally {
      if (stmt != null) {
        stmt.close();
      }
      if (conn != null) {
        conn.close();
      }
    }
  }
}

ヒントを一つ。
問題があるのは「finally { … }」の部分だ。

追記
アジャイルプラクティス 達人プログラマに学ぶ現場開発者の習慣』にも、おそらくこの事件と思われる話が紹介されていました。

つい先日も、大規模な航空座席予約システムでの重大な障害がニュースで報じられた。
システムがクラッシュして飛行機が空港に足止めされて、何千人もの乗客が立ち往生することになった。
航空輸送システム全体の混乱は数日間にもわたって続いたそうだ。
原因は何だったのかって?
アプリケーションサーバで起きた、たった1つのSQLの非チェック例外だったんだ。