Java/XMLタイプマッピング

Novell exteNd WSSDKでは、JavaタイプからXMLタイプへのマッピングを包括的にサポートしていますが、アプリケーションによってはより高度なタイプマッピングが必要な場合があります。 これは、外来環境でJavaプログラムの単純でないデータタイプをJava以外のプログラムと交換する場合、特に注意が必要です。 そのようなアプリケーションの場合は、Javaタイプと外部XML表記とのマッピングを定義してランタイムにこのマッピングを使用するように指示する必要があります。

バージョン2.0では、ユーザ定義のタイプマッピングの必要性が大幅に減少しました。これは、xsd2javaコンパイラが複合タイプを処理するために、タイプマッピング、シリアライザ、およびデシリアライザを自動的に生成するためです。 このため、タイプマッピング機能はほとんどのアプリケーションで使用されませんが、エキスパート機能のステータスを持っています。 また、以前のマッピングパッケージは、標準のタイプマッピングフレームワークの新しいエンコーディングパッケージに置き換えられるので、注意が必要です。

Novell exteNd WSSDKを使用することによって、プログラマはタイプマッピングレジストリと呼ばれるレジストリを使用してXMLをJavaマッピングに定義することができます。 タイプマッパーには、JavaタイプおよびそのXMLマッピングについての情報が含まれています。 また、JavaタイプとXMLタイプの間にマップすることができるマーシャラオブジェクトがあります。 マーシャラには、JavaをXMLに変換するシリアライザと、XMLをJavaに変換するデシリアライザがあります。 Novell exteNd WSSDKでは、MapRemoteなどのタイプには、この同じアーキテクチャが内部で使用されます。

このセクションでは、Novell exteNd WSSDKのタイプマッピングレジストリ機能を示すアドレスブックの例について説明します。 Address Book Webサービスには、Addressと呼ばれる複合データタイプを使用してアドレスを保存したり取得できるメソッドがあります。 Addressクラスは、StreetNum、StreetName、Cith、State、およびzip codeなどのフィールドを持つ構成体(Java Bean)です。 例には、データ構造をシリアル化またはデシリアル化することが可能なAddressMashalerが定義されています。

1 Addressクラス

Addressクラスを次に示します。
package addressbook;
                                                                           
public class Address
{
    private int     streetNum;
    private String  streetName;
    private String  city;
    private String  state;
    private int     zipCode;
                                                                           
    public Address() {}
                                                                           
    public Address(String city, String state, String streetName, int streetNum,         int zipCode)
    {
    |   setCity(city);
    |   setState(state);
    |   setStreetName(streetName);
    |   setStreetNum(streetNum);
    |   setZipCode(zipCode);
    }
                                                                           
    public int getStreetNum() { return streetNum; }
                                                                           
    public void setStreetNum(int i) { streetNum = i; }
                                                                           
    public String getStreetName() { return streetName; }
                                                                           
    public void setStreetName(String s) { streetName = s; }
                                                                           
    public String getCity() { return city; }
                                                                           
    public void setCity(String s) { city = s; }
                                                                           
    public String getState() { return state; }
                                                                           
    public void setState(String s) { state = s; }
                                                                           
    public int getZipCode() { return zipCode; }
                                                                           
    public void setZipCode(int i) { zipCode = i; }
                                                                           
    public String toString()
    {
    |   return streetNum +" "+ streetName +" "+ city +" "+ state +" "+ zipCode;
    }
}
上のXMLタイプの例を次に示します。
<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://www.acme.org/Address"
?xmlns="http://www.w3.org/2001/XMLSchema">
?<complexType name="Address">
? <sequence>
????? <element name="streetNum" type="string"/>
????? <element name="streetName" type="string"/>
????? <element name="city" type="string"/>
????? <element name="state" type="string"/>
????? <element name="zipCode" type="string"/>
? </sequence>
?</complexType>
</schema>

図1: AddressデータタイプのXML定義。

2 AddressBookマーシャラ

マーシャラは、JavaタイプからXMLタイプにマップしたり、その逆方向にマップする方法を定義する重要なオブジェクトです。 XMLを使用して直接プログラミングすると、未完成でエラーが起こりやすいため、Novell exteNd WSSDKではデータのマーシャリングとマーシャリング解除のストリームをサポートしています。 ストリームは、多くのJavaプログラマにとって一般的で便利な概念ですが、DOMやSAX APIのストリームを使用した単調なプログラミングは拒否されます。XMLでは、java.io.DataInputStreamおよびjava.io.DataOutputStreamと同じAPIのストリームを使用してマーシャルおよびマーシャル解除されます。

