監(jiān)理公司管理系統(tǒng) | 工程企業(yè)管理系統(tǒng) | OA系統(tǒng) | ERP系統(tǒng) | 造價咨詢管理系統(tǒng) | 工程設(shè)計管理系統(tǒng) | 甲方項目管理系統(tǒng) | 簽約案例 | 客戶案例 | 在線試用
X 關(guān)閉

技巧:通過Web Service讓Delphi/Visual Basic程序訪問EJB

申請免費試用、咨詢電話:400-8352-114

AMTeam.org

技巧:通過Web Service讓Delphi/Visual Basic程序訪問EJB    

 
 
陳凱琪 (
chenkq@sina.com)

2002 年 5 月

問題的提出

EJB是使用Java語言編寫分布式的、面向?qū)ο蟮?、商業(yè)應用的標準組件架構(gòu)(參見EJB 1.1規(guī)范)。一些企業(yè)正在將他們以前的基于COM+組件的應用移植到EJB組件技術(shù),或者采用EJB構(gòu)建新的應用,同時為保護以前的IT投入還要保留以前的采用Delphi或者Visual Basic等編程語言編寫的客戶端應用。這就提出了Delphi/Visual Basic訪問EJB的需求。

本篇文章將首先介紹Delphi/Visual Basic訪問EJB的兩種方法,然后介紹Delphi/Visual Basic如何通過Web Service訪問EJB:

將EJB封裝為Web Service并發(fā)布到應用服務(wù)器上

Delphi訪問Web Service

Visual Basic訪問Web Service

最后文章中討論了在Delphi和Visual Basic中調(diào)用Web service時遇到的中文問題。

Delphi/Visual Basic訪問EJB的兩種方法

Delphi通過CORBA ORB訪問EJB

Delphi企業(yè)版中含有CORBA Visibroker server,可以在Borland App Server服務(wù)器端將EJB封裝為CORBA服務(wù),然后在客戶端用SIDL (Simple IDL)的來描述封裝EJB的CORBA服務(wù),最后用Delphi工具idl2pas將描述文件翻譯成pascal程序。這個方案在部署的時候需要在客戶端安裝Visibroker server。

Visual Basic通過ActiveX to EJB Bridge訪問EJB

IBM WebSphere Application Server Enterprise Edition 4.x中的Enterprise Service當中包含ActiveX to EJB bridge,它為VB、VBScript以及ASP提供了一個ActiveX組件來方便地訪問EJB。

客戶端程序首先初始化一個ActiveX控件,該控件初始化一個Java虛擬機,ActiveX控件和Java虛擬機通過JNI(Java Native Interface)的方式交互。Java虛擬機通過Java ORB和運行在應用服務(wù)器上的EJB進行交互。Active Bridge支持使用J2EE Client Container或者直接通過JNDI的方式訪問EJB。

圖一:ActiveX to EJB Bridge系統(tǒng)結(jié)構(gòu)


通過Web Service讓Delphi/Visual Basic訪問EJB

采用Web Service的方式封裝EJB,我們理論上可以讓支持Web Service的任何編程語言來訪問該EJB,比如Java, Visual Basic, Visual C++,Delphi,perl,PHP等。

將EJB封裝為web service的工具有很多種,這里我們以開放源碼的Apache SOAP為例來說明這個開發(fā)流程,開發(fā)工具采用WSAD(IBM WebSphere Studio Application Developer)。這是一個以Java語言編寫的,可以運行在Windows和Linux平臺上的J2EE開發(fā)工具,支持servlet、jsp、ejb、web service和xml的開發(fā)和調(diào)試,可以在http://www-3.ibm.com/software/ad/studioappdev/免費下載試用版。我們將以一個程序員都非常熟悉的例子-HelloWorld來展示開發(fā)流程。下面,是我們動手開始做的時間了!

第一步,編寫一個EJB,名字叫做HelloWorld。

1)建立一個EJB項目(EJB project):在J2EE perspective選擇菜單File > New > EJB Project,輸入Project Name為HelloWorldEJB,Enterprise Application project name為HelloWorldEAR,選擇Finish建立該EJB項目,如圖二所示:

圖二:建立EJB項目


