Streaming API for XML(StAX)
XSDとWSDLは、標準のXML処理テクニックで処理できるXMLベースの文法に従っています。JavaでXML処理を行うには、主に次の2つのアプローチがあります。
- Simple API for XML processing(SAX)
- Document Object Model(DOM)
SAXはコールバック方式の解析APIを公開しているのに対し、DOMはランダムにアクセスできるツリー構造を公開しています。
Streaming API for XML(StAX)仕様は、XMLの解析とストリーミングを行うための新しい非常に効率的なAPIを定義しています。StAX仕様はXMLの読み取りと書き込みを行う双方向のAPIを定義しており、開発者はnext()やhasNext()などの直感的メソッドを含んだ単純な反復子ベースのAPIと基盤となるイベントストリームを公開することで、解析制御を行うことができます。この反復子ベースのAPIでは、コールバック内で次のイベントを処理するのではなく、次のイベントを「プル」できます。このカーソルAPIを使用すると、処理の停止、文書中の先のセクションへのスキップ、文書のサブセクションの取得などを行って、さらなる処理制御をすることができます。
XMLBeansの概要
Apache XMLBeansは、StAX仕様に基づくオープンソースのXML-Javaバインディングツールです。XMLBeansを使用すると、XMLスキーマからJavaのクラスやインターフェイスを生成でき、この生成されたJavaクラスを使用して、そのXMLスキーマに従っているXML文書を解析または生成することができます。
XMLBeansは元々はBEA社が開発したものですが、現在はApache Software Foundationに寄贈されています。XMLBeansでは、XMLとスキーマにアクセスするために3種類の主要なAPIが用意されています。
- 厳密に型指定されたアクセス……XMLBeansでは、XMLスキーマをコンパイルしてJavaのクラスとインターフェイスを生成できます。生成されたクラスとインターフェイスはいずれも共通の基本クラスを拡張したものであり、スキーマ内の各要素に対して厳密に型指定されたゲッターとセッターを提供します。
- カーソル方式のアクセス……生成されたクラスから、XML情報への低レベルアクセスを可能にする位置カーソルオブジェクトのインスタンスを取得できます。
- スキーマ方式のアクセス……XMLBeansには、XMLスキーマを反映するために使用するスキーマオブジェクトモデルが用意されています。
XMLBeansのインストール
XMLBeansをインストールするには、XMLBeansのバイナリディストリビューションをダウンロードし、そのアーカイブを適切なディレクトリに展開します。次に、XMLBEANS_HOME環境変数を作成して、その値にバイナリディストリビューションの展開先ディレクトリを指定します。最後に、XMLBeansディストリビューションに含まれていた.jarおよび.zipファイルをプロジェクトに追加します。
この記事の残りの部分では、XMLBeansのフレームワークとAPIを使用して、WSDLからJavaへの単純なインタープリタを実装する方法を説明します。ここで理解しておいてもらいたいのは、今日ではますます多くのWebサービスプロバイダがそのシステムを公共利用可能なサービスに統合するための交換規約としてWSDLを採用しているため、サービスクライアントは、そうしたサービスを利用するためにWSDLを動的に処理できなければならないということです。WSDL文書がしばしばサービス情報への唯一のアクセスポイントとなるのはこのためです。幸い、WSDL文書が適切に準備されていれば、クライアント側に必要なのはWSDL文書だけになります。
XMLBeansのフレームワークは、WSDLファイルをJavaオブジェクトへとコンパイルするための機能を提供します。この機能により、開発者はWebサービスのデータ型とメソッドに関連付けられているJavaクラスやインターフェイスを使用して、WSDL文書からWebサービスクライアントを動的に生成することができます。WSDLを動的に解釈することで、標準の静的なXML解析/処理テクニックよりも大きな威力が生まれます。
XMLBeansによるカスタムWSDLインタープリタの作成
次に示すWSDL2Javaクラスは、XMLBeansを使用して実装したWSDLからJavaへの単純なインタープリタの例です。この記事のソースコードはすべてダウンロードサンプルに含まれています。
package com.jeffhanson.clienttier; import org.apache.xmlbeans.*; import org.apache.xmlbeans.impl.xb.xsdschema.*; import org.apache.xmlbeans.impl.xb.xsdschema.impl.*; import org.apache.xmlbeans.impl.xb.substwsdl.*; import org.apache.xmlbeans.impl.util.FilerImpl; import org.apache.xmlbeans.impl.schema.SchemaTypeSystemCompiler; import org.apache.xmlbeans.impl.schema.SchemaTypeLoaderImpl; import org.apache.xmlbeans.impl.schema.StscState; import org.apache.xmlbeans.impl.common.XmlErrorWatcher; import org.apache.xmlbeans.impl.common.IOUtil; import java.util.*; import java.io.File; import repackage.Repackager; public class WSDL2Java { private static class MyResolver implements org.xml.sax.EntityResolver { public org.xml.sax.InputSource resolveEntity( String publicId, String systemId) { System.out.println("System ID: " + systemId); return null; } } ...
リスト2に示すgenerateメソッドは、与えられたWSDLファイルを解析して、そのWSDL文書の<Types>セクションに定義されていたデータ型を表すJavaコードを生成します。
... public List generate(String schemaFilesDir, String schemaClassesDir, String schemaSrcDir, String wsdlFileName) { boolean doNotValidateContents = false; XmlObject wsdlDoc = null; MyResolver entityResolver = new MyResolver(); ArrayList outerErrorListener = new ArrayList(); XmlErrorWatcher errorListener = new XmlErrorWatcher(outerErrorListener); ArrayList scontentlist = new ArrayList(); try { SchemaTypeLoader loader = XmlBeans.typeLoaderForClassLoader( SchemaDocument.class.getClassLoader()); XmlOptions options = new XmlOptions(); options.setLoadLineNumbers(); options.setLoadSubstituteNamespaces( Collections.singletonMap( "http://schemas.xmlsoap.org/wsdl/", "http://www.apache.org/internal/xmlbeans/wsdlsubst")); options.setEntityResolver(entityResolver); System.out.println("Parsing WSDL: " + wsdlFileName + "..."); wsdlDoc = loader.parse(new File(wsdlFileName), null, options); if (!(wsdlDoc instanceof DefinitionsDocument)) { // Create new definitions doc DefinitionsDocument.Definitions definitions = DefinitionsDocument. Definitions.Factory.newInstance(); // Set types array definitions.setTypesArray( wsdlDoc.selectPath("wsdl:types")); DefinitionsDocument defDoc = DefinitionsDocument.Factory.newInstance(); defDoc.setDefinitions(definitions); // Adding constructed schema addWsdlSchemas(wsdlFileName, defDoc, errorListener, doNotValidateContents, scontentlist); } else { addWsdlSchemas(wsdlFileName, (DefinitionsDocument)wsdlDoc, errorListener, doNotValidateContents, scontentlist); } } catch (XmlException e) { errorListener.add(e.getError()); } catch (Exception e) { StscState.addError(errorListener, XmlErrorCodes.CANNOT_LOAD_FILE, new Object[] { "wsdl", wsdlFileName, e.getMessage() },wsdlDoc); } SchemaTypeSystem sts = compileSchemas(schemaFilesDir, scontentlist, entityResolver, errorListener); return generateJavaSource(schemaClassesDir, schemaSrcDir, sts); } ...
次に示すcompileSchemasメソッドは、XMLBeans固有のスキーマファイル(.xsb)を作成してディスクに保存します。
private SchemaTypeSystem compileSchemas( String schemaFilesDir, ArrayList scontentlist, MyResolver entityResolver, XmlErrorWatcher errorListener) { SchemaDocument.Schema[] sdocs = (SchemaDocument.Schema[])scontentlist.toArray( new SchemaDocument.Schema[scontentlist.size()]); ResourceLoader cpResourceLoader = null; SchemaTypeLoader linkTo = SchemaTypeLoaderImpl.build(null, cpResourceLoader, null); File baseDir = new File(System.getProperty( "user.dir")); java.net.URI baseURI = baseDir.toURI(); XmlOptions opts = new XmlOptions(); opts.setCompileNoValidation(); opts.setEntityResolver(entityResolver); Map sourcesToCopyMap = new HashMap(); File schemasDir = IOUtil.createDir(new File("."), schemaFilesDir); // create parameters for the main compile function SchemaTypeSystemCompiler.Parameters params = new SchemaTypeSystemCompiler.Parameters(); params.setName(null); params.setSchemas(sdocs); params.setLinkTo(linkTo); params.setOptions(opts); params.setErrorListener(errorListener); params.setJavaize(true); params.setBaseURI(baseURI); params.setSourcesToCopyMap(sourcesToCopyMap); params.setSchemasDir(schemasDir); System.out.println("Compiling schemas..."); try { // create schema files (.xsb's) SchemaTypeSystem sts = SchemaTypeSystemCompiler.compile(params); // now save .xsb's to disk sts.saveToDirectory(schemasDir); System.out.println( "Schema compilation succeeded"); return sts; } catch (Exception e) { e.printStackTrace(); } return null; }
次に示すgenerateJavaSourceメソッドは、それまでに指定の型システムに追加されていたWSDLデータ型に関するJavaソースファイルを生成します。
private List generateJavaSource(String schemaClassesDir, String schemaSrcDir, SchemaTypeSystem sts) { File classesDir = new File(schemaClassesDir); File srcDir = IOUtil.createDir(new File("."), schemaSrcDir); // now, generate the source files XmlOptions options = new XmlOptions(); boolean verbose = false; boolean incrSrcGen = false; Repackager repackager = null; FilerImpl filer = new FilerImpl(classesDir, srcDir, repackager, verbose, incrSrcGen); System.out.println("Generating Java source..."); if (SchemaTypeSystemCompiler.generateTypes(sts, filer, options)) { System.out.println( "Java source generation succeeded"); return filer.getSourceFiles(); } else { System.out.println("Java source generation failed"); } return null; }
リスト3に示すaddWsdlSchemasメソッドは、WSDL定義文書から<Types>の配列を取り出して、それぞれの型を表すスキーマをコンテンツリストに追加します。
private void addWsdlSchemas(String wsdlFileName, DefinitionsDocument definitionsDocument, XmlErrorWatcher errorListener, boolean doNotValidateContents, ArrayList scontentlist) { XmlOptions opts = new XmlOptions().setErrorListener( errorListener); if (doNotValidateContents) { opts.setValidateTreatLaxAsSkip(); } XmlObject[] types = definitionsDocument.getDefinitions() .getTypesArray(); for (int j = 0; j < types.length; j++) { XmlObject[] schemas = types[j].selectPath("declare namespace" + " xs=\"http://www.w3.org/2001/XMLSchema\"" + " xs:schema"); if (schemas.length == 0) { StscState.addWarning(errorListener, "The WSDL " + wsdlFileName + " has no schema documents in namespace " + "'http://www.w3.org/2001/XMLSchema'", XmlErrorCodes.GENERIC_ERROR, definitionsDocument); continue; } for (int k = 0; k < schemas.length; k++) { if (schemas[k] instanceof SchemaDocument.Schema) { SchemaDocumentImpl.SchemaImpl schemaImpl = (SchemaDocumentImpl.SchemaImpl)schemas[k]; System.out.println("Validating schema..."); if (schemaImpl.validate(opts)) { System.out.println("Schema passed validation"); scontentlist.add(schemas[k]); } else { System.out.println("Schema failed validation"); scontentlist.add(schemas[k]); } } } } }
次に示すJavaSourceCompilerクラスは、XMLBeans APIのユーティリティを使用して、それまでに生成したJavaソースファイルをコンパイルします。
package com.jeffhanson.clienttier; import org.apache.xmlbeans.impl.tool.CodeGenUtil; import org.apache.xmlbeans.impl.common.IOUtil; import java.io.File; import java.util.List; public class JavaSourceCompiler { public JavaSourceCompiler() { } public void generate(String schemasDirName, List sourceFiles, String classesDirName) { File classesDir = new File(classesDirName); File schemasDir = IOUtil.createDir(new File("."), schemasDirName); boolean verbose = false; // now, compile source files String compiler = "/tools/VMs/j2sdk1.4.2_04/bin/javac.exe"; File[] tempClasspath = CodeGenUtil.systemClasspath(); File[] classpath = new File[tempClasspath.length + 1]; System.arraycopy(tempClasspath, 0, classpath, 0, tempClasspath.length); classpath[tempClasspath.length] = schemasDir; boolean debug = false; String javasource = null; String memoryInitialSize = CodeGenUtil.DEFAULT_MEM_START; String memoryMaximumSize = CodeGenUtil.DEFAULT_MEM_MAX; boolean quiet = true; System.out.println("Compiling Java source files..."); if (CodeGenUtil.externalCompile( sourceFiles, classesDir, classpath, debug, compiler, javasource, memoryInitialSize, memoryMaximumSize, quiet, verbose)) { System.out.println("Source compile succeeded"); } else { System.out.println("Source compile failed"); } } }
この記事のダウンロードサンプルを見て、自分で実際に試してみてください。WebサービスのメッセージングとXMLベースのデータ転送では、メッセージ交換を表現するための構造化された方法が必要になります。WSDLは、どんなプログラミング言語にも対応できるXMLベースのサービス記述と汎用的なデータ型システムを定義することで、この必要性に対処しています。
WSDLのデータ型システムは、サービスコンシューマとサービスプロバイダの間で交換できるデータ型を定義します。WSDLでは、組み込みのXML Schema単純型を使用することも、独自のカスタム型を定義することもできます。
Streaming API for XML(StAX)は、XMLの解析とストリーミングを行う双方向のAPIを使用する新しいAPIを定義しています。StAXは、単純な反復子ベースのAPIとカーソルAPIを公開することで、解析制御を行えるようにします。反復子ベースのAPIでは、必要に応じてイベントを「プル」できます。カーソルAPIを使用すると、処理の停止、文書中の先のセクションへのスキップ、文書のサブセクションの取得などを行って、さらなる処理制御をすることができます。
Apache XMLBeansは、StAX仕様に基づくオープンソースのXML-Javaバインディングツールであり、これを使用して、XMLスキーマからJavaのクラスとインターフェイスを生成することができます。その後、これらのクラスとインターフェイスを使用して、同じXMLスキーマに従っているXML文書を解析または生成できます。
StAXとXMLBeansフレームワークの組み合わせにより、開発者はWSDL文書とその中で宣言されているデータ型を、標準の静的XML解析/処理テクニックよりも強力かつ柔軟な方法で動的に解釈および操作できるようになります。