AddressMarshalerクラスを次に示します。 表示されているように、データはJavaシリアル化に非常に近い方法で読み取られて書き込まれます。 Marshalerインタフェースは、XMLタイプ定義が属性を要求した場合に、getAttributesメソッドをサポートします。 このAPIを使用すると、アプリケーションプログラマはXMLプログラミングに表示されません。 その代わり、オブジェクトの状態を外部化および内部化する場合は、コードの行をほとんど使用しません。

package addressbook;
                                                                           
import java.io.IOException;
                                                                           
import com.sssw.jbroker.web.encoding.Attribute;
import com.sssw.jbroker.web.encoding.Marshaler;
                                                                           
import com.sssw.jbroker.web.portable.InputStream;
import com.sssw.jbroker.web.portable.OutputStream;
                                                                           
public class AddressMarshaler implements Marshaler
{
    public void serialize(OutputStream os, Object obj) throws IOException
    {
    |   Address address = (Address) obj;
    |                                                                      
    |   os.writeObject(address.getCity(), "city");
    |   os.writeObject(address.getState(), "state");
    |   os.writeObject(address.getStreetName(), "streetName");
    |   os.writeInt(address.getStreetNum(), "streetNum");
    |   os.writeInt(address.getZipCode(), "zipCode");
    }
                                                                           
    public Attribute[] getAttributes(Object obj) { return null; }
                                                                           
    public String getMechanismType() { return null; }
                                                                           
    public Object deserialize(InputStream is, Class cl) throws IOException
    {
    |   Address address = new Address();
    |                                                                      
    |   address.setCity((String) is.readObject(String.class, "city"));
    |   address.setState((String) is.readObject(String.class, "state"));
    |   address.setStreetName((String) is.readObject(String.class, "streetName"));
    |   address.setStreetNum(is.readInt("streetNum"));
    |   address.setZipCode(is.readInt("zipCode"));
    |                                                                      
    |   return address;
    }
}
AddressクラスはJava Beanなので、組み込まれている
BeanMarshalerを使用することもできます。 ここでは、カスタムマーシャラを実装する方法を示して、概念について説明しています。

3 AddressBook Webサービス

AddressBookサービスを次に示します。
package addressbook;
                                                                           
import java.rmi.Remote;
import java.rmi.RemoteException;
                                                                           
public interface AddressBook extends Remote
{
    void putAddress(String name, Address address) throws RemoteException;
                                                                           
    Address getAddress(String name) throws NoAddressFound, RemoteException;
                                                                           
    String[] getAllNames() throws RemoteException;
                                                                           
    Address[] getAllAddresses() throws RemoteException;
}

4 クライアント

クライアントプログラムでは、アドレスブックにいくつかのアドレスを入力し、後で照会します。 クライアントはさらに、TypeMappingRegistryimportTypeMappingsメソッドを使用してランタイムに既知のAddressタイプを作成する必要があります。 タイプマッパーは、タプル数6のリストを持つプロパティファイルから簡単にマッピングをロードできます。
<name> = <class> <serializer> <deserializer> <namespace> <local name> <location>
たとえば、次のように入力します。
#
# Address Book Type Library Descriptor
#
                                                                           