2)建立EJB:在J2EE perspective中的J2EE View中選擇EJB Modules > HelloWorldEJB,然后選擇菜單File > New > Enterprise Bean,輸入Bean Name為HelloWorld,Bean class為ejbs.HelloWorld(這里使用package名為ejbs)。選擇Finish建立該EJB。如圖三所示:

圖三 建立EJB


3)給HelloWorldBean添加一個方法:在J2EE perspective中的J2EE View中選擇EJB Modules > HelloWorldEJB > HelloWorld > HelloWorldBean,鼠標雙擊打開并編輯該EJB。在該EJB后面加入方法sayHelloWorld
 /** sayHello
  * @return "Hello someone"
   * @param theName the name of someone
  */
 public String sayHello(String theName) {
  return "Hello " + theName;
 }

圖四 給HelloWorldBean增加一個方法sayHello


4)將新添加的方法sayHello加入到EJB remote interface中:鼠標選中WSAD左下角處Java的Outline窗口中的方法sayHelloWorld,選擇鼠標右鍵菜單Enterprise Bean > Promote to Remote Interface。WSAD將在HelloWorld EJB的remote Interface HelloWorld中加入一行: public String sayHello(String theName) throws java.rmi.RemoteException;

遠程接口HelloWorld.java的完整的代碼:
package ejbs;
/**
 * Remote interface for Enterprise Bean: HelloWorld
 */
public interface HelloWorld extends javax.ejb.EJBObject {
 /** sayHello
  * @return "Hello someone"
   * @param theName the name of someone
  */
 public String sayHello(String theName) throws java.rmi.RemoteException;
}

圖五 將新添加的方法sayHello加入到EJB remote interface中


5)讓WSAD生成EJB的部署代碼(Deploy code)。如圖六操作即可。

圖六 生成EJB的部署代碼


6)現(xiàn)在我們可以部署并測試HelloWorld EJB了! 如圖七所示,運行HelloWorld EJB。

圖七 選擇在服務(wù)器上運行EJB


我們將看到WSAD將這個EJB項目發(fā)布到一個WebSphere v4.0 Test Environment中,啟動該WebSphere測試環(huán)境。在控制臺中可以看到
02.04.14 11:56:59:171 CST] 55e67dfe EJBEngine     I WSVR0037I:正在啟動 EJB jar:HelloWorldEJB
[02.04.14 11:56:59:661 CST] 55e67dfe EJBEngine     I WSVR0038I:未找到 HelloWorld 的 JNDI 名,綁定起始對象名為 HelloWorldHome

[02.04.14 11:57:05:830 CST] 55e67dfe Server        A WSVR0023I:服務(wù)器 Default Server 為電子商務(wù)開放

最后WSAD將打開一個EJB Test Client窗口。選擇EJB References > HelloWorld > HelloWorldHome > HelloWorld create()方法,EJB Test Client獲得一個HelloWorld EJB Home Stub。如圖八所示:

圖八 使用EJB Test Client測試EJB(1)


7)調(diào)用HelloWorld EJB Home Stub對象的方法sayHello測試EJB。

圖九 使用EJB Test Client測試EJB(2)


第二步,使用WSAD中的 Web Service生成向?qū)砂b該EJB的Web Service、測試該Web Service的java proxy(一個Java Bean)和JSP測試程序,最后運行生成的程序來測試這個Web Service。

1)建立一個名叫HelloWorldWeb的Web Project:選擇菜單File > New > Web Project,輸入project name為HelloWorldWeb,選擇Enterprise Application project name為HelloWorldEAR,如圖十所示。選擇next命令按鈕,選中available dependent JARs HelloWorldEJB.jar,如圖十一所示。

圖十                                                                                                                        圖十一



 選擇HelloWorldWeb > source,選擇菜單File > New > Web Service,如圖十二所示:

圖十二


在"Web Service"窗口中選擇部署該Web Service的Web project為HelloWorldWeb,如圖十三所示:

圖十三


選擇Next進入下一個向?qū)Т翱?,選擇Web service type為EJB Web service。

選擇Next進入下一個向?qū)Т翱?,選擇命令按鈕"Browse EJB Bean…",在彈出窗口中選擇EAR Project為HelloWorldEAR,EJB bean為HelloWorld(如圖十四所示)

圖十四


關(guān)閉彈出窗口,輸入EJB JNDI name 為HelloWorldHome,如圖十五所示。

圖十五


