SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

カスタムな型定義を含むWSDL文書をJavaで扱う方法

XMLBeansを利用してXMLスキーマからJavaのクラスとインターフェイスを生成する

Streaming API for XML(StAX)

 XSDとWSDLは、標準のXML処理テクニックで処理できるXMLベースの文法に従っています。JavaでXML処理を行うには、主に次の2つのアプローチがあります。

  1. Simple API for XML processing(SAX)
  2. 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コードを生成します。

リスト2 generateメソッド
...
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>の配列を取り出して、それぞれの型を表すスキーマをコンテンツリストに追加します。

リスト3 addWsdlSchemasメソッド
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解析/処理テクニックよりも強力かつ柔軟な方法で動的に解釈および操作できるようになります。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Jeff Hanson(Jeff Hanson)

ソフトウェア業界で18年以上の経験を持つ。これまでにWindows OpenDocポートの上級エンジニア、NovellのRoute 66フレームワークの主席設計者を務める。現在は、J2EEベースの再保険システム用のフレームワークとプラットフォームの提供を専門とするeReinsureの主任設計者。著書、執筆記事多数。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/182 2005/12/06 18:03

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー