![]() ![]() ![]() ![]() ![]() |
開発ガイド 05/16/03 09:21:28 |
この章では、さまざまなソースからWebサービスを生成する「Web Serviceウィザード」の使用について、基本手順と一般的なシナリオを説明します。トピックは次のとおりです。
プログラムからWebサービスにアクセスする場合のウィザードの使用手順とシナリオについては、
を参照してください。
WorkbenchのWeb Serviceウィザードを使用すると、「Javaリモートオブジェクト」(RMIを使用)として実装される「標準(SOAPベース)のWebサービス」を開発することができます。ウィザードでは、 JAX-RPC (Java API for XML-based RPC)および jBroker Web (Novell exteNdに含まれているJAX-RPX実装)に基づいて、Javaソースファイルを生成します。JAZ-RPCは、Webサービスサポートを提供するJ2EE指定です。
生成されたファイルには、WebサービスへのアクセスおよびHTTP SOAPリクエストからのメソッドを処理する「サーブレット」が含まれています。生成されたファイルは、そのまま使用したり、必要に応じて変更したりできます。このJava指向の方法には、低レベルのSOAP APIをコーディングする代わりに、熟知しているRMIおよびJ2EEの技術を使用してWebサービスを処理できるという利点があります。
Webサービスの概念、標準、および技術の紹介については、
を参照してください。
ウィザードの詳細については、『ツールガイド』の章「
Web Serviceウィザード」を参照してください。
Web Serviceウィザードの使用を準備するには、次の操作を行います。
Workbenchで「WARプロジェクト」をセットアップします。
生成する各Webサービスに対して、ウィザードでは、(HTTP SOAPリクエストから)そのWebサービスへのアクセスを処理する「サーブレット」を作成します。結果として、WARでは、配備するWebサービス(WARにつき1つまたは複数)を、実行場所であるJ2EEサーバにパッケージ化する必要があります。
その他の方法は、WARで「JARサブプロジェクト」をセットアップし、このJARを使用して、Webサービスに対するサーブレットおよびその他のクラスを含めることです。いずれの場合でも、「サーブレットマッピング」がWARの配備記述子(web.xml)に含まれます。
WSDLファイルからWebサービスを生成する場合、JARサブオブジェクトを使用する方法はWeb Serviceウィザードによって現在サポートされていません。この状況では、WARプロジェクトのみがサポートされます。
ファイル |
詳細 |
---|---|
Webサービスが生成される元のソースファイル、クラス、またはアーカイブ |
Webサービスは、次のいずれかから生成できます。 いずれを指定した場合でも、生成されたWebサービスで表示されるメソッドが(最低限)宣言される必要があります。 「Java ファイルをコンパイルする」 Javaファイルを指定した場合は、Web Serviceウィザードを開始する前に、プロジェクトでこれらのファイルをコンパイルする必要があります(ウィザードはコンパイルされたクラスから機能するため)。 「WSDLバインドを編集する」 WSDLファイルを指定した場合は、サービス定義のSOAPアドレスによって正しいバインドURLが指定されるように、ファイルを必要に応じて修正します。Web Serviceウィザードでは、Webサービスに対して生成するファイルでこのURLを使用します。 |
jBroker Webに必要なアーカイブ: |
これらのJARは、Workbench compilelibディレクトリにあります。J2EEサーバの設定に基づいて、次のいずれかの操作を行います。
|
生成、編集された後でWeb Serviceクラスをコンパイルできるように、プロジェクトの「クラスパス」を編集します。次のアーカイブを含める必要があります。
アプリケーションで「SOAPメッセージハンドラ」(高度なJAR-RPC機能)を使用する場合、プロジェクトには、activation.jar、commons-logging.jar、dom4j.jar、jaxp-api.jar、およびsaaj-ri.jarというアーカイブも必要です。これらのJARは、Workbench compilelibディレクトリにあります。
WARプロジェクトをセットアップしたら、Web Serviceウィザードを使用する準備が整ったことになります。ウィザードでは、一度に1つのWebサービスが作成されるため、複数を開発する場合は、Webサービスを複数回使用する必要があります。
ウィザードを起動するたびに、作成するWebサービスの種類に関する入力が使用されます。その後、Webサービスを構成する1セットのソースファイルが生成されます。このプロセスの要約は次のとおりです。
[File]>[New]の順に選択して[New File]ダイアログボックスを表示し、[Web Services]タブに移動します。
次のいずれかの操作を実行して、Web Serviceウィザードを起動します。
「プロジェクトの場所」についての情報を入力するようウィザードで要求されたら、次の項目を指定します。
生成されたWebサービスファイルを含むようにセットアップした 「WARプロジェクトまたはJARプロジェクト」 (WSDLファイルから生成する場合は、ウィザードによってここでWARプロジェクトを指定するよう要求されます)。
このプロジェクトのターゲットの「ディレクトリおよびパッケージ」(Javaクラスから生成する場合、これらの設定の一部は入力する必要がありません。これは、ウィザードによって自動的に処理されるためです)。
生成されたWebサービスファイルを含むようにJARプロジェクトを指定した場合、ウィザードによって、WebサービスのサーブレットをマップするWARプロジェクトを指定するよう求められます。
ウィザードによってメッセージが表示されたら、Webサービスの生成元である「クラスまたはWSDLファイル」を選択します。
その後、選択した項目に基づいて追加の情報を入力するよう求められます。
選択項目 |
ウィザードによって指定するよう求められる内容 |
---|---|
JavaBeanまたはその他のJavaクラス |
|
EJBセッションBeanのホームインタフェース |
|
EJBセッションBeanのリモートインタフェースまたはSessionBeanクラス自体 |
|
Javaリモートインタフェース |
|
WSDLファイル |
「クラス生成およびSOAPオプション」を入力するようウィザードで要求されたら、Webサービスに対して生成する1セットのソースファイルを選択して設定する必要があります。
最も重要な選択は、「スケルトン」を「結合ベース」になるように生成するかどうかということです。この答えは、Webサービスの実装で従うアーキテクチャモデルによって異なります。 実装モデルの選択を参照してください。
Webサービスをテストするための「スタブ」(単純なクライアントアプリケーションに付属)を生成することを選択できます。Javaクラスから生成する場合は、「WSDLファイル」(Webサービスをレジストリに発行するため)を要求したり、「バインドスタイル」(ドキュメントまたはRPC)およびWebサービスの「サービスアドレス」(URL)を指定したりすることもできます。WSDLファイルから生成する場合は、「複合タイプ」がどのようにマップされるかを指定できます。
注記: jBroker Web 1.xアプリケーションのサポートは、「旧製品との互換性」のオプションを使用して有効にできます。詳細については、 jBroker Web 1.xの互換性を選択した場合を参照してください。
ウィザードを終了すると、Webサービスに対して指定したものがすべて生成され、プロジェクトの他の部分は、サポートする変更内容で更新されます。
ファイル名を生成する場合、Web Serviceウィザードでは、JAX-RPCによって指定された名前付けルールに従います。Javaクラスで開始すると、結果として生成されるファイル名は、そのクラスの名前に基づいて付けられます。WSDLで開始すると、結果として生成されるファイル名は、そのWSDLの定義に基づいて付けられます。
簡単にするため、このマニュアルでは、クラス名またはWSDL定義から派生して生成されたWebサービスのファイル名の一部を表すのにxxxを使用します。
Web Serviceウィザードでは、Webサービスのファイル(前のリストを参照)を生成する際に、背後で「jBroker Webコンパイラ」を使用します。場合によっては、次のようなアプリケーションに固有の要件をサポートするために、これらのコンパイラによって追加コードやファイルが生成されることがあります。
詳細については、
jBroker Webヘルプを参照してください。
jBroker Webの現在のバージョンには、以前のバージョンとの高度な互換性があります。ただし、JAX-RPC標準をサポートするために導入された変更の中には、jBroker Web 1.xに由来するアプリケーションをアップグレードする場合にコード変更しなければならないものもあります。このような変更には、次に対して使用される規則が含まれています。
ファイル名 JAX-RPCでは、特定のWebサービスファイルに名前を付ける場合の規則が指定されます。生成される名前のすべてを簡潔かつ一貫したものに保ちながらこれらの規則に従うために、新しい名前のパターンが採用されています(詳細については、次の「 生成された1.x互換ファイル」を参照してください)。
クライアントコードでのスタブアクセス JAX-RPCでは、JNDI経由でスタブが直接検索される代わりに、クライアントによってサービスオブジェクトが使用され、スタブがインスタンス化されます。
現在のjBroker WebおよびJAX-RPCの規則にアップグレードすることは推奨されていますが、必須ではありません。Web ServiceウィザードのjBroker Web 1.xの「互換性」オプションを使用することによって、ファイル名およびスタブアクセスに対する元のjBroker Web規則に従ってWebサービスファイルを生成できます。これにより、既存の1.xアプリケーションを変更することなく、jBroker Webの最新バージョンの他の改善点をすべて活用できるようになります。
生成された1.x互換ファイル jBroker Web 1.xの互換性オプションを使用すると生成されるファイルについては、次の表のとおりです。
Web Serviceウィザードによって生成されたファイルを編集する場合は、次のガイドラインに従います。
ガイドライン |
詳細 |
---|---|
編集しなければならない可能性があるファイル |
|
編集する必要のあるファイル |
|
編集してはいけないファイル |
生成された他のファイルは編集してもかまいませんが、通常は必要ではありません。
場合によっては、Webサービスの実装を完了するために、手動でコード化されたファイルを1つまたは複数追加して、生成されたファイルとともに使用できるようにしなければならないことがあります。 追加ファイルの作成を参照してください。
生成されたxxxTie.javaファイルには、編集しなければならない可能性のあるメソッドが2つ含まれています。
JavaBeanまたはJavaクラスで開始すると、xxx_ServiceTieSkeletonのsetTarget()メソッドを呼び出し、(委任する) xxxDelegateのインスタンスを渡すために、init()が生成されます。xxxDelegateによって空のコンストラクタが提供された場合、生成されたコードでは、そのコンストラクタを使用してインスタンス化を行います。
ただし、暗黙的または明示的な空のコンストラクタがない場合は、使用するコンストラクタを指定するようにコードを変更しなければなりません。また、引数を期待するコンストラクタを使用するようにコードを変更することもできます。
ウィザードでは、xxxDelegateの各パブリックコンストラクタに対してsetTarget()への呼び出しを自動的に生成します。空のコンストラクタ(存在する場合)を使用する行以外の各行は、コメントアウトされます。希望のコンストラクタのある行のコメントを解除し、関連する変更を行います。
//super.setTarget( new MyObjectWSDelegate( java.lang.String arg0) );
//super.setTarget( new MyObjectWSDelegate( java.lang.String arg0, java.lang.String arg1) );
super.setTarget( new MyObjectWSDelegate( ) );
JavaリモートインタフェースまたはWSDLファイルで開始する場合、init()は、setTarget()呼び出しがコメントアウトされた状態で常に生成されます。この場合、インスタンス化して委任する独自のクラスを指定しなければなりません。
//super.setTarget(new CONSTRUCT_YOUR_SERVICE_OBJECT_HERE);
EJBセッションBeanで開始する場合、生成されたinit()メソッドを編集する必要はありません。
このメソッドは、Webサービスに送信されたHTTP GETリクエストを処理するために生成されます。このメソッドによって、WebサービスのWSDLファイル(存在する場合)が返されます。このファイルが返されない場合は、GETリクエストがサポートされていないことがユーザに通知されます。
独自のHTTP GET動作を実装する場合は、doGet()コードをカスタマイズできます。デフォルトのSOAP動作を使用する場合は、このコードを削除するか、またはコメントアウトできます。
xxxClientでWebサービスをテストする前に、Webサービスの1つまたは複数のメソッドを呼び出すように、生成されたxxxClient.javaファイルを編集する必要があります。このファイルで「process()メソッド」を検索すると、考えられるメソッド呼び出しがすべてリストされたコメントが見つかります。
// System.out.println("Test Result = " + remote.getString()); // System.out.println("Test Result = " + remote.setString(java.lang.String)); // System.out.println("Test Result = " + remote.sayHello());
テストするメソッド呼び出しのコメントを解除し、必要に応じて適切な引数値を指定します。
// System.out.println("Test Result = " + remote.getString()); System.out.println("Test Result = " + remote.setString(args[0])); System.out.println("Test Result = " + remote.sayHello());
生成されたxxxClient.javaファイルに対するその他の変更については、
を参照してください。
多くの場合、ウィザードで生成が終了すると、Webサービスに必要なJavaソースファイルがすべて使用可能になります。ただし、手動で追加のクラスをコード化しなければならない場合もあります。
ウィザードによって生成されたWebサービスファイルを使用するには、次の操作を行います。
結合モデルを使用する場合、ウィザードでは、WARプロジェクトのweb.xmlファイルをWebサービスの適切なサーブレットマッピングで更新します。ただし、スケルトンモデルの場合は、この情報を指定するようにweb.xmlを手動で編集する必要があります。
次の例では、MyServiceは、WebサービスMyRemoteに対して開発者がコード化したサーブレットクラスです。
<servlet> <servlet-name>MyService</servlet-name> <servlet-class>com.exsamp.rem.MyService</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyService</servlet-name> <url-pattern>MyRemote</url-pattern> </servlet-mapping>
ウィザードが機能するにつれて、ファイルは、必要に応じてプロジェクトのクラスパスおよびコンテンツに自動的に追加されます。ただし、コンパイルおよび実行するために必要なものがすべてプロジェクトに存在しているかどうかを自分で確認することも必要です。
たとえば、WebサービスでEJBセッションBeanにアクセスする場合、EJBクライアントJARファイルはプロジェクトのクラスパスに存在しています。
プロジェクトに必要なクラスパスおよびコンテンツ(jBroker Webで必要なものを含む)のセットアップの詳細については、
生成の準備を参照してください。
この手順を終了すると、生成したWebサービスを含むWARファイルが使用可能になります。
WARをJ2EEサーバに配備するために必要なサーバ固有の配備情報を準備します。たとえば、Novell exteNd Application Serverに配備する場合は、exteNd配備計画ファイルを作成します。
Workbenchから配備する場合は、J2EEサーバのサーバプロファイルをセットアップする必要もあります。
この手順を完了すると、WARの各Webサービスは、表示されたメソッドの標準HTTP SOAPリクエストに応答できるサーブレットとしてアクセス可能になります。
WebサービスのxxxClientクラスを生成、編集、およびコンパイルした場合は、このクラスをメソッド呼び出しの簡単なテスト用に使用できます。WorkbenchからxxxClientを実行するには、[Project]>[Run Web Service Client Class]の順に選択します。Web Service Wizard Client Runnerが表示され、現在のプロジェクトから選択できるクライアントクラスのリストが提供されます。
xxxClientはコマンドラインから実行することもできます(システムクラスパスに適切なディレクトリおよびアーカイブを含めた場合)。
Web Serviceウィザードを使用して開発する場合に選択できる基本的な実装モデルには、2種類あります。この節では、生成するWebサービスに最も適した実装モデルを選択できるようにするこれらの実装モデルについて説明します。
結合モデルの概要と、このモデルを使用する場合については、次のとおりです。
Web Serviceウィザードへの入力として指定するJavaリモートインタフェースまたはWSDLファイルしかない場合、この結合モデルを使用することは可能ですが、一般的ではありません。この場合、ウィザードの出力では、後で完了できるように、モデルの委任部分が残されます。その後、実装クラスをコード化し、インスタンス化して委任するように生成された結合クラスを編集する必要があります。
スケルトンモデルの概要と、このモデルを使用する場合については、次のとおりです。
このシナリオでは、表示するメソッドを実装する既存のJavaクラスに基づいてWebサービスを生成するために、Web Serviceウィザードをどのように使用できるかについて確認できます。
実装モデル このシナリオでは、「結合」モデルの使用が示されています。このアーキテクチャの概要については、 実装モデルの選択を参照してください。
このシナリオのWARプロジェクトは、次のようにセットアップされます。
WebServiceSample.spf
WebServiceSample.war
WEB-INF lib jbroker-web.jar jaxrpc-api.jar saaj-api.jar xerces.jar classes com exsamp obj MyObject.java web.xml
...\WEB-INF\lib\jbroker-web.jar
...\WEB-INF\lib\jaxrpc-api.jar
...\WEB-INF\lib\saaj-api.jar
...\WEB-INF\lib\xerces.jar
...\exteNdWorkbench\compilelib\j2ee_api_1_n.jar
このシナリオでWeb Serviceウィザードに対して指定されている入力は、次のとおりです。
MyObjectは、Webサービスの生成元となる既存のJavaクラスです。これにより、表示するメソッドが実装されます。MyObject.javaには、(ウィザードを開始する前にコンパイルする必要のある)次のコードが含まれています。
package com.exsamp.obj; public class MyObject { private String s; public MyObject() { } public MyObject(String xxx) { } public MyObject(String xxx, String yyy) { } public String getString() { return s; } public boolean setString(String s) { this.s = s; return true; } public String sayHello() { return "Hello there, I am on the server"; } }
このシナリオで指定されている入力に基づいて、Web Serviceウィザードでは、Webサービスを実装するために次のファイルを生成します。
MyObjectWSは、Webサービスのリモートインタフェースです。ウィザードでは、これに対して次のソースコードを生成します。
package com.exsamp.obj; import java.rmi.Remote; import java.rmi.RemoteException; public interface MyObjectWS extends Remote { public java.lang.String getString( ) throws RemoteException; public boolean setString( java.lang.String arg0 ) throws RemoteException; public java.lang.String sayHello( ) throws RemoteException; }
MyObjectWS_ServiceSkeletonは、Webサービスへのアクセスを処理する抽象サーブレットクラスです。ウィザードでは、これに対して次のソースコードを生成します。
package com.exsamp.obj; import java.rmi.RemoteException; import java.util.Properties; import com.sssw.jbroker.web.encoding.TypeMappingRegistry; import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry; public abstract class MyObjectWS_ServiceSkeleton extends com.sssw.jbroker.web.portable.ServletSkeleton implements MyObjectWS { private static final com.sssw.jbroker.web.QName _portType = new com.sssw.jbroker.web.QName("urn:com.exsamp.obj.MyObject", "MyObjectWS"); public MyObjectWS_ServiceSkeleton() { super(_portType); _setProperty("xmlrpc.schema.uri", "http://www.w3.org/2001/XMLSchema"); _setProperty("version", "1.1"); } private static java.util.Dictionary _atable = new java.util.Hashtable(); static { _atable.put("\"urn:com.exsamp.obj.MyObject/setString\"", new java.lang.Integer(0)); _atable.put("\"urn:com.exsamp.obj.MyObject/getString\"", new java.lang.Integer(1)); _atable.put("\"urn:com.exsamp.obj.MyObject/sayHello\"", new java.lang.Integer(2)); } private static java.util.Dictionary _mtable = new java.util.Hashtable(); static { _mtable.put("setString", new java.lang.Integer(0)); _mtable.put("getString", new java.lang.Integer(1)); _mtable.put("sayHello", new java.lang.Integer(2)); } public com.sssw.jbroker.web.portable.ServerResponse _invoke(com.sssw.jbroker.web.portable.ServerRequest in) throws java.io.IOException { com.sssw.jbroker.web.portable.ServerResponse out = null; String soapEncURI = "soap"; String literalURI = "literal"; try { java.lang.Integer _m = null; String sac = in.getAction(); if (sac != null) _m = (java.lang.Integer) _atable.get(sac); if (_m == null) { sac = "\"" + sac + "\""; _m = (java.lang.Integer) _atable.get(sac); } if (_m == null) { String methodName = in.getMethod(); if (methodName != null) _m = (java.lang.Integer) _mtable.get(methodName); } if (_m == null) throw new com.sssw.jbroker.web.ServiceException("unable to dispatch SOAP request"); switch(_m.intValue()) { // setString case 0: { in.setEncodingStyleURI(soapEncURI); java.lang.String _arg0 = null; try { _arg0 = (java.lang.String) in.readObject(java.lang.String.class, "arg0"); } catch (java.io.EOFException eofExc) { _arg0 = null; } boolean result = setString(_arg0); //create reply out = in.createReply(); //set the content type java.lang.Object arg = null; arg = new java.lang.Boolean(result); out.writeObject(arg, "result"); break; } // getString case 1: { in.setEncodingStyleURI(soapEncURI); java.lang.String result = getString(); //create reply out = in.createReply(); //set the content type java.lang.Object arg = null; arg = result; out.writeObject(arg, "result"); break; } // sayHello case 2: { in.setEncodingStyleURI(soapEncURI); java.lang.String result = sayHello(); //create reply out = in.createReply(); //set the content type java.lang.Object arg = null; arg = result; out.writeObject(arg, "result"); break; } } } catch (java.lang.Throwable ex) { if (System.getProperty("SOAP_DEBUG") != null) ex.printStackTrace(); out = in.createExceptionReply(); out.writeException(ex, "exception"); } return out; } public boolean isDocument(String action) { return false; } private static Properties _rootHeaders = new Properties(); static { _rootHeaders.setProperty("content-type", "text/xml; charset=UTF-8"); _rootHeaders.setProperty("content-id", "<soapbody>"); } }
MyObjectWS_ServiceTieSkeletonは、結合モデルをサポートするためにMyObjectWS_ServiceSkeletonを拡張する抽象クラスです。ウィザードでは、これに対して次のソースコードを生成します。
package com.exsamp.obj; import java.rmi.RemoteException; import java.util.Properties; import com.sssw.jbroker.web.encoding.TypeMappingRegistry; import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry; public abstract class MyObjectWS_ServiceTieSkeleton extends com.exsamp.obj.MyObjectWS_ServiceSkeleton implements com.sssw.jbroker.web.portable.TieSkeleton { private MyObjectWS _target; public void setTarget(java.rmi.Remote target) { _target = (MyObjectWS) target; } public java.rmi.Remote getTarget() { return _target; } public boolean setString(java.lang.String _arg0) throws java.rmi.RemoteException { return _target.setString(_arg0); } public java.lang.String getString( ) throws java.rmi.RemoteException { return _target.getString(); } public java.lang.String sayHello( ) throws java.rmi.RemoteException { return _target.sayHello(); } }
MyObjectWSTieは、Webサービスのフロントエンドとして機能するように、抽象サーブレットクラスを拡張します。受信するリクエスト(メソッド呼び出し)を処理するために、このサーブレットは、MyObjectWSDelegateをインスタンス化して委任します。ウィザードでは、これに対して次のソースコードを生成します。
package com.exsamp.obj; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class MyObjectWSTie extends MyObjectWS_ServiceTieSkeleton { public void init() throws ServletException { try { super.init(); // The following are all public constructors for the implemented service // class. IMPORTANT NOTE: If available, the empty constructor has been // implemented by default. If no implicit or explicit empty constructor // is available, you *must* select one from the list below and uncomment // it in order to construct the generated service implementation. //super.setTarget( new MyObjectWSDelegate( java.lang.String arg0) ); //super.setTarget( new MyObjectWSDelegate( java.lang.String arg0, java.lang.String arg1) ); super.setTarget( new MyObjectWSDelegate( ) ); } catch (Exception _e) { throw new ServletException(_e); } } // The following method may be freely modified to provide custom behavior // when an HTTP GET request is made. Comment-out or remove this method to // provide default SOAP doGet functionality. public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { StringBuffer sb = new StringBuffer(1024); OutputStream out = null; InputStream in = null; String path = "/MyObjectWS.wsdl"; try { // Try to load the WSDL file. in = getServletConfig().getServletContext().getResourceAsStream(path); if (in == null) { // If it can\qt be found, return a default message. sendDefaultMsg(response); } else { // Try to determine the WSDL file\qs character encoding for content-type. byte[] buf = new byte[512]; int read = in.read(buf); if (read <= 0) sendDefaultMsg(response); String cs = getXMLEncoding(buf); StringBuffer ct = new StringBuffer(64); ct.append("text/xml"); if (cs != null) { ct.append("; charset="); ct.append(cs); } // Return the WSDL file. response.setContentType(ct.toString()); out = response.getOutputStream(); do { out.write(buf, 0, read); } while ((read = in.read(buf)) >= 0); } } catch (Exception _e) { throw new ServletException("Exception trying to return " + path, _e); } finally { if (out != null) out.close(); if (in != null) in.close(); } } catch (Exception _e) { throw new ServletException(_e); } } // Try to determine the character encoding of this XML document. public static String getXMLEncoding(byte[] bytes) { String lsLine = ""; String lsEncoding = "UTF-8"; if (bytes.length >=2 && bytes[0]==0xFE && bytes[1]==0xFF) return "UTF-16"; String lsState = ""; int liDeclStart = 0; int liDeclLength = 0; for (int i=0; i < bytes.length; i++) { if (lsState.equals("") && bytes[i] == \q<\q && bytes[i+1] == \q?\q) { lsState = "<?"; } else { if (lsState.equals("<?") && bytes[i] == \qx\q && bytes[i+1] == \qm\q && bytes[i+2] == \ql\q && bytes[i+3] == \q \q) { liDeclStart = i; lsState = "xml"; } else { if (lsState.equals("xml") && bytes[i] == \q?\q && bytes[i+1] == \q>\q) { liDeclLength = i - liDeclStart; break; } } } } lsLine = new String(bytes, liDeclStart, liDeclLength); int liPos = lsLine.indexOf("encoding"); if (liPos > 0) { lsLine = lsLine.substring(liPos + 8); int liEncStart = lsLine.indexOf(\q"\q); int liEncEnd = lsLine.indexOf(\q"\q, liEncStart +1); if (liEncStart < 0 && liEncEnd < 0) { liEncStart = lsLine.indexOf("\q"); liEncEnd = lsLine.indexOf("\q", liEncStart +1 ); } if (liEncStart >= 0 && liEncEnd >= 0) lsEncoding = lsLine.substring(liEncStart + 1, liEncEnd); } return lsEncoding; } static private final String DEFAULT_MESSAGE = "<html><head><title>Novell exteNd Web Service</title>" + "</head><body><h3 align=\"center\">Novell exteNd Web Service</h3>" + "By default, SOAP servers do not communicate via HTTP GET requests. The Novell " + "exteNd Web Service Wizard has generated an overloaded version of the " + "<i>doGet()</i> method for your convience. This method, found in your " + "generated _TIE code, is producing this message. If the WSDL file for this Web Service " + "is available in the root of your Web Service WAR, this method will return the WSDL instead " + "of this default message. You may add any custom code you like in your generated _TIE\qs " + "<i>doGet()</i> method to handle HTTP GET support.</body></html>"; private void sendDefaultMsg(HttpServletResponse response) throws IOException { PrintWriter out = null; try { response.setContentType("text/html"); response.setContentLength(DEFAULT_MESSAGE.length()); out = response.getWriter(); out.print(DEFAULT_MESSAGE); } finally { if (out != null) out.close(); } } }
MyObjectWSDelegateは、導入クラス(MyObject)をインスタンス化し、要求されたメソッド呼び出しをこのインスタンスに対して実行します。ウィザードでは、これに対して次のソースコードを生成します。
package com.exsamp.obj; import java.rmi.Remote; import java.rmi.RemoteException; public class MyObjectWSDelegate implements MyObjectWS { private MyObject m_objMyObject; public MyObjectWSDelegate( java.lang.String arg0 ) { m_objMyObject = new MyObject( arg0 ); } public MyObjectWSDelegate( java.lang.String arg0, java.lang.String arg1 ) { m_objMyObject = new MyObject( arg0, arg1 ); } public MyObjectWSDelegate( ) { m_objMyObject = new MyObject( ); } public java.lang.String getString( ) throws RemoteException { return m_objMyObject.getString( ); } public boolean setString( java.lang.String arg0 ) throws RemoteException { return m_objMyObject.setString( arg0 ); } public java.lang.String sayHello( ) throws RemoteException { return m_objMyObject.sayHello( ); } }
生成されたこのファイルは、(レジストリに発行する場合に便利な)標準のWSDL形式でWebサービスを説明します。
<?xml version="1.0" encoding="UTF-8"?> <definitions name="MyObjectWSService" targetNamespace="urn:com.exsamp.obj.MyObject" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="urn:com.exsamp.obj.MyObject" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <types/> <message name="setStringRequest"> <part name="arg0" type="xsd:string"/> </message> <message name="setStringResponse"> <part name="result" type="xsd:boolean"/> </message> <message name="getStringRequest"/> <message name="getStringResponse"> <part name="result" type="xsd:string"/> </message> <message name="sayHelloRequest"/> <message name="sayHelloResponse"> <part name="result" type="xsd:string"/> </message> <portType name="MyObjectWS"> <operation name="setString" parameterOrder="arg0"> <input message="tns:setStringRequest"/> <output message="tns:setStringResponse"/> </operation> <operation name="getString"> <input message="tns:getStringRequest"/> <output message="tns:getStringResponse"/> </operation> <operation name="sayHello"> <input message="tns:sayHelloRequest"/> <output message="tns:sayHelloResponse"/> </operation> </portType> <binding name="MyObjectWSBinding" type="tns:MyObjectWS"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="setString"> <soap:operation soapAction="urn:com.exsamp.obj.MyObject/setString"/> <input> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:com.exsamp.obj.MyObject" use="encoded"/> </input> <output> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:com.exsamp.obj.MyObject" use="encoded"/> </output> </operation> <operation name="getString"> <soap:operation soapAction="urn:com.exsamp.obj.MyObject/getString"/> <input> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:com.exsamp.obj.MyObject" use="encoded"/> </input> <output> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:com.exsamp.obj.MyObject" use="encoded"/> </output> </operation> <operation name="sayHello"> <soap:operation soapAction="urn:com.exsamp.obj.MyObject/sayHello"/> <input> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:com.exsamp.obj.MyObject" use="encoded"/> </input> <output> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:com.exsamp.obj.MyObject" use="encoded"/> </output> </operation> </binding> <service name="MyObjectWSService"> <port binding="tns:MyObjectWSBinding" name="MyObjectWSPort"> <soap:address location="http://localhost/WebServiceSampleDB/WebServiceSample/MyObject"/> </port> </service> </definitions>
このシナリオで指定されている入力に基づいて、Web Serviceウィザードでは、配備されたWebサービスをテストできるように次のファイルを生成します。
MyObjectWSServiceは、JAX-RPCで使用されるサービスインタフェースで、Webサービスのスタブをクライアントで取得できるようにします。ウィザードでは、これに対して次のソースコードを生成します。
package com.exsamp.obj; import javax.xml.rpc.ServiceException; public interface MyObjectWSService extends javax.xml.rpc.Service { public MyObjectWS_Stub getMyObjectWSPort() throws ServiceException; }
MyObjectWSServiceImplは、スタブ(MyObjectWS_Stub)のインスタンス化を処理するサービス実装クラスです。ウィザードでは、これに対して次のソースコードを生成します。
package com.exsamp.obj; import java.io.FileNotFoundException; import java.util.Iterator; import java.util.Hashtable; import java.util.Properties; import java.util.ArrayList; import java.net.URL; import java.net.MalformedURLException; import javax.xml.rpc.Call; import javax.xml.rpc.ParameterMode; import javax.xml.namespace.QName; import javax.xml.rpc.ServiceException; import com.sssw.jbroker.web.Binding; import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry; public class MyObjectWSServiceImpl extends com.sssw.jbroker.web.xml.rpc.ServiceImpl implements MyObjectWSService { public MyObjectWSServiceImpl() { try { createCalls(); } catch (ServiceException ex) { throw new javax.xml.rpc.JAXRPCException("failed to create the call objects: " + ex.getMessage()); } } public QName getServiceName() { return _serviceName; } public Iterator getPorts() { return _portMapping.keySet().iterator(); } public void setProxyMode(boolean proxy) { _proxy = proxy; } public boolean getProxyMode() { return _proxy; } public URL getWSDLDocumentLocation() { return null; } public java.rmi.Remote getPort(Class serviceDefInterface) throws ServiceException { if (serviceDefInterface == null) throw new ServiceException("No Service class specified."); if (!java.rmi.Remote.class.isAssignableFrom(serviceDefInterface)) throw new ServiceException("Class is not a valid Interface."); String stubName = (String) _intfMapping.get(serviceDefInterface); Binding binding = (Binding) _intfBinding.get(serviceDefInterface); if (stubName == null) return getPort(serviceDefInterface, binding, _classInfo, _typeMappingRegistry, null); else return getPort(stubName, binding, _typeMappingRegistry); } public java.rmi.Remote getPort(QName portName, Class serviceDefInterface) throws ServiceException { return getPort(portName, serviceDefInterface, getProxyMode()); } public java.rmi.Remote getPort(QName portName, Class serviceDefInterface, boolean proxy) throws ServiceException { if (((proxy==false) || (serviceDefInterface == null)) && (portName != null)) { String stubName = (String) _portMapping.get(portName); Binding binding = (Binding) _portBinding.get(portName); if (stubName == null) return getPort(null, serviceDefInterface); try { return getPort(stubName, binding, portName, _typeMappingRegistry); } catch (Exception ex) { return getPort(null, serviceDefInterface); } } else { if (serviceDefInterface == null) throw new ServiceException("No Service class specified."); if (!java.rmi.Remote.class.isAssignableFrom(serviceDefInterface)) throw new ServiceException("Class is not a valid Interface."); Binding binding = (Binding) _intfBinding.get(serviceDefInterface); String uri = (portName == null) ? null : portName.getNamespaceURI(); return getPort(serviceDefInterface, binding, _classInfo, _typeMappingRegistry, uri); } } public Call[] getCalls(QName portName) throws ServiceException { ArrayList callslist = (ArrayList) _calls.get(portName); if (callslist == null) return null; Call[] calls = new Call[callslist.size()]; return (Call[]) callslist.toArray(calls); } private void addCall(QName portName, Call call) { ArrayList callslist = (ArrayList) _calls.get(portName); if (callslist == null) { callslist = new ArrayList(); _calls.put(portName, callslist); } callslist.add(call); } public MyObjectWS_Stub getMyObjectWSPort() throws ServiceException { try { return (MyObjectWS_Stub) getPort(new QName( "urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), null, false); } catch (Exception ex) { return (MyObjectWS_Stub) getPort(com.exsamp.obj.MyObjectWS.class); } } private void createCalls() throws ServiceException { Call call = null; call = createCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), new QName("urn:com.exsamp.obj.MyObject", "setString")); call.addParameter("arg0", new QName("http://www.w3.org/2001/XMLSchema", "string"), java.lang.String.class, ParameterMode.IN); call.addParameter("result", new QName("http://www.w3.org/2001/XMLSchema", "boolean"), boolean.class, ParameterMode.OUT); call.setReturnType(new QName("http://www.w3.org/2001/XMLSchema", "boolean"), boolean.class); call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc"); call.setProperty(Call.SOAPACTION_URI_PROPERTY, "\"urn:com.exsamp.obj.MyObject/setString\""); call.setTargetEndpointAddress("http://localhost/WebServiceSampleDB/WebServiceSample/MyObject"); addCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), call); call = createCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), new QName("urn:com.exsamp.obj.MyObject", "getString")); call.addParameter("result", new QName("http://www.w3.org/2001/XMLSchema", "string"), java.lang.String.class, ParameterMode.OUT); call.setReturnType(new QName("http://www.w3.org/2001/XMLSchema", "string"), java.lang.String.class); call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc"); call.setProperty(Call.SOAPACTION_URI_PROPERTY, "\"urn:com.exsamp.obj.MyObject/getString\""); call.setTargetEndpointAddress("http://localhost/WebServiceSampleDB/WebServiceSample/MyObject"); addCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), call); call = createCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), new QName("urn:com.exsamp.obj.MyObject", "sayHello")); call.addParameter("result", new QName("http://www.w3.org/2001/XMLSchema", "string"), java.lang.String.class, ParameterMode.OUT); call.setReturnType(new QName("http://www.w3.org/2001/XMLSchema", "string"), java.lang.String.class); call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc"); call.setProperty(Call.SOAPACTION_URI_PROPERTY, "\"urn:com.exsamp.obj.MyObject/sayHello\""); call.setTargetEndpointAddress("http://localhost/WebServiceSampleDB/WebServiceSample/MyObject"); addCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), call); } static boolean _proxy = true; static final QName _serviceName; static final Hashtable _intfMapping = new Hashtable(); static final Hashtable _intfBinding = new Hashtable(); static final Hashtable _portBinding = new Hashtable(); static final Hashtable _portMapping = new Hashtable(); static final Hashtable _classInfo = new Hashtable(); private final Hashtable _calls = new Hashtable(); static { _serviceName = new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSService"); _intfBinding.put(MyObjectWS.class, new Binding("soap", "http://localhost/WebServiceSampleDB/WebServiceSample/MyObject")); _portBinding.put(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), new Binding("soap", "http://localhost/WebServiceSampleDB/WebServiceSample/MyObject")); _intfMapping.put(MyObjectWS.class, "com.exsamp.obj.MyObjectWS_Stub"); _portMapping.put(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), "com.exsamp.obj.MyObjectWS_Stub"); Hashtable _methodInfo; Hashtable _paramInfo; Properties _props; _methodInfo = new Hashtable(); _paramInfo = new Hashtable(); _props = new Properties(); _props.setProperty("jbroker.web.soap.action","\"urn:com.exsamp.obj.MyObject/setString\""); _paramInfo.put("Properties", _props); _props = new Properties(); _props.setProperty("jbroker.web.parameter.name", "arg0"); _props.setProperty("jbroker.web.parameter.inout", "1"); _paramInfo.put("Param0", _props); _props = new Properties(); _props.setProperty("jbroker.web.parameter.name", "result"); _props.setProperty("jbroker.web.parameter.inout", "2"); _paramInfo.put("Result", _props); _methodInfo.put("setString", _paramInfo); _paramInfo = new Hashtable(); _props = new Properties(); _props.setProperty("jbroker.web.soap.action","\"urn:com.exsamp.obj.MyObject/getString\""); _paramInfo.put("Properties", _props); _props = new Properties(); _props.setProperty("jbroker.web.parameter.name", "result"); _props.setProperty("jbroker.web.parameter.inout", "2"); _paramInfo.put("Result", _props); _methodInfo.put("getString", _paramInfo); _paramInfo = new Hashtable(); _props = new Properties(); _props.setProperty("jbroker.web.soap.action","\"urn:com.exsamp.obj.MyObject/sayHello\""); _paramInfo.put("Properties", _props); _props = new Properties(); _props.setProperty("jbroker.web.parameter.name", "result"); _props.setProperty("jbroker.web.parameter.inout", "2"); _paramInfo.put("Result", _props); _methodInfo.put("sayHello", _paramInfo); _classInfo.put("com.exsamp.obj.MyObjectWS", _methodInfo); } }
MyObjectWS_Stubは、Webサービスにアクセスするためのプロキシとしてクライアントで使用されます。このスタブクラスは、各メソッド呼び出しのロジスティクスを処理するリモートインタフェース(MyObjectWS)を実装します。ウィザードでは、これに対して次のソースコードを生成します。
package com.exsamp.obj; import java.util.Properties; import com.sssw.jbroker.web.core.Constants; import com.sssw.jbroker.web.encoding.TypeMappingRegistry; import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry; public class MyObjectWS_Stub extends com.sssw.jbroker.web.portable.Stub implements MyObjectWS { private static com.sssw.jbroker.web.QName _portType = new com.sssw.jbroker.web.QName("urn:com.exsamp.obj.MyObject", "MyObjectWS"); private static final com.sssw.jbroker.web.Binding[] _bindings = new com.sssw.jbroker.web.Binding[] { new com.sssw.jbroker.web.Binding("soap", "http://localhost/WebServiceSampleDB/WebServiceSample/MyObject"), }; public MyObjectWS_Stub() { this(null); } public MyObjectWS_Stub(DefaultTypeMappingRegistry tmr) { super(_portType, _bindings); _setProperty("xmlrpc.schema.uri", (Object) "http://www.w3.org/2001/XMLSchema".intern()); _setProperty("version", (Object) "1.1"); TypeMappingRegistry _tm = null; try { if (tmr != null) _tm = tmr; else { _tm = new DefaultTypeMappingRegistry(); } _setTypeMappingRegistry(_tm); } catch (Exception ex) { throw new javax.xml.rpc.JAXRPCException("failed to initialize type mapping registry: " + ex.getMessage()); } } public boolean setString(java.lang.String _arg0) throws java.rmi.RemoteException { com.sssw.jbroker.web.portable.ClientResponse in = null; try { // create an output stream _getDelegate().setProperty("xmlrpc.soap.operation.name", new com.sssw.jbroker.web.QName("urn:com.exsamp.obj.MyObject", "setString")); //create request com.sssw.jbroker.web.portable.ClientRequest out = _request("setString", true, "soap", false, "\"urn:com.exsamp.obj.MyObject/setString\""); _getDelegate().setProperty("soapAction", (Object) "\"urn:com.exsamp.obj.MyObject/setString\""); _getDelegate().setProperty(Constants.HTTP_CONTENT_TYPE, (Object) "text/xml; charset=utf-8"); out._setProperties(_getDelegate().getProperties()); Object arg = null; // marshal the parameters arg = _arg0; out.writeObject(arg, "arg0"); // do the invocation in = _invoke(out); // unmarshal the results // return java.lang.Boolean retWrapper = (java.lang.Boolean)in.readObject(boolean.class, "result"); boolean ret = retWrapper.booleanValue(); return ret; } catch (java.lang.Throwable t) { // map to remote exception throw com.sssw.jbroker.web.ServiceException.mapToRemote(t); } } public java.lang.String getString() throws java.rmi.RemoteException { com.sssw.jbroker.web.portable.ClientResponse in = null; try { // create an output stream _getDelegate().setProperty("xmlrpc.soap.operation.name", new com.sssw.jbroker.web.QName("urn:com.exsamp.obj.MyObject", "getString")); //create request com.sssw.jbroker.web.portable.ClientRequest out = _request("getString", true, "soap", false, "\"urn:com.exsamp.obj.MyObject/getString\""); _getDelegate().setProperty("soapAction", (Object) "\"urn:com.exsamp.obj.MyObject/getString\""); _getDelegate().setProperty(Constants.HTTP_CONTENT_TYPE, (Object) "text/xml; charset=utf-8"); out._setProperties(_getDelegate().getProperties()); Object arg = null; // do the invocation in = _invoke(out); // unmarshal the results // return java.lang.String ret = null; try { ret = (java.lang.String) in.readObject(java.lang.String.class, "result"); } catch (java.io.EOFException eofExc) { ret = null; } return ret; } catch (java.lang.Throwable t) { // map to remote exception throw com.sssw.jbroker.web.ServiceException.mapToRemote(t); } } public java.lang.String sayHello() throws java.rmi.RemoteException { com.sssw.jbroker.web.portable.ClientResponse in = null; try { // create an output stream _getDelegate().setProperty("xmlrpc.soap.operation.name", new com.sssw.jbroker.web.QName("urn:com.exsamp.obj.MyObject", "sayHello")); //create request com.sssw.jbroker.web.portable.ClientRequest out = _request("sayHello", true, "soap", false, "\"urn:com.exsamp.obj.MyObject/sayHello\""); _getDelegate().setProperty("soapAction", (Object) "\"urn:com.exsamp.obj.MyObject/sayHello\""); _getDelegate().setProperty(Constants.HTTP_CONTENT_TYPE, (Object) "text/xml; charset=utf-8"); out._setProperties(_getDelegate().getProperties()); Object arg = null; // do the invocation in = _invoke(out); // unmarshal the results // return java.lang.String ret = null; try { ret = (java.lang.String) in.readObject(java.lang.String.class, "result"); } catch (java.io.EOFException eofExc) { ret = null; } return ret; } catch (java.lang.Throwable t) { // map to remote exception throw com.sssw.jbroker.web.ServiceException.mapToRemote(t); } } private static Properties _rootHeaders = new Properties(); static { _rootHeaders.setProperty("content-type", "text/xml; charset=UTF-8"); _rootHeaders.setProperty("content-id", "<soapbody>"); } }
MyObjectWSClientは、次の方法でWebサービスにアクセスする単純なクライアントアプリケーションです。
package com.exsamp.obj; import javax.naming.*; public class MyObjectWSClient { public void process(String[] args) throws Exception { MyObjectWS remote = getRemote(args); // The following code has been generated for your testing convenience. ln // order to successfully test your Web Service, you must uncomment one or // more of these lines and supply meaningful arguments where necessary. // Once you have modified the test method(s) below, compile this class and // execute it from a command line with your class path set appropriately. // System.out.println("Test Result = " + remote.getString()); // System.out.println("Test Result = " + remote.setString(java.lang.String)); // System.out.println("Test Result = " + remote.sayHello()); } public MyObjectWS getRemote(String[] args) throws Exception { InitialContext ctx = new InitialContext(); String lookup = "xmlrpc:soap:com.exsamp.obj.MyObjectWSService"; MyObjectWSService service = (MyObjectWSService)ctx.lookup(lookup); MyObjectWS remote = (MyObjectWS)service.getMyObjectWSPort(); return remote; } public static void main(String[] args) { try { MyObjectWSClient client = new MyObjectWSClient(); client.process(args); } catch (Exception _e) { System.out.println("*** Error Executing Generated Test Client ***"); _e.printStackTrace(); } } }
必要な変更 生成されたMyObjectWSClient.javaファイルのprocess()メソッドは、テストするWebサービスメソッド呼び出しのコメントを解除するように編集する必要があります。変更内容は次のとおりです。
// System.out.println("Test Result = " + remote.getString());
// System.out.println("Test Result = " + remote.setString(java.lang.String));
System.out.println("Test Result = " + remote.sayHello());
このシナリオでは結合モデルが使用されるため、Web Serviceウィザードによってweb.xmlファイルが自動的に更新され、MyObject Webサービスのリクエストを処理するサーブレットクラスとしてMyObjectWSTieが宣言されます。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <servlet> <servlet-name>MyObject</servlet-name> <servlet-class>com.exsamp.obj.MyObjectWSTie</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyObject</servlet-name> <url-pattern>MyObject</url-pattern> </servlet-mapping> </web-app>
このプロジェクトを作成した後、WARファイルが作成されてJ2EEサーバに配備されると、MyObject Webサービスのテストを実行する準備が整ったことになります。MyObjectWSClientアプリケーションを実行するためにWorkbenchでClient Runnerを使用した場合の結果は、次のとおりです。
![]() ![]() ![]() ![]() ![]() |
開発ガイド 05/16/03 09:21:28 |
Copyright © 1997, 1998, 1999, 2000, 2001, 2002, 2003 SilverStream Software, LLC, a wholly owned subsidiary of Novell, Inc. All rights reserved.