選擇Next進入下一個向?qū)Т翱?,向?qū)⒁筝斎隬eb service URI的名稱和多個描述文件的文件名,如圖十六、十七所示。Web service URI是符合uniform resource Naming convention標準(http://www.ietf.org/rfc/rfc2141.txt)web service的標識符,應用程序訪問Web service的時候需要指定這個參數(shù)。

ISD文件是Apache SOAP內(nèi)部使用的web service的發(fā)布描述文件。/wsdl/HelloWorld-service.wsdl文件描述了相關(guān)的web service port信息,應用程序可以通過該文件獲得服務(wù)器上各種web service的信息。

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="HelloWorldService"
 targetNamespace="
http://localhost:8080/HelloWorldWeb/wsdl/HelloWorld-service.wsdl"
 xmlns="
http://schemas.xmlsoap.org/wsdl/"
 xmlns:tns="
http://localhost:8080/HelloWorldWeb/wsdl/HelloWorld-service.wsdl"
 xmlns:binding="
http://www.helloworld.com/definitions/HelloWorldRemoteInterface"
 xmlns:soap="
http://schemas.xmlsoap.org/wsdl/soap/">
  <import namespace="
http://www.helloworld.com/definitions/HelloWorldRemoteInterface"
  location="
http://localhost:8080/HelloWorldWeb/wsdl/HelloWorld-binding.wsdl"/>
  <service name="HelloWorldService">
    <port name="HelloWorldPort" binding="binding:HelloWorldBinding">
      <soap:address location="
http://localhost:8080/HelloWorldWeb/servlet/rpcrouter"/>
    </port>
  </service>
</definitions>
 /wsdl/HelloWorld-bind.wsdl則具體定義了具體一個portType的操作和消息的消息格式和協(xié)議。
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="HelloWorldRemoteInterface"
 targetNamespace="
http://www.helloworld.com/definitions/HelloWorldRemoteInterface"
 xmlns="
http://schemas.xmlsoap.org/wsdl/"
 xmlns:tns="
http://www.helloworld.com/definitions/HelloWorldRemoteInterface"
 xmlns:xsd="
http://www.w3.org/2001/XMLSchema"
 xmlns:soap="
http://schemas.xmlsoap.org/wsdl/soap/">
  <message name="sayHelloRequest">
    <part name="theName" type="xsd:string"/>
  </message>
  <message name="sayHelloResponse">
    <part name="result" type="xsd:string"/>
  </message>
  <portType name="HelloWorldJavaPortType">
    <operation name="sayHello">
      <input name="sayHelloRequest" message="tns:sayHelloRequest"/>
      <output name="sayHelloResponse" message="tns:sayHelloResponse"/>
    </operation>
  </portType>
  <binding name="HelloWorldBinding" type="tns:HelloWorldJavaPortType">
    <soap:binding style="rpc" transport="
http://schemas.xmlsoap.org/soap/http"/>
    <operation name="sayHello">
      <soap:operation soapAction="" style="rpc"/>
      <input name="sayHelloRequest">
        <soap:body use="encoded" encodingStyle="
http://schemas.xmlsoap.org/soap/encoding/" namespace="http://tempuri.org/ejbs.HelloWorld"/>
      </input>
      <output name="sayHelloResponse">
        <soap:body use="encoded" encodingStyle="
http://schemas.xmlsoap.org/soap/encoding/" namespace="http://tempuri.org/ejbs.HelloWorld"/>
      </output>
    </operation>
  </binding>
</definitions>

圖十六

 

圖十七


3)WSAD可以生成訪問剛才發(fā)布的Web service的Java類,如圖十八、十九所示。該類的方法 public synchronized java.lang.String sayHello(java.lang.String theName) throws Exception包裝了發(fā)布為Web service的EJB HelloWorld的方法sayHello(java.lang.String theName)。在發(fā)布EJB為Web service的向?qū)е锌梢陨蛇@個Java類(web service binding proxy),同時還可以生成使用這個類訪問web service的jsp頁面。

圖十八

 

圖十九


最后,你可以選擇將Web Service發(fā)布為到UDDI服務(wù)器(比如免費的IBM的UDDI4J)上。

第三步,在Delphi中調(diào)用Web Service

