LoginSignup
4
2

More than 1 year has passed since last update.

結局web.xmlのスキーマ指定はどうするのがいいのか

Posted at
web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee web-app_6_0.xsd"
         version="6.0">

web.xml の冒頭に書く、この xmlns とかの呪文の話。
結局、どう書くのが正解なのか調べたことをメモする。

結論

色々調べたり試したりした結果、以下のように xsi:schemaLocation のスキーマファイルの場所に、インターネットで公開されている xsd ファイルの URL を指定するのがいいのではないだろうか、というのが個人的な結論。
あっているかはわからない。

web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                             https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">

以下、調べたこととかのメモ。

前提知識

XML Schema

  • XML には、ある XML 文書で使用できるタグや属性を定義できる仕組みが存在する
  • これを XML Schema と呼ぶ(もしくは XML Schema Definition Language(XSD))
  • XSD 自体も XML で記述されており、拡張子 *.xsd とかのファイルで用意されていたりする
  • たとえば、 Jakarta EE が提供している各 xsd ファイルは以下で確認できる
  • XML Schema があることで、次のようなことができるようになる
    • ある XML 文書が XML Schema で定義されたルール通りに記載できているかどうかチェックできる
    • IDE などで、 XML のタグの入力補完ができる

XML Schema の Namespace

  • XML Schema は、他の XML Schema と区別できるように、 Namespace(名前空間) という固有の識別子を持っている
  • ある XML Schema の Namespace は、 xsd ファイルで targetNamespace という属性で定義されている
  • 例えば、 Jakarta Servlet 6.0 の web.xml で使用できるタグを定義した web-app_6_0.xsd では、以下のように定義されている
web-app_6_0.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
            targetNamespace="https://jakarta.ee/xml/ns/jakartaee" ★これ
            xmlns:jakartaee="https://jakarta.ee/xml/ns/jakartaee"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified"
            version="6.0">
  • Namespace は URI 形式で定義されているが、これはあくまで XML Schema を識別するための値(識別子)にすぎない
    • この URI の場所に xsd ファイルが置いてあるわけではない

XML Schema を指定する

  • ある XML 文書がどの XML Schema に従って書かれているかを宣言するために、 xmlns という属性が存在する
<?xml version="1.0" encoding="UTF-8" ?>
<hello xmlns="https://example.com/xml/ns/foo">
  <world>foo</world>
</hello>
  • ここでは、 https://example.com/xml/ns/foo という Namespace の XML Schema が指定されている
  • つまり、 <hello>, <world> というタグは、 https://example.com/xml/ns/foo という XML Schema で定義されたタグであることが分かる

複数の XML Schema を指定する

  • 1つの XML 文書の中で複数の XML Schema が提供するタグを組み合わせて使いたくなることもある
  • そういう場合は、次のように xmlns を宣言する
<?xml version="1.0" encoding="UTF-8" ?>
<hello xmlns="https://example.com/xml/ns/foo"
       xmlns:bar="https://example.com/xml/ns/bar">
  <world>foo</world>
  <bar:message>BAR!!</bar:message>
</hello>
  • https://example.com/xml/ns/bar という Namespace の XML Schema を使用することを宣言している
  • このとき、 xmlns の後ろに :bar とつけることで、 https://example.com/xml/ns/bar の XML Schema は bar というプレフィックスを使って区別することを宣言している
  • これにより、 https://example.com/xml/ns/bar で定義されたタグや属性は bar というプレフィックスを付けて利用できるようになる

xsd ファイルの場所を指定する

  • xmlns は、あくまで XML Schema の Namespace を指定しているにすぎない
  • XML Schema を定義した xsd ファイルの場所は Namespace だけではわからない
  • xsd ファイルがないと、その XML 文書が本当に xsd ファイルに書かれた定義通りに記載されているかどうかは判定できない
  • そこで、 Namespace ごとに xsd ファイルの場所を指定できる方法が用意されている
<?xml version="1.0" encoding="UTF-8" ?>
<hello xmlns="https://example.com/xml/ns/foo"
       xmlns:bar="https://example.com/xml/ns/bar"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           https://example.com/xml/ns/foo https://example.com/xml/ns/foo/foo.xsd
           https://example.com/xml/ns/bar https://example.com/xml/ns/foo/bar.xsd">
  • xsi:schemaLocation で、 Namespace ごとの xsd ファイルの場所を宣言している
    • <NamespaceのURI> <xsdファイルのURI> という形式で列挙する(空白区切り)
  • schemaLocation 属性は http://www.w3.org/2001/XMLSchema-instance という Namespace の XML Schema で定義されているものなので、それを xsi というプレフィックスで利用できるように xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" と宣言している

