`application/vnd.java.hprof` の末尾スペースバグを修正し、正規表現マッチングを追加
TikaのMIMEタイプ定義に含まれていた application/vnd.java.hprof の末尾スペースが、テーブル生成スクリプトを通じてそのまま tables.rb に出力されていた問題を修正しました。あわせて、Javaヒープダンプファイルのマジックバイト検出に正規表現マッチングを導入しています。
背景
#112 で報告されたように、Tikaの定義ファイル中に application/vnd.java.hprof という末尾スペース付きのMIMEタイプが存在していました。script/generate_tables.rb はこの値をそのまま出力していたため、生成された lib/marcel/tables.rb のキーにもスペースが残り、MIMEタイプの検索や照合が失敗する原因となっていました。
この問題は #132 で導入されたTikaの正規表現サポートと密接に関連しています。application/vnd.java.hprof のマジックバイト検出はTika定義内で正規表現(regex 型)を使用しており、正規表現対応なしでは検出自体が機能しませんでした。
技術的な変更
変更は生成スクリプト、生成済みテーブル、そしてRakefileの3箇所に及びます。
script/generate_tables.rb への .strip 追加により、Tikaの定義に末尾スペースが含まれていてもテーブルの出力キーが正規化されます。EXTENSIONS と TYPES の両テーブルで出力時に key.strip を適用するよう変更されました。
# 変更前
puts " '#{key}' => '#{extensions[key]}',"
# 変更後
puts " '#{key.strip}' => '#{extensions[key]}',"
同様に TYPES テーブル出力の箇所にも key.strip が適用されており、両テーブルで一貫してキーが正規化されます。
正規表現対応タイプのホワイトリスト(WELL_KNOWN_REGEX_TYPES) に application/vnd.java.hprof と application/vnd.java.hprof.text が追加されました。このリストは以前はメソッド内のローカル変数でしたが、定数に昇格してスコープが明確化されています。
# 変更前(メソッド内ローカル変数)
def get_matches(mime, parent)
well_known_regex_types = %w( application/x-bzip2 text/html )
...
# 変更後(定数)
WELL_KNOWN_REGEX_TYPES = %w(
application/x-bzip2
text/html
application/vnd.java.hprof
application/vnd.java.hprof.text
)
def get_matches(mime, parent)
...
また、正規表現タイプのスキップ判定にも .strip が適用され、mime['type'].strip で比較するよう修正されました。これにより末尾スペース付きのタイプ名でもホワイトリスト照合が正しく機能します。
lib/marcel/tables.rb の生成結果として、EXTENSIONS のキーから末尾スペースが除去され、MAGIC 定数に新たな正規表現エントリが追加されました。
# EXTENSIONS(変更前)
'application/vnd.java.hprof ' => %w(hprof),
# EXTENSIONS(変更後)
'application/vnd.java.hprof' => %w(hprof),
# MAGIC(追加)
['application/vnd.java.hprof', [[0, /JAVA PROFILE \d\.\d\.\d\u0000/]]],
['application/vnd.java.hprof.text', [[0, /JAVA PROFILE \d\.\d\.\d,/]]],
バイナリ形式のhprofはヘッダが \u0000(NULLバイト)で終端され、テキスト形式は ,(カンマ)で終端されるという仕様の違いが、それぞれの正規表現で表現されています。
Rakefileの変更では、task :tables の定義がファイル依存タスク(fileタスク)からシンプルな名前付きタスクへ変更されました。
# 変更前
task tables: "lib/marcel/tables.rb"
file "lib/marcel/tables.rb" => %w[ data/tika.xml data/custom.xml ] do |target|
sh "script/generate_tables.rb", *target.prerequisites, out: target.name
end
# 変更後
task :tables do
sh "script/generate_tables.rb", "data/tika.xml", "data/custom.xml", out: "lib/marcel/tables.rb"
end
これにより、tika.xml のタイムスタンプに関わらず常にテーブルが再生成されるようになります。
フィクスチャ として test/fixtures/magic/application/vnd.java.hprof/minimal.hprof が追加され、正規表現マッチングのテストが可能になりました。
設計判断
根本原因をデータ側でなくスクリプト側で修正するアプローチが採用されました。Tikaの上流定義ファイルの末尾スペースを直接除去するのではなく、生成スクリプト側で key.strip を適用することで、将来的に類似の問題が発生しても自動的に正規化されます。
WELL_KNOWN_REGEX_TYPES の定数化も同様の方針に沿っています。以前はメソッドローカル変数だったため、呼び出しごとに配列が生成される実装でした。定数に昇格することで、リストを一箇所で管理でき、新しい正規表現対応タイプの追加箇所が明確になっています。
また、ホワイトリストに含まれない正規表現タイプに対してはスキップしつつ warn を出力する動作が #132 で導入されており、本PRでも引き継がれています。これにより、対応されていない定義が暗黙的に誤ったルールとして扱われる問題が防止されています。
Rakefileの変更については、ファイルタスクによるタイムスタンプベースのスキップが不要なユースケースに合わせた簡素化であり、明示的な依存関係の記述よりも単純な実行可能性が優先されています。
本PRは、データ生成パイプラインにおける「入力データの不整合を出力側で吸収する」という原則と、正規表現マッチングの適用範囲を明示的なホワイトリストで制御するという安全策を組み合わせた変更です。上流のTika定義に同様の末尾スペースが混入しても、今後は tables.rb に影響が及ばない構造が整備されました。