使用Delphi Web Services importer將定義Web Service的WSDL文件(可以是從Web服務(wù)器上下載下來的本地wsdl文件,也可以是一個指向該文件的HTTP URL,比如http://localhost:8080/HelloWorldWeb/wsdl/HelloWorld-service.wsdl)導入,這將自動生成定義和注冊interfaces and types的pascal代碼。

選擇菜單File > New > Other… > 出現(xiàn)New Iterms窗口,選擇WebServices,選擇Web Services importer。如圖二十、二十一所示:

圖二十

 

圖二十一


生成的代碼如下:
Unit Unit_2;
interface
uses Types, XSBuiltIns;
type

HelloWorldJavaPortType = interface(IInvokable)
    ['{521A5B71-4CB9-4FAA-82AD-0F9CCF423FB9}']
//為方便使用,我們把Delphi聲明的過程 sayHello改為方法
    //procedure sayHello(const theName: WideString; out result: WideString);  stdcall;
function sayHello(const theName: WideString): WideString;  stdcall;
end;
implementation
uses InvokeRegistry;
initialization
  InvRegistry.RegisterInterface(TypeInfo(HelloWorldJavaPortType), '', 'UTF-8');
end.

現(xiàn)在,我們可以利用THTTPRio對象來調(diào)用Web Service。THTTPRio是Delphi提供的支持SOAP over HTTP的有源代碼的SOAP支持類庫。部分代碼如下:

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, SoapHTTPClient, Unit2, StdCtrls;

procedure TForm_main.Button_callClick(Sender: TObject);
var
        //使用Delphi的Unit SoapHTTPClient,Delphi
        X :THTTPRio;
        //使用Unit_2中定義的HelloWorld Interface
        InterfaceVariable: HelloWorld;
        para1, para2: WideString;
begin
        X := THTTPRio.Create(nil);
        //設(shè)置Delphi在提出HTTP請求的時候設(shè)置Content-Type為text/xml; charset= UTF-8
       X.HTTPWebNode.UseUTF8InHeader := true ;

        //WSDLLocation也可以是在本地文件系統(tǒng)中的wsdl文件。
        X.WSDLLocation := 'http://localhost:8080/HelloWorldWeb/wsdl/HelloWorld-service.wsdl';
       
// THTTPRio 的Service參數(shù)和Port參數(shù)對應HelloWorld-service.wsdl文件中的定義。
// <service name="HelloWorldService">
//    <port name="HelloWorldPort" binding="binding:HelloWorldBinding">
  //      <soap:address location="
http://localhost:8080/HelloWorldWeb/servlet/rpcrouter"/>
   //    </port>
  //</service>
        X.Service := 'HelloWorldService';
        X.Port := 'HelloWorldPort';

        InterfaceVariable := X as HelloWorld;
        para1 := '中文';
        //從輸入文本框中獲得輸入?yún)?shù)
        para1 := Edit_para1.Text;
        para2 := InterfaceVariable.sayHello(para1);
        //顯示調(diào)用web service返回的結(jié)果
        Label_return_value.Caption := 'Return Value: ' + para2;
        X.free;
end;

使用Microsoft SOAP Toolkit 編寫SOAP客戶端程序

使用Microsoft SOAP Toolkit - High Level API編寫SOAP客戶端程序非常簡單,只需要四條語句就可以完成基本的SOAP請求。使用SOAP Toolkit之前,需要在項目增加Microsoft XML v3.0和Microsoft SOAP Type Library的引用(reference)。

如下是訪問HelloWorld web service的代碼:

'定義Client為可以發(fā)送SOAP請求到服務(wù)器并能處理服務(wù)器返回的應答的SoapClient Object

Private Client As SoapClient

Set Client = New SoapClient

'使用WSDL文件作為輸入文件初始化SoapClient

Client.mssoapinit "
http://localhost:8080/HelloWorldWeb/wsdl/HelloWorld-service.wsdl"

'調(diào)用sayHello方法

txtEquals.Text = CStr(Client.sayHello(txtA.Text)

這段使用High Level API的代碼在運行的時候,MS SOAP Toolkit將報錯:

WSDLReader: No valid schema specification was found. This version of the SOAP Toolkit only supports 1999 and 2000 XSD schema specifications

 錯誤的原因是使用High Level API,Microsoft SOAP Toolkit提交的XML格式的SOAP請求和Apache SOAP要求的格式不同。文章http://www-1.ibm.com/support/techdocs/atsmastr.nsf/PubAllNum/TD100540詳細地介紹了格式的具體不同。

使用Low Level API編寫SOAP客戶端程序需要使用程序員使用SoapConnector的一個實現(xiàn)HttpConnector來建立HTTP連接,使用SoapSerializer object來建立一個SOAP message并發(fā)出SOAP請求;SoapReader從HttpConnector對象中獲得返回結(jié)果,生成一個XML DOM對象SoapReader.DOM,使用Microsoft XML API分析SoapReader.DOM獲得返回結(jié)果。

文章http://www.soapuser.com/client4.html非常清楚地介紹了如何使用Low Level API編寫SOAP客戶端程序。訪問HelloWorld EJB Web Service的demo代碼請在此處下載。

Delphi Web Service中文問題

通過監(jiān)控SOAP調(diào)用的全過程我們發(fā)現(xiàn)Delphi向Web Server提出HTTP請求的時候,數(shù)據(jù)是以UTF-8編碼通過POST方式提交給Web服務(wù)器的,但是另一方面,Delphi在HTTP請求頭中設(shè)置Content-Type為text/xml,造成Apache SOAP Toolkit認為POST方式的數(shù)據(jù)是iso8859-1編碼,出現(xiàn)了所謂中文問題。

我們可以設(shè)置Delphi在提出HTTP請求的時候設(shè)置Content-Type為text/xml; charset= UTF-8。具體做法是在創(chuàng)建了 THTTPRio對象之后設(shè)置它的屬性UseUTF8InHeader

X.HTTPWebNode.UseUTF8InHeader := true ;

需要注意SoapHTTPTrans.pas代碼 header := 'text/xml charset=UTF-8'; 當中少了一個分號";",這造成服務(wù)器認為soap 客戶程序POST上來的數(shù)據(jù)是iso8859-1編碼。

在Delphi選擇Project > Add to Project,加入Delphi6SourceSoapSoapHTTPTrans.pas。修改該行代碼為:header := 'text/xml; charset=UTF-8';

微軟SOAP Toolkit 2.0 with SP2的中文問題

微軟SOAP Toolkit 2.0 with SP2的中文問題的原因和Delphi一樣。

HttpConnector有一個在SOAP Toolkit 文檔中沒有標明的屬性HTTPCharset,設(shè)置為utf-8即可。 Connector.Property("HTTPCharset") = "utf-8"

Web Service程序的調(diào)試

我們可以使用監(jiān)控程序來監(jiān)控Web Service的全過程,XML格式的HTTP訪問請求和返回。

微軟的SOAP Toolkit中帶的SOAP Toolkit監(jiān)視器MSSoapT、Apache soap toolkit中的工具、或者其他的網(wǎng)絡(luò)監(jiān)控工具(比如network sniffer)。

結(jié)束語:

Web Service為傳統(tǒng)的GUI程序提供了使用遠程過程調(diào)用(RPC)的一種很好的方式,但是各家廠商對于Web Service的實現(xiàn)還是稍有差異(正如EJB, CORBA),這就提出了不同實現(xiàn)的互相操作性的問題。此外,在我們國家的應用當中,還需要注意中文問題。

參考資料:

  • 下載本文中用到的樣本代碼
  • Apache SOAP

    http://xml.apache.org/soap/index.html
  • Apache Axis

    http://xml.apache.org/axis/
  • IBM WebSphere Application Server

    http://www-4.ibm.com/software/info/websphere/wstechnology.html
  • MS SOAP Toolkit download area
  • 一篇很好SOAP快速入門
  • Microsoft MSSOAP client to IBM HTTP/Apache server interoperability issue

作者簡介:

陳凱琪:對Java和Linux有濃厚的興趣,精通J2EE應用服務(wù)器方面的技術(shù),可以通過
chenkq@sina.com 與他聯(lián)系  

發(fā)布:2007-03-25 13:28    編輯:泛普軟件 · xiaona    [打印此頁]    [關(guān)閉]
相關(guān)文章:
石家莊OA系統(tǒng)
聯(lián)系方式

成都公司:成都市成華區(qū)建設(shè)南路160號1層9號

重慶公司:重慶市江北區(qū)紅旗河溝華創(chuàng)商務(wù)大廈18樓

咨詢:400-8352-114

加微信,免費獲取試用系統(tǒng)

QQ在線咨詢