<?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 を指定するのがいいのではないだろうか、というのが個人的な結論。
あっているかはわからない。
<?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
では、以下のように定義されている
<?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 が同じであることがわかる
- web-app_5_0.xsd
- web-app_6_0.xsd
- どちらも
targetNamespace
はhttps://jakarta.ee/xml/ns/jakartaee
となっている
- このままでは Namespace の指定だけでは xsd を一意に特定できないので、
version
属性を合わせて指定しなければならい- Jakarta EE XML Schemas で、以下のように説明されている
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 の仕様に記載されている記述方法
-
Jakarta Servlet 6.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_{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
plugins {
id "war"
}
sourceCompatibility = 17
targetCompatibility = 17
compileJava.options.encoding = "UTF-8"
repositories {
mavenCentral()
}
dependencies {
compileOnly "jakarta.servlet:jakarta.servlet-api:6.0.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">
<hoge></hoge>
</web-app>
IDE
IntelliJ
- IntelliJ IDEA 2022.1.3 (Community Edition) で確認
- スキーマのチェックは行われている
- 存在しないタグ(
<hoge>
)を書くと怒られる
- 存在しないタグ(
- 入力補完が効く
- ただし、
schemaLocation
のところで指定している xsd ファイルの場所の指定がエラーになっている- エラーメッセージ >
Cannot resolve file 'web-app_6_0.xsd'
- この場合は相対パス扱いになるので、
web-app_6_0.xsd
ファイルはweb.xml
と同じところに配置されている必要がある
- エラーメッセージ >
- 以下のようにインターネット上で公開されている xsd ファイルの URL を指定すれば、警告はなくなった
クラスパス内に含まれる xsd ファイルを勝手に検索しているっぽい
- いろいろ試したところ、以下のように
schemaLocation
の指定を削除してもスキーマのチェックや入力補完は効くことが分かった
- この状態で
xmlns
のところの Namespace を Ctrl + クリックすると、 xsd ファイルにジャンプできる - この xsd ファイルは、 Jakarta Servlet の API の jar の中に組み込まれているものになる
- IntelliJ の仕様などを調査したわけではないのであくまで推測だが、どうやら IntelliJ はクラスパス内に含まれる xsd ファイルをすべてチェックして、
xmlns
で指定された Namespace の URI に一致する xsd ファイルがあればそれを利用してくれるようになっているっぽい
Eclipse
- Pleiades All in One Eclipse の 2022 の Java で確認
- スキーマのチェックは行われていない
- 存在しないタグ(
<hoge>
)を書いても、何も警告されない
- 存在しないタグ(
- 入力補完も効かない
- 以下のようにインターネット上で公開されている xsd ファイルの URL を指定すれば、スキーマのチェックや入力補完が効くようになった
アプリケーションサーバー
GlassFish
要素'{"https://jakarta.ee/xml/ns/jakartaee":hoge}'で始まる無効なコンテンツが見つかりました。
- スキーマのチェックが行われ、存在しないタグがあるとデプロイがエラーになった
- 試しに xsd ファイルの名前を以下のように適当なもの(
test.xsd
)に変えてみた
<?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>
- 指定された xsd ファイルが見つからないとしてデプロイに失敗した
- なので、
schemaLocation
の指定は見ている様子 - xsd ファイルの指定を
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd
に変えた場合は、web-app_6_0.xsd
と指定した場合と同じようになった(スキーマのチェックが行われた)
WildFly
- 27.0.1.Final で確認
Message: Unexpected element '{https://jakarta.ee/xml/ns/jakartaee}hoge' encountered"}}
- スキーマのチェックにひっかかってデプロイに失敗した
-
web-app_6_0.xsd
をtest.xsd
に変えてデプロイしたところ、エラーは変わらなかった(hoge
という存在しないタグを検知してエラー)- つまり、
schemaLocation
の指定は見ていない様子
- つまり、
-
web-app_6_0.xsd
をhttps://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd
に変えた場合も、変化はなかった(普通にスキーマのチェックが行われた)
Tomcat
- 10.1.5 で確認
- エラー無くデプロイできた
- スキーマのチェックは行われていない
-
web-app_6_0.xsd
をtest.xsd
やhttps://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 を指定する以下の形が良いのではないかなと思った
- この URL は Jakarta Servlet の仕様書にも「この URL で見れるよ」と記載されているものなので、使っても問題ないと思う
- https://jakarta.ee/specifications/servlet/6.0/jakarta-servlet-spec-6.0.html#deployment-descriptor-2
<?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 で確認してみた。
すると、普通にスキーマのチェックが行われてエラーになった。
どういう仕組みなのかはわからない。