Java Platform, Standard Edition 1.4 (Java SE 1.4)には、JAXP 1.1用のCrimsonリファレンス実装が含まれていました。Java platform, Standard Edition 6 (Java SE 6)には、Apache Xercesライブラリに基づく、JAXP 1.4用のリファレンス実装が含まれています。
これらの実装はまったく異なるコード・ベースが基になっており、かつJAXP標準は1.1から1.4へと発展したため、どちらもJAXP標準に準拠していますが、これらの実装間にはわずかな差異があります。これら2つの要因が組み合わさるために、このガイドで説明する互換性の問題が発生しています。
リリース・ノートを参照してください。
Java SE 1.4のリファレンス実装ではDOM Level 2 APIをサポートしていますが、Java SE 6の実装ではDOM Level 3ファミリのAPIをサポートしています。このセクションでは、この変更に伴う、JAXP 1.1リファレンス実装を使用するプログラムへの影響を説明します。
詳細は、DOM Level 3の付録「Changes」にある変更点の完全な一覧を参照してください。
DOM Level 3では、次のインタフェースにメソッドが追加定義されました。
追加されたメソッドにより影響を受けるのは、インタフェースを直接実装しているアプリケーションだけで、それもそのようなアプリケーションを再コンパイルしたときだけです。アプリケーションでこれらのインタフェースの実装クラスを取得するためにファクトリ・メソッドを使用している場合は、問題ありません。
これらの変更の影響を受けるアプリケーションは、XMLデータをDOMに読み込んで変更し、元のデータ形式を保持しながら書き出すようなアプリケーションです。
JAXP 1.1では、余分な空白は入力時に自動的に削除されました。このとき、たとえばエンティティ・ノードやCDATAノードを保持するために、単一のプロパティ(ignoringLexicalInfo
)がfalse
に設定されていました。これらのノードを含めることで、DOMは処理がいくらか複雑になっていました。しかしこれらのノードが含まれているために空白出力(インデントや改行)が追加され、可読性が高く、書式化された、入力内容に非常に近いXMLデータを出力できました。
JAXP 1.4では、処理に利用できる字句(書式)情報の程度を判別するためにアプリケーションが使用するAPIが4つあり、それぞれ以下のDocumentBuilderFactory
メソッドを使用します。
setCoalescing()
– CDATAノードをTextノードに変換し、隣接するTextノードがあれば、そのノードに追加する。setExpandEntityReferences()
– エンティティ参照ノードを展開する。setIgnoringComments()
- コメントを無視する。setIgnoringElementContentWhitespace()
- 空白が要素の内容に重要でない場合は、その空白を無視する。これらのプロパティのデフォルト値はどれもfalse
で、受け取ったドキュメントを元の形式に再構築するために必要な字句情報がすべて保持されます。これらのプロパティをすべてtrue
に設定すると、もっとも単純なDOMが構築できるため、アプリケーションでは字句構文の詳細を考慮することなく、データのセマンティック・コンテンツに集中できます。
注:新しいノードを追加するときは、可読性を高めるために必要となるインデントや改行の書式は自動的に付加されないため、アプリケーションで付加する必要があります。
SAX 2.0.0とSAX 2.0.2の間で行われた変更で、互換性に影響を及ぼす可能性のあるものは以下のとおりです。
DeclHandler.externalEntityDecl
は、DTDHandler.unparsedEntityDecl
との一貫性を保つため、現在では絶対システム識別子を返すパーサーを必要とする。 このため、非互換性が生じる可能性がある。
SAX 2.0.1では、アプリケーションはErrorHandler
、EntityResolver
、ContentHandler
、またはDTDHandler
をnull
に設定できる。このようにするとSAX 2.0ではNullPointerException
(NPE)が発生していたが、この制約が緩くなった。
そのため、JAXP 1.3では、次のコードも有効
SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); XMLReader reader = sp.getXMLReader(); reader.setErrorHandler(null); reader.setContentHandler(null); reader.setEntityResolver(null); reader.setDTDHandler(null);
EntityResolver
APIのresolveEntity()
メソッドで、IOException
とSAXException
をスローするようになった。これまではSAXException
だけがスローされていた。
問題が生じるような方法でDefaultHandler
を使用するアプリケーションはごく一部だけであり、また、DefaultHandler
実装クラスが追加の例外を宣言するように変更されたため、ほとんどのアプリケーションはこの影響を受けない。
アプリケーションに影響があるのは、resolveEntity()
メソッドをオーバーライドして、かつ super.resolveEntity()
を呼び出す場合だけである。その場合、アプリケーションは、super.resolveEntity()
がスローするIOException
を処理するようにメソッドを変更するまで、Java SE 5でコンパイルされない。
新機能は次のとおりです。
http://xml.org/sax/features/external-general-entities
- 外部の一般的なエンティティを含めることができる。
http://xml.org/sax/features/external-parameter-entities
– 外部のパラメータ・エンティティと、外部のDTDサブセットを含めることができる。
また、新しいプロパティは次のとおり
http://xml.org/sax/properties/xml-string
- 現在のイベントに関連する文字列を取得する。
Xercesの機能およびプロパティの完全な一覧は、http://xerces.apache.org/xerces2-j/features.htmlおよびhttp://xerces.apache.org/xerces2-j/properties.htmlを参照してください。
注:互換性についても説明すべき点が1つあります。Java SE 1.4 (JAXP 1.1)では、名前空間の認識は、デフォルトでオフになっていました。下位互換性のため、この方針はJava SE 6 (JAXP 1.4)でも変わりません。ただし、https://sourceforge.net/projects/sax/から入手できる公式のSAX実装では、名前空間の認識がデフォルトでオンになっています。JAXPの観点からは厳密には互換性の問題となりませんが、思いがけない結果を招くことがあります。
標準JAXP APIを使用し、XSLトランスフォーマを作成したりXSLトランスフォーマにアクセスしたりするコードは、変更する必要がありません。出力は同じですが、ほとんどの場合は生成が高速になります。これは、Xalanトランスフォーマを解釈する代わりに、XSLTCのコンパイル・トランスフォーマがデフォルトで使用されるためです。
注: XSLスタイル・シートの開発やテストなど、小さいデータ・セットで1回実行する場合は、XalanとXSLTCのパフォーマンス上の大きな差はありません。しかし、大きなデータ・セットでXSLTCを使用する場合には、パフォーマンス上の大きな利点があります。
JAXP 1.4では、XPath表現を評価するための標準XPath APIを提供します。このAPIを使用することをお薦めします。Xalanインタプリタ型トランスフォーマは、リファレンス参照に含まれていません。アプリケーションがスタンドアロンXPath表現(XSLTスタイル・シートの一部でないもの)を評価するためにXalan XPath APIを明示的に使用している場合は、Xalan用のApacheライブラリをダウンロードしてインストールする必要があります。
この変更点は、標準JAXP APIを使用するように制限されているアプリケーションには影響ありません。ただし、1.3より前のJAXPバージョンで定義されたXMLプロセッサの実装固有機能にアクセスするアプリケーションは、変更する必要があります。
変更により、従来のアプリケーションには次の影響があります。
Java SE 1.4では、JAXPがJavaプラットフォームに組み込まれたことにはメリットもデメリットもありました。一方、アプリケーションは、それが組み込まれているという事実に依存できました。他方、ほとんどのアプリケーションでは、以降のバージョンで入手可能になった機能やバグ修正が必要でした。
しかし、内部クラスは常にクラス・パスよりも優先されるため、新しいライブラリを追加しても効果はありませんでした。1.4ではこの問題を解決するために、承認済み標準メカニズムが使用されました。しかしそのメカニズムは新しく、アプリケーション開発者だけでなくエンド・ユーザーにも余分の労力が必要とされました。
JAXP 1.3以降では、実装で使用されるApacheライブラリのパッケージ名を変更することで解決します。この変更により、クラス・パスで新しいApacheライブラリを参照できるため、アプリケーション開発者はそのライブラリをこれまでと同じ方法で使用しながら、Javaプラットフォームに追加されたその他の機能を利用できます。
JAXP 1.3リファレンス実装でApacheパッケージに付けられた新しい名前は次のとおりです。
JAXPパッケージ名の変更点
JAXP 1.1 |
JAXP 1.3以降 |
---|---|
|
|
|
|
XSLTパッケージ名の変更点
JAXP 1.1 |
JAXP 1.3以降 |
---|---|
|
|
XMLでは、再帰的なエンティティ定義は認められませんが、入れ子にされたエンティティ定義は認められます。しかし、外部ソースからのXMLデータを許可するサーバーがサービス妨害攻撃を受ける可能性があります。たとえば、次のように非常に深く入れ子にされたエンティティ定義が含まれるSOAPドキュメントは、エンティティを展開するのにCPU時間の100%と大量のメモリーを消費してしまいます。
<?xml version="1.0" encoding ="UTF-8"?> <!DOCTYPE foobar[ <!ENTITY x100 "foobar"> <!ENTITY x99 "&x100;&x100;"> <!ENTITY x98 "&x99;&x99;"> ... <!ENTITY x2 "&x3;&x3;"> <!ENTITY x1 "&x2;&x2;"> ]> <SOAP-ENV:Envelope xmlns:SOAP-ENV=...> <SOAP-ENV:Body> <ns1:aaa xmlns:ns1="urn:aaa" SOAP-ENV:encodingStyle="..."> <foobar xsi:type="xsd:string">&x1;</foobar> </ns1:aaa> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
外部XMLデータを受け入れないシステムではこの問題を考慮する必要はありませんが、受け入れるシステムではこの問題を防ぐために、次のような予防手段のどれかを利用できます。
エンティティ展開を制限する新しいシステム・プロパティ: entityExpansionLimitシステム・プロパティを使用すると、既存のアプリケーションは、コードを再コンパイルしなくても、エンティティ展開の総回数を制限できます。エンティティ展開の上限に達すると、パーサーは致命的なエラーをスローします。(デフォルトでは、64000に制限されています。)
システム・プロパティでエンティティ展開の上限を設定するには、Javaコマンド行で-DentityExpansionLimit=100000のようなオプションを使用します。
DTDを禁止する新しいパーサー・プロパティ: アプリケーションでは、http://apache.org/xml/features/disallow-doctype-declパーサー・プロパティをtrueに設定することもできます。この場合、受け取ったXMLドキュメントにDOCTYPE宣言が含まれていると、致命的なエラーがスローされます。このプロパティのデフォルト値はfalseです。SOAPメッセージには文書型定義を含めることができないため、一般にこのプロパティはSOAPベースのアプリケーションで便利です。
セキュア処理のための新機能: JAXP 1.3には、新しくセキュア処理機能が含まれているため、アプリケーションではSAXParserFactory
または DocumentBuilderFactory
を構成して、セキュリティ保護された方法で動作するXMLプロセッサを取得できます。 この機能をtrueに設定すると、エンティティ展開の制限が64000に設定されます。 デフォルトの制限は、entityExpansionLimit
システム・プロパティを使用して増やすことができます。