FessクローラーのURL処理をjava.net.URIからjava.net.URLに戻した

FessのクローラーでURL処理に使っていたjava.net.URLjava.net.URIに置き換える対応を以前行いましたが、URIでは対応できない文字が多く問題が発生したため、java.net.URLに戻しました。

背景

Javaではjava.net.URLjava.net.URIの2つのURLを扱うクラスがあります。java.net.URLは古くからあるクラスで、java.net.URIはRFC 2396に準拠したより厳密なクラスです。一般的にはURIの使用が推奨されていますが、Webクローラーの用途では事情が異なります。

Webの世界には、RFCに準拠していない非標準的なURLが数多く存在します。URI.create()はこれらの非標準URLをIllegalArgumentExceptionで拒否してしまいますが、java.net.URLはより寛容に処理できます。

変更内容

PR #3066で、FessXpathTransformerProtocolHelperの2つのクラスを中心に、URIからURLへの変更を行いました。

FessXpathTransformer

主な変更点は以下の通りです。

  • java.net.URI/URISyntaxExceptionjava.net.URL/MalformedURLExceptionに変更
  • getURI()メソッドをgetURL()に、getBaseUri()getBaseUrl()にリネーム
  • addChildUrlFromTagAttribute()の引数の型をURIからURLに変更
  • 相対URL解決をURI.resolve()からnew URL(base, spec)コンストラクタに変更

new URL(base, spec)コンストラクタは相対URLの解決をネイティブに処理できるため、URI使用時に必要だった手動でのフォールバック処理(//で始まるURL、?#で始まるURL、相対パスなど)を大幅に簡素化できました。

// 変更前: URI.resolve()を使用
final URI childUri = uri.resolve(resolveTarget);
u = encodeUrl(normalizeUrl(childUri.toString()), encoding);

// 変更後: new URL(base, spec)を使用
final URL childUrl = new URL(url, urlValue.startsWith(":") ? url.getProtocol() + urlValue : urlValue);
String childUrlStr = childUrl.toExternalForm();

一方で、/../で始まるパスの正規化処理は引き続き必要なため、その部分は残しています。

ProtocolHelper

ProtocolHelperでは、リソースのプロトコルチェックで不要なURI変換を削除し、URL.getProtocol()を直接使用するようにしました。URIへの変換はFileコンストラクタがURIを要求する箇所でのみ残しています。

URIで問題が起きた理由

java.net.URIはRFC 2396に厳密に準拠しているため、以下のような文字を含むURLを処理できません。

  • ブラケット([]
  • 一部のUnicode文字
  • パーセント記号の不正なエンコーディング
  • HTMLエンティティを含むURL

Webクローラーは様々なサイトのHTMLを解析するため、このような非標準的なURLに頻繁に遭遇します。URI.create()IllegalArgumentExceptionを投げると、そのURLはクロール対象から外れてしまい、取得漏れの原因になります。

テストの追加

今回の変更に合わせて、特殊文字を含むURLに対するテストも追加しました。

  • FessXpathTransformerTest: getBaseUrl()のテストをリネームし、URL APIに合わせてアサーションを更新
  • CrawlingInfoHelperIndexExportJobDocumentUtilにブラケット、パーセント記号、Unicode、HTMLエンティティを含むURLのテストを追加

まとめ

java.net.URIはRFC準拠の厳密なURL処理には適していますが、Webクローラーのように非標準的なURLを扱う必要がある場面ではjava.net.URLの方が実用的です。今回の変更により、new URL(base, spec)による相対URL解決のおかげでコードも簡素化でき、クローラーの堅牢性も向上しました。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です