有啊,
请看 http://www-900.ibm.com/developerWorks/cn/webservices/ws-ejbacess/index.shtml ,
其实使用起来非常简单,没下面说的那么麻烦,我现在设计的系统就是Delphi7+Axis3.0+JBuilder+SqlServer开发的,
没什么难,比用Corba相对简单,关键是便宜啊,哈哈
下面是摘录:
技巧: 通过Web Service让Delphi/Visual Basic程序访问EJB
内容:
Delphi/Visual Basic访问EJB的两种方法介绍
通过Web Service让Delphi/Visual Basic访问EJB
使用Microsoft SOAP Toolkit 编写SOAP客户端程序
结束语:
参考资料
关于作者
陈凯琪 (chenkq@sina.com)
2002 年 5 月
问题的提出
EJB是使用Java语言编写分布式的、面向对象的、商业应用的标准组件架构(参见EJB 1.1规范)。一些企业正在将他们以前的基于COM+组件的应用移植到EJB组件技术,或者采用EJB构建新的应用,同时为保护以前的IT投入还要保留以前的采用Delphi或者Visual Basic等编程语言编写的客户端应用。这就提出了Delphi/Visual Basic访问EJB的需求。
本篇文章将首先介绍Delphi/Visual Basic访问EJB的两种方法,然后介绍Delphi/Visual Basic如何通过Web Service访问EJB:
将EJB封装为Web Service并发布到应用服务器上
Delphi访问Web Service
Visual Basic访问Web Service
最后文章中讨论了在Delphi和Visual Basic中调用Web service时遇到的中文问题。
Delphi/Visual Basic访问EJB的两种方法
Delphi通过CORBA ORB访问EJB
Delphi企业版中含有CORBA Visibroker server,可以在Borland App Server服务器端将EJB封装为CORBA服务,然后在客户端用SIDL (Simple IDL)的来描述封装EJB的CORBA服务,最后用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和运行在应用服务器上的EJB进行交互。Active Bridge支持使用J2EE Client Container或者直接通过JNDI的方式访问EJB。
图一:ActiveX to EJB Bridge系统结构
通过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为例来说明这个开发流程,开发工具采用WSAD(IBM WebSphere Studio Application Developer)。这是一个以Java语言编写的,可以运行在Windows和Linux平台上的J2EE开发工具,支持servlet、jsp、ejb、web service和xml的开发和调试,可以在http://www-3.ibm.com/software/ad/studioappdev/免费下载试用版。我们将以一个程序员都非常熟悉的例子-HelloWorld来展示开发流程。下面,是我们动手开始做的时间了!
第一步,编写一个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 then
ame the name of someone
*/
public String sayHello(String then
ame) {
return "Hello " + then
ame;
}
图四 给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 then
ame) 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 then
ame the name of someone
*/
public String sayHello(String then
ame) throws java.rmi.RemoteException;
}
图五 将新添加的方法sayHello加入到EJB remote interface中
5)让WSAD生成EJB的部署代码(Deploy code)。如图六操作即可。
图六 生成EJB的部署代码
6)现在我们可以部署并测试HelloWorld EJB了! 如图七所示,运行HelloWorld EJB。
图七 选择在服务器上运行EJB
我们将看到WSAD将这个EJB项目发布到一个WebSphere v4.0 Test Environment中,启动该WebSphere测试环境。在控制台中可以看到
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:服务器 Default Server 为电子商务开放
最后WSAD将打开一个EJB Test Client窗口。选择EJB References > HelloWorld > HelloWorldHome > HelloWorld create()方法,EJB Test Client获得一个HelloWorld EJB Home Stub。如图八所示:
图八 使用EJB Test Client测试EJB(1)
7)调用HelloWorld EJB Home Stub对象的方法sayHello测试EJB。
图九 使用EJB Test Client测试EJB(2)
第二步,使用WSAD中的 Web Service生成向导生成包装该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进入下一个向导窗口,选择Web service type为EJB Web service。
选择Next进入下一个向导窗口,选择命令按钮"Browse EJB Bean…",在弹出窗口中选择EAR Project为HelloWorldEAR,EJB bean为HelloWorld(如图十四所示)
图十四
关闭弹出窗口,输入EJB JNDI name 为HelloWorldHome,如图十五所示。
图十五
选择Next进入下一个向导窗口,向导将要求输入Web service URI的名称和多个描述文件的文件名,如图十六、十七所示。Web service URI是符合uniform resource Naming convention标准(http://www.ietf.org/rfc/rfc2141.txt)web service的标识符,应用程序访问Web service的时候需要指定这个参数。
ISD文件是Apache SOAP内部使用的web service的发布描述文件。/wsdl/HelloWorld-service.wsdl文件描述了相关的web service port信息,应用程序可以通过该文件获得服务器上各种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的操作和消息的消息格式和协议。
<?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="then
ame" 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
peration 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可以生成访问刚才发布的Web service的Java类,如图十八、十九所示。该类的方法 public synchronized java.lang.String sayHello(java.lang.String then
ame) throws Exception包装了发布为Web service的EJB HelloWorld的方法sayHello(java.lang.String then
ame)。在发布EJB为Web service的向导中可以生成这个Java类(web service binding proxy),同时还可以生成使用这个类访问web service的jsp页面。
图十八
图十九
最后,你可以选择将Web Service发布为到UDDI服务器(比如免费的IBM的UDDI4J)上。
第三步,在Delphi中调用Web Service
使用Delphi Web Services importer将定义Web Service的WSDL文件(可以是从Web服务器上下载下来的本地wsdl文件,也可以是一个指向该文件的HTTP URL,比如http://localhost:8080/HelloWorldWeb/wsdl/HelloWorld-service.wsdl)导入,这将自动生成定义和注册interfaces and types的pascal代码。
选择菜单File > New > Other… > 出现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 then
ame: WideString;
out result: WideString);
stdcall;
function sayHello(const then
ame: WideString): WideString;
stdcall;
end;
implementation
uses InvokeRegistry;
initialization
InvRegistry.RegisterInterface(TypeInfo(HelloWorldJavaPortType), '', 'UTF-8');
end.
现在,我们可以利用THTTPRio对象来调用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);
//设置Delphi在提出HTTP请求的时候设置Content-Type为text/xml;
charset= UTF-8
X.HTTPWebNode.UseUTF8InHeader := true ;
//WSDLLocation也可以是在本地文件系统中的wsdl文件。
X.WSDLLocation := 'http://localhost:8080/HelloWorldWeb/wsdl/HelloWorld-service.wsdl';
// THTTPRio 的Service参数和Port参数对应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 := '中文';
//从输入文本框中获得输入参数
para1 := Edit_para1.Text;
para2 := InterfaceVariable.sayHello(para1);
//显示调用web service返回的结果
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为可以发送SOAP请求到服务器并能处理服务器返回的应答的SoapClient Object
Private Client As SoapClient
Set Client = New SoapClient
'使用WSDL文件作为输入文件初始化SoapClient
Client.mssoapinit "http://localhost:8080/HelloWorldWeb/wsdl/HelloWorld-service.wsdl"
'调用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的一个实现HttpConnector来建立HTTP连接,使用SoapSerializer object来建立一个SOAP message并发出SOAP请求;SoapReader从HttpConnector对象中获得返回结果,生成一个XMLdo
M对象SoapReader.DOM,使用Microsoft XML API分析SoapReader.DOM获得返回结果。
文章http://www.soapuser.com/client4.html非常清楚地介绍了如何使用Low Level API编写SOAP客户端程序。访问HelloWorld EJB Web Service的demo代码请在此处下载。
Delphi Web Service中文问题
通过监控SOAP调用的全过程我们发现Delphi向Web Server提出HTTP请求的时候,数据是以UTF-8编码通过POST方式提交给Web服务器的,但是另一方面,Delphi在HTTP请求头中设置Content-Type为text/xml,造成Apache SOAP Toolkit认为POST方式的数据是iso8859-1编码,出现了所谓中文问题。
我们可以设置Delphi在提出HTTP请求的时候设置Content-Type为text/xml;
charset= UTF-8。具体做法是在创建了 THTTPRio对象之后设置它的属性UseUTF8InHeader
X.HTTPWebNode.UseUTF8InHeader := true ;
需要注意SoapHTTPTrans.pas代码 header := 'text/xml charset=UTF-8';
当中少了一个分号";",这造成服务器认为soap 客户程序POST上来的数据是iso8859-1编码。
在Delphi选择Project > Add to Project,加入Delphi6/Source/Soap/SoapHTTPTrans.pas。修改该行代码为:header := 'text/xml;
charset=UTF-8';
微软SOAP Toolkit 2.0 with SP2的中文问题
微软SOAP Toolkit 2.0 with SP2的中文问题的原因和Delphi一样。
HttpConnector有一个在SOAP Toolkit 文档中没有标明的属性HTTPCharset,设置为utf-8即可。 Connector.Property("HTTPCharset") = "utf-8"
Web Service程序的调试
我们可以使用监控程序来监控Web Service的全过程,XML格式的HTTP访问请求和返回。
微软的SOAP Toolkit中带的SOAP Toolkit监视器MSSoapT、Apache soap toolkit中的工具、或者其他的网络监控工具(比如network sniffer)。
结束语:
Web Service为传统的GUI程序提供了使用远程过程调用(RPC)的一种很好的方式,但是各家厂商对于Web Service的实现还是稍有差异(正如EJB, CORBA),这就提出了不同实现的互相操作性的问题。此外,在我们国家的应用当中,还需要注意中文问题。