address=addressbook.Address addressbook.AddressMarshaler addressbook.AddressMarshaler urn:AddressBook Address Address.xsd
exception=addressbook.NoAddressFound addressbook.NoAddressFoundMarshaler addressbook.NoAddressFoundMarshaler urn:AddressBook NoAddressFound none
場所は、タイプのスキーマ定義への絶対パスです(例:http://www.acme.org/Address)。 クライアントプログラムを次に示します。
package addressbook;
                                                                           
import java.lang.reflect.Array;
                                                                           
import javax.xml.rpc.Stub;
import javax.naming.InitialContext;
                                                                           
import com.sssw.jbroker.web.ServiceObject;
import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry;
                                                                           
public class Client
{
    public static void main(String[] args) throws Exception
    {
    |   // lookup the AddressBook Service
    |   InitialContext ctx = new InitialContext();
    |   AddressBookService service = (AddressBookService)
    |       ctx.lookup("xmlrpc:soap:addressbook.AddressBookService");
    |   AddressBook abook = service.getAddressBookPort();
    |   // set the end point address
    |   ((Stub)abook)._setProperty("javax.xml.rpc.service.endpoint.address",
    |       args.length > 0 ? args[0] :"http://localhost:9090/addressbook");
    |                                                                      
    |   // create and set the type mapper
    |   DefaultTypeMappingRegistry registry =
    |       new DefaultTypeMappingRegistry();
    |   registry.importTypeMappings("address.mappings");
    |   ((ServiceObject)abook)._setTypeMappingRegistry(registry);
    |                                                                      
    |   // add some addresses to the address book
    |   abook.putAddress(frodo,   addr1);
    |   abook.putAddress(bilbo,   addr1);
    |   abook.putAddress(pippin,  addr2);
    |   abook.putAddress(merry,   addr2);
    |   abook.putAddress(samwise, addr3);
    |                                                                      
    |   // query individual addresses
    |   System.out.println(abook.getAddress(frodo));
    |   System.out.println(abook.getAddress(samwise));
    |                                                                      
    |   // get a list of all the names
    |   printArray(abook.getAllNames());
    |                                                                      
    |   // get a list of all the addresses
    |   printArray(abook.getAllAddresses());
    |                                                                      
    |   // look up an undefined name
    |   try {
    |   |   abook.getAddress(gollum);
    |   } catch (NoAddressFound ex) {
    |   |   System.out.println(ex.getName() + " is not a Hobbit");
    |   }
    }
                                                                           
    private static void printArray(Object array)
    {
    |   int len = Array.getLength(array);
    |                                                                      
    |   System.out.print("{");
    |                                                                      
    |   for (int i=0; i < len; i++) {
    |   |   System.out.print((i == 0) ? "" : ", ");
    |   |   Object element = Array.get(array, i);
    |   |   System.out.print(element);
    |   }
    |                                                                      
    |   System.out.print("}\n");
    }
                                                                           
    static String bilbo   = "Bilbo";
    static String frodo   = "Frodo";
    static String pippin  = "Pippin";
    static String merry   = "Merry";
    static String samwise = "Samwise";
    static String gollum  = "Gollum";
                                                                           
    static Address addr1 = new Address("YYYYYYYY", "ZZZZZ", "XXXX", 111, 0);
    static Address addr2 = new Address("BBBBBBBB", "CCCCC", "AAAA", 222, 1);
    static Address addr3 = new Address("LLLLLLLL", "MMMMM", "KKKK", 333, 1);
}

5 NoAddressFound

アドレスブックの例にも例外があり、標準BeanMarshalerを使用してマーシャルされます。
package addressbook;
                                                                           
public class NoAddressFound extends Exception
{
    private String _name;
                                                                           
    public NoAddressFound() { super("address not found"); }
                                                                           
    public NoAddressFound(String name) { _name = name; }
                                                                           
    public String getName() { return _name; }
                                                                           
    public void setName(String name) { _name = name; }
}

6 AddressBook Webサービス実装

サーバ実装では、TIEアプローチを使用して実装オブジェクトに委任します。 サーバはまた、タイプマッパーを取り込む必要があります。 これは、サーブレットのinitメソッドで一般的に行われます。
package addressbook;
                                                                           
import javax.servlet.ServletException;
                                                                           
import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry;
                                                                           
public class AddressBookImpl extends AddressBook_ServiceTieSkeleton
{
    public AddressBookImpl()
    {
    |   // initialize the delegate
    |   setTarget(new AddressBookDelegate());
    }
                                                                           
    public void init() throws ServletException
    {
    |   super.init();
    |                                                                      
    |   // load and set the type library
    |   // Note: you may have to use the context class loader instead on S3
    |                                                                      
    |   try {
    |   |   DefaultTypeMappingRegistry registry =
    |   |       new DefaultTypeMappingRegistry();
    |   |   registry.importTypeMappings("address.mappings", this.getClass().
    |   |       getClassLoader());
    |   |   _setTypeMappingRegistry(registry);
    |   } catch (Throwable t) {
    |   |   t.printStackTrace();
    |   |   throw new ServletException(t.getMessage());
    |   }
    }
}

7 TIE実装

委任実装のリストを次に示します。
package addressbook;
                                                                           
import java.util.HashMap;
                                                                           
import java.rmi.RemoteException;
                                                                           
public class AddressBookDelegate implements AddressBook
{
    private final HashMap _map = new HashMap();
                                                                           
    public synchronized void putAddress(String name, Address address)
        throws RemoteException
    {
    |   _map.put(name, address);
    }
                                                                           
    public synchronized Address getAddress(String name)
        throws NoAddressFound, RemoteException
    {
    |   Address addr = (Address) _map.get(name);
    |   if (addr == null) throw new NoAddressFound(name);
    |   return addr;
    }
                                                                           
    public synchronized String[] getAllNames() throws RemoteException
    {
    |   Object[] objs = _map.keySet().toArray();
    |   String[] names = new String[objs.length];
    |   for (int i=0; i < objs.length; i++)
    |       names[i] = (String) objs[i];
    |   return names;
    }
                                                                           
    public synchronized Address[] getAllAddresses() throws RemoteException
    {
    |   Object[] objs = _map.values().toArray();
    |   Address[] addrs = new Address[objs.length];
    |   for (int i=0; i < objs.length; i++)
    |       addrs[i] = (Address) objs[i];
    |   return addrs;
    }
}

8 rmi2wsdlを使用したスキーマの生成

rmi2wsdlを使用して、リモートインタフェースでWSDLを生成します。 WSDLを生成する際に、リモートインタフェースメソッドで使用するタイプのスキーマを生成することができます。 マッピングは、-typemappingフラグを使用して指定することができます。例:
rmi2wsdl -typemapping address.mappings addressbook.AddressBook

生成されたWSDLを次に示します。
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="AddressBookService"
 targetNamespace="http://www.addressbook"
 xmlns="http://schemas.xmlsoap.org/wsdl/"
 xmlns:ns0="http://www.lang.java"
 xmlns:ns1="urn:AddressBook"
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:tns="http://www.addressbook"
 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <types>
  <schema targetNamespace="http://www.lang.java">
   <complexType name="ArrayOfString">
    <complexContent>
     <restriction base="soapenc:Array">
      <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:string[]"/>
     </restriction>
    </complexContent>
   </complexType>
  </schema>
  <schema targetNamespace="http://www.addressbook"
   xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <complexType name="Address">
    <sequence>
     <element name="city" type="string"/>
     <element name="state" type="string"/>
     <element name="streetName" type="string"/>
     <element name="streetNum" type="int"/>
     <element name="zipCode" type="int"/>
    </sequence>
   </complexType>
  </schema>
  <schema targetNamespace="http://www.addressbook">
   <complexType name="ArrayOfAddress">
    <complexContent>
     <restriction base="soapenc:Array">
      <attribute ref="soapenc:arrayType" wsdl:arrayType="ns1:Address[]"/>
     </restriction>
    </complexContent>
   </complexType>
  </schema>
 </types>
 <message name="getAllNamesRequest"/>
 <message name="getAllNamesResponse">
  <part name="result" type="ns0:ArrayOfString"/>
 </message>
 <message name="getAllAddressesRequest"/>
 <message name="getAllAddressesResponse">
  <part name="result" type="tns:ArrayOfAddress"/>
 </message>
 <message name="putAddressRequest">
  <part name="arg0" type="xsd:string"/>
  <part name="arg1" type="ns1:Address"/>
 </message>
 <message name="putAddressResponse"/>
 <message name="getAddressRequest">
  <part name="arg0" type="xsd:string"/>
 </message>
 <message name="getAddressResponse">
  <part name="result" type="ns1:Address"/>
 </message>
 <message name="NoAddressFound">
  <part name="name" type="xsd:string"/>
 </message>
 <portType name="AddressBook">
  <operation name="getAllNames">
   <input message="tns:getAllNamesRequest"/>
   <output message="tns:getAllNamesResponse"/>
  </operation>
  <operation name="getAllAddresses">
   <input message="tns:getAllAddressesRequest"/>
   <output message="tns:getAllAddressesResponse"/>
  </operation>
  <operation name="putAddress" parameterOrder="arg0 arg1">
   <input message="tns:putAddressRequest"/>
   <output message="tns:putAddressResponse"/>
  </operation>
  <operation name="getAddress" parameterOrder="arg0">
   <input message="tns:getAddressRequest"/>
   <output message="tns:getAddressResponse"/>
   <fault message="tns:NoAddressFound" name="NoAddressFound"/>
  </operation>
 </portType>
</definitions>
Copyright © 2000-2003, Novell, Inc.All rights reserved.