schemaLocation はあくまで参考情報

  • schemaLocation で xsd ファイルの場所を指定できるが、これはあくまで XML パーサーの参考情報としてしか利用されない
    • ここで指定された xsd ファイルを使って解析しなければならない、という仕様ではない
  • ここで指定された xsd ファイルをどう扱うかは、その XML を使用するアプリケーションに委ねられている
    • あるアプリケーションはここで指定された xsd ファイルを利用するかもしれないが、別のアプリケーションは無視して自分が保有している(組み込まれている) xsd ファイルを使うかもしれない

Jakarta EE のスキーマ指定は version も必要

  • Jakarta EE が提供している xsd ファイルを見ると、同じ種類のスキーマのバージョン違いでは Namespace が同じであることがわかる
  • このままでは Namespace の指定だけでは xsd を一意に特定できないので、 version 属性を合わせて指定しなければならい

All Jakarta EE 9 and newer Deployment Descriptor Schemas share the namespace http://jakarta.ee/xml/ns/jakartaee. Each schema document contains a version attribute that contains the version of the specification.
For example, the XML Schema document for the Jakarta Servlet specification contains the version attribute value “5.0”, pertaining to the specific version of the specification as well as the schema document itself.
(中略)
For example, Jakarta servlet Deployment Descriptor instances that must be processed with the servlet 5.0 version must indicate the version within the version attribute of the instance document, for example, “5.0”.

Jakarta EE 9 以降のデプロイメント記述子スキーマはすべて、名前空間 http://jakarta.ee/xml/ns/jakartaee を共有します。 各スキーマ ドキュメントには、仕様のバージョンを含む version 属性が含まれています。
たとえば、Jakarta Servlet 仕様の XML スキーマ ドキュメントには、仕様の特定のバージョンとスキーマ ドキュメント自体に関連するバージョン属性値「5.0」が含まれています。
(中略)
たとえば、サーブレット 5.0 バージョンで処理する必要がある Jakarta サーブレット デプロイメント記述子インスタンスは、インスタンス ドキュメントの version 属性内でバージョンを示す必要があります (たとえば、「5.0」)。

アプリケーションサーバが web.xml のスキーマをチェックするかは任意?

Jakarta Servlet 6.0 の仕様 には、次のように記載されている。

14.2. Rules for Processing the Deployment Descriptor

The deployment descriptor must be valid against the schema. Web containers and tools that manipulate web applications have a wide range of options for checking the validity of a WAR. This includes checking the validity of the deployment descriptor document held within.

デプロイメント記述子は、スキーマに対して有効である必要があります。 Web アプリケーションを操作する Web コンテナとツールには、WAR の有効性をチェックするための幅広いオプションがあります。 これには、保持されているデプロイメント記述子ドキュメントの有効性のチェックが含まれます。

  • 「デプロイメント記述子ドキュメント(web.xml)の有効性のチェック」が、「WAR の有効性をチェックするための幅広いオプション」の1つとされている
  • 「幅広いオプション」のオプションが任意という意味であれば、 web.xml のスキーマのチェックは任意ということになる?
    • 後述するが、 Tomacat ではチェックがされていないようだった

Jakarta Servlet の仕様に記載されている記述方法

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
         web-app_{spec-version-underscore}.xsd"
         version="{spec-version}">
  • {spec-version-underscore}{spec-version} の部分を、例えば 6.0 用に置き換えると次のようになる
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
         web-app_6_0.xsd"
         version="6.0">
  • ここまでの前提知識をもとに見ると、それぞれの指定が何を意味しているかが分かるようになる
    • xmlns="https://jakarta.ee/xml/ns/jakartaee", version="6.0"
      • Jakarta EE が提供している XML Schema を使用することを宣言している
      • ただし、 Namespace だけでは一意にならないので version="6.0" も指定している
    • xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance", xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee web-app_6_0.xsd"
      • xsd ファイルの場所を指定している

xsd ファイルの場所の指定が問題

  • 前述のとおり、 schemaLocation で指定している xsd ファイルの場所はあくまで参考情報にすぎない
  • このため、この指定がどう解釈されるかは IDE やアプリケーションサーバーによって異なる

各IDEやアプリケーションサーバの挙動

  • 前述の Jakarta Servlet の仕様書に書かれているスキーマの指定で記述したときに、それぞれのアプリケーションでどう解釈されるか見てみる
  • 検証用に、以下のようなプロジェクトを作成して確認した
フォルダ構成
|-build.gradle
`-src/main/webapp/WEB-INF/
  `-web.xml
build.gradle
plugins {
    id "war"
}

sourceCompatibility = 17
targetCompatibility = 17

compileJava.options.encoding = "UTF-8"

repositories {
    mavenCentral()
}

dependencies {
    compileOnly "jakarta.servlet:jakarta.servlet-api:6.0.0"
}
web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee web-app_6_0.xsd"
         version="6.0">
    <hoge></hoge>
</web-app>

IDE

IntelliJ

  • IntelliJ IDEA 2022.1.3 (Community Edition) で確認

image.png

  • スキーマのチェックは行われている
    • 存在しないタグ(<hoge>)を書くと怒られる
  • 入力補完が効く
  • ただし、 schemaLocation のところで指定している xsd ファイルの場所の指定がエラーになっている
    • エラーメッセージ > Cannot resolve file 'web-app_6_0.xsd'
    • この場合は相対パス扱いになるので、web-app_6_0.xsd ファイルは web.xml と同じところに配置されている必要がある
  • 以下のようにインターネット上で公開されている xsd ファイルの URL を指定すれば、警告はなくなった

image.png

クラスパス内に含まれる xsd ファイルを勝手に検索しているっぽい

  • いろいろ試したところ、以下のように schemaLocation の指定を削除してもスキーマのチェックや入力補完は効くことが分かった

image.png

  • この状態で xmlns のところの Namespace を Ctrl + クリックすると、 xsd ファイルにジャンプできる
  • この xsd ファイルは、 Jakarta Servlet の API の jar の中に組み込まれているものになる

image.png

  • IntelliJ の仕様などを調査したわけではないのであくまで推測だが、どうやら IntelliJ はクラスパス内に含まれる xsd ファイルをすべてチェックして、 xmlns で指定された Namespace の URI に一致する xsd ファイルがあればそれを利用してくれるようになっているっぽい

Eclipse

image.png

  • スキーマのチェックは行われていない
    • 存在しないタグ(<hoge>)を書いても、何も警告されない
  • 入力補完も効かない
  • 以下のようにインターネット上で公開されている xsd ファイルの URL を指定すれば、スキーマのチェックや入力補完が効くようになった

image.png

アプリケーションサーバー

GlassFish

image.png

要素'{"https://jakarta.ee/xml/ns/jakartaee":hoge}'で始まる無効なコンテンツが見つかりました。

  • スキーマのチェックが行われ、存在しないタグがあるとデプロイがエラーになった
  • 試しに xsd ファイルの名前を以下のように適当なもの(test.xsd)に変えてみた
web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee test.xsd"
         version="6.0">
    <hoge></hoge>
</web-app>

image.png

  • 指定された xsd ファイルが見つからないとしてデプロイに失敗した
  • なので、 schemaLocation の指定は見ている様子
  • xsd ファイルの指定を https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd に変えた場合は、 web-app_6_0.xsd と指定した場合と同じようになった(スキーマのチェックが行われた)

WildFly

image.png

Message: Unexpected element '{https://jakarta.ee/xml/ns/jakartaee}hoge' encountered"}}

  • スキーマのチェックにひっかかってデプロイに失敗した
  • web-app_6_0.xsdtest.xsd に変えてデプロイしたところ、エラーは変わらなかった(hoge という存在しないタグを検知してエラー)
    • つまり、 schemaLocation の指定は見ていない様子
  • web-app_6_0.xsdhttps://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd に変えた場合も、変化はなかった(普通にスキーマのチェックが行われた)

Tomcat

  • 10.1.5 で確認
  • エラー無くデプロイできた
    • スキーマのチェックは行われていない
  • web-app_6_0.xsdtest.xsdhttps://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd に変えても、変化はなかった(デプロイに成功した)

まとめ

  • Jakarta Servlet の仕様書では、 schemaLocation を設定する例が記載されている
  • しかし、 schemaLocation の指定を実際に使うかどうかはアプリケーション依存
    • GlassFish は見てる
    • WildFly は、スキーマのチェックはしてるけど schemaLocation は見てない
    • Tomcat は見てないし、スキーマのチェックすらしない
    • IntelliJ は見てるけど、クラスパスから勝手に見つけてくれるので無くてもいい
    • Eclipse は見てる(IntelliJ みたいにクラスパスから勝手に見つけてはくれない)
  • Jakarta Servlet の仕様書に記載のとおりの形だと、
    • IntelliJ だと警告が出てちょっとうざい
    • Eclipse だと、スキーマのチェックや入力補完が効かずに不便
  • インターネットで公開されている xsd ファイルの URL (https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd)を指定すると、すべての IDE, アプリケーションサーバでいい感じで動作する
  • IDE で警告も出ずチェックや補完が効いて、アプリケーションサーバでも問題なく動作するということで、 xsd ファイルのパスにインターネットで公開されている URL を指定する以下の形が良いのではないかなと思った
web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                             https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">

インターネットアクセスが必要になる?
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd 指定にすると、チェックのときにインターネット経由で xsd ファイルを落としてきたりするのかなと思ったので、試しに LAN ケーブルを引っこ抜いた状態で GlassFish で確認してみた。
すると、普通にスキーマのチェックが行われてエラーになった。
どういう仕組みなのかはわからない。

参考

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2