2002 年 3 月 03 日 本文作者通過一次使用SUN的 tnameserv命名服務程序,服務器用JAVA編寫,客戶機分別用JAVA和C++(VC6+omniORB)編寫的試驗,希望通過一次編程的具體操作實例來體驗或明了CORBA思想。 前言 現(xiàn)在很多人在對CORBA進行學習,大家都已經了解到CORBA是一個完全中間性的語言,可以使用接口定義語言(IDL)定義開發(fā)時使用接口的 Client 和實現(xiàn)接口的 Server 所需要的信息。Client 和 Server 的具體實現(xiàn)代碼并不在IDL定義中編寫,而是使用某種目標語言的IDL 編譯器生成所需的代碼存根及helper類,Client 和 Server再使用真正的編程語言來進行具體實現(xiàn)。 為了保證在不同的 CORBA 產品基礎之上構建的分布式對象可以相互通信,Client和Server通過ORB(對象請求代理)進行通信。一般的運行流程是Client把請求發(fā)送給ORB,ORB再把請求發(fā)送給Server,Server把返回結果發(fā)送ORB,ORB再把返回結果發(fā)送給Client。ORB可以說Client和Server之間的翻譯者。即使Client和Server使用不同的編程語言編寫,只要是符合相同的IDL定義,ORB也可以完成相互的通信。 所有的文檔在強調服務器及客戶機可以是Java也可以是C++或其他語言(如:Delphi)進行編寫,但在網站或書本是沒有詳細說如何應對多語言客戶機的例子?!禞AVA2核心技術》上面有些說明,但也只是介紹性的文章,故自己下載了omniORB304,進行了一次使用SUN的 tnameserv命名服務程序,服務器用JAVA編寫,客戶機分別用JAVA和C++(VC6+omniORB)編寫的試驗,希望通過一次編程的具體操作實例來體驗或明了CORBA思想。 總體的編寫過程如下: - 用IDL定義一個接口文件,描繪要實現(xiàn)的功能,也可以說是定義一個要實現(xiàn)功能的一個模版(SysProp.idl)
- 使用"IDL to Java"編譯器(這里是IDLJ)將IDL文件轉化為Java編程語言中編寫的接口定義,生成所需的代碼存根及helper類
- 使用Java語言編寫客戶機和服務器的實現(xiàn)程序。
- 使用"IDL to C++"編譯器(這里是omniidl)將IDL文件轉化為C++編程語言中編寫的接口定義,生成所需的代碼存根及helper類
- 使用C++語言編寫客戶機實現(xiàn)程序(當然也可編寫服務器程序,但本次試驗沒有進行)
- 起動命名服務tnameserv
- 起動Java編寫的服務程序
- 用Java和C++編寫的客戶機分別調用相應的服務
運行環(huán)境的設定: 總體環(huán)境由jdk1.3+omniORB3.0(www.uk.research.att.com\omniORB\doc\3.0) +vc6 組成,下面說明具體的安裝。 2.1. 安裝JDK1.3 從SUN公司DOWN JDK1.3或者通過其他方式得到jdk1.3進行安裝,再設定相應的環(huán)境變量,在本文測試用的電腦上是如下所示: CLASSPATH=.; JAVA_HOME=D:\jdk130 | 修改原來的PATH變量,添加"%JAVA_HOME%\bin;",如下 注意:我在第一次使用jbuilder的jdk1.3時,服務器不能正常工作,我只是發(fā)覺這么一回事,具體原因與本文無關而沒有進行了解,請諒。 2.2. 安裝VC6 VC6按常規(guī)方式安裝,注意的是:在本文測試用的電腦上安裝在如下位置 C:\Program Files\Microsoft Visual Studio 2.3. 安裝omniORB 從 www.uk.research.att.com\omniORB\doc\3.0 下載omniORB3.0 ( 本文測試所下載的文件是 omniORB_304_x86_win32.zip )。 用WINZIP進行解壓omniORB_304_x86_win32.zip到omniORB_304_x86_win32目錄,目錄內存在omni目錄,復制omni目錄內的文件到你把想存放的位置。 測試電腦安裝在 C:\omni 根據(jù)C:\omni\README.win32 文檔進行設定,由于運行程序及命令行在控制臺進行,所以本次測試并不根據(jù)文檔要求去設定環(huán)境變量,而是編寫了一個omni.bat,在使用控制臺時,首先運行。 本測試電腦omni.bat內容如下 set TOP=c:\omni set path=%TOP%\bin\x86_win32;%path% set LIB=%TOP%\bin\x86_win32;%LIB% set INCLUDE=%TOP%\include;%INCLUDE% set VcOsDir= set VSCommonDir= | 如果你的電腦VC的環(huán)境變量已經設定在你的環(huán)境變量中,那么C:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT 就可以不運行。否則運行omni.bat前要首先運行VCVARS32.BAT。
實踐過程 約定所有編寫的文件保存在D:\mywork\t1中,omni.bat也在這個目錄內 3.1. 編寫SysProp.idl,功能是返回系統(tǒng)屬性 interface SysProp { string getProperty(in string name); }; | 3.2. 編寫JAVA的服務器 3.2.1. 把IDL文件轉化為JAVA編程語言代碼存根類及helper類。 執(zhí)行如下命令 在正常的情況下D:\mywork\t1 目錄內將生成以下文件,否則請檢查你的執(zhí)行程序及文件 SysProp.java SysPropHelper.java SysPropHolder.java SysPropOperations.java _SysPropImplBase.java _SysPropStub.java 3.2.2. 編寫 SysPropServer.java import org.omg.CosNaming.*; import org.omg.CORBA.*; | //編寫相對應的服務,一定要從 _類名ImplBase繼承,并實現(xiàn)相應的方法 class SysPropS extends _SysPropImplBase //具體的服務實現(xiàn) { public String getProperty(String key) { System.out.println("調用"+key); String S; S=System.getProperty(key); if (S==null) { S="null"; } System.out.println(key+"="+S); return S; } } public class SysPropServer //起動服務的程序 { public static void main(String args[]) { try { System.out.println("創(chuàng)建和初始化 ORB "); ORB orb = ORB.init(args, null); System.out.println("創(chuàng)建服務對象并將其向 ORB 注冊 "); SysPropS impl = new SysPropS(); orb.connect(impl); //打印IOR字符串 System.out.println(orb.object_to_string(impl)); org.omg.CORBA.Object namingContextObj =orb.resolve_initial_references("NameService"); NamingContext namingContext= NamingContextHelper.narrow(namingContextObj); NameComponent[] path = {new NameComponent("SysProp", "")}; System.out.println("綁定服務...SysPropS"); namingContext.rebind(path, impl); System.out.println("等待調用...SysPropS"); java.lang.Object sync = new java.lang.Object(); synchronized (sync) { sync.wait(); } } catch (Exception e) { System.err.println("Error: " + e); e.printStackTrace(System.out); } } } | 3.3. 編寫JAVA的客戶機 3.3.1. 編寫 SysPropClient.java 使用IOR字符串的方式 注意在代碼內有一段注解掉的代碼,用"http://使用ORB的方法的開始"開始,用"http://使用ORB的方法的結束"結束。這段代碼是使用ORB方法的代碼,如果在代碼中"http://使用IOR的方法開始"前一行添加"/*",在"http://使用IOR的方法結束"后一行添加"*/",而把"http://使用ORB的方法的開始"前面的"/*"去掉,把"http://使用ORB的方法的結束"后面的"*/"去掉,就是使用ORB方法的代碼,程序運行時就是" SysPropClient [環(huán)境變量] "的方式。以下是具體代碼: import org.omg.CosNaming.*; import org.omg.CORBA.*; public class SysPropClient { public static void main(String args[]) { try{ String SetInfo,ReturnInfo,ref; org.omg.CORBA.Object objRef; SysProp syspropRef; ORB orb = ORB.init(args, null); //使用IOR的方法開始 if (args.length>=1) { ref=args[0]; } else { System.out.println("SysPropClient <IOR字符串> [環(huán)境變量]"); return; } objRef = orb.string_to_object(ref); syspropRef = SysPropHelper.narrow(objRef); //使用IOR的方法結束 /* //使用ORB的方法的開始 objRef = orb.resolve_initial_references("NameService"); NamingContext ncRef = NamingContextHelper.narrow(objRef); // 進行服務定位 NameComponent nc = new NameComponent("SysProp", ""); NameComponent path[] = {nc}; syspropRef = SysPropHelper.narrow(ncRef.resolve(path)); //使用ORB的方法的開始結束 */ if (args.length>1) { SetInfo=args[1]; } else { SetInfo="java.home"; } System.out.println("開始調用"); ReturnInfo = syspropRef.getProperty(SetInfo); System.out.println(SetInfo+"="+ReturnInfo); } catch (Exception e) { System.out.println("ERROR : " + e) ; } } } | 3.3.2. 編譯程序,在文件目錄內執(zhí)行如下命令 jAVAC *.JAVA 3.4. 進行測試 第1控制臺,執(zhí)行 tnameserv 測試時如下所示 D:\mywork\t1>java tnameserv Initial Naming Context: IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f 6e746578743a312e3000000000010000000000000054000101000000000c3139322e3136382e302e 31000ca6000000000018afabcafe00000002a999c474000000080000000000000000000000010000 0001000000140000000000010020000000000001010000000000 TransientNameServer: setting port for initial object references to: 900 Ready. | 第2控制臺,執(zhí)行 java SysPropServer 測試時如下所示 D:\mywork\t1>java SysPropServer 創(chuàng)建和初始化 ORB 創(chuàng)建服務對象并將其向 ORB 注冊 IOR:000000000000001049444c3a53797350726f703a312e30000000000100000000000000540001 01000000000c3139322e3136382e302e31000ca7000000000018afabcafe00000002a999dbeb0000 00080000000000000000000000010000000100000014000000000001002000000000000101000000 0000 綁定服務...SysPropS 等待調用...SysPropS | 第3控制臺,執(zhí)行 java SysPropClient IOR:XXX JAVA.HOME 測試時如下所示 D:\mywork\t1>java SysPropClient IOR:000000000000001049444c3a53797350726f703a312e 3000000000010000000000000054000101000000000c3139322e3136382e302e31000ca700000000 0018afabcafe00000002a999dbeb0000000800000000000000000000000100000001000000140000 000000010020000000000001010000000000 java.home 開始調用 java.home=D:\bea\jdk130\jre | 3.5. 編寫C++的IOR客戶機 從實踐來講編寫C++的客戶機程序同JAVA沒有多大的區(qū)別,只不過JAVA是用idlj生成代碼存根類及helper類,而omni是用omniidl來生成代碼存根類及helper類,而編程思想及編碼過程非常相似。 由于C++的程序要調用omni及VC6的相關文件,所以進入控制臺后,如果VC沒有進行環(huán)境變量設定,那么要先運行 C:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT,再運行omni.bat,否則直接運行omni.bat后再編譯程序及運行程序。 3.5.1. 把IDL文件轉化為C++編程語言代碼存根類及helper類。 執(zhí)行如下命令 omniidl -bcxx SysProp.idl 在正常的情況下D:\mywork\t1 目錄內將生成C++編程語言的代碼存根類及helper類SysProp.hh和SysPropSK.cc。否則請檢查你的執(zhí)行程序及文件。 3.5.2. 編寫SysPropC.cc #include <iostream.h> #include <SysProp.hh> int main(int argc, char** argv) { try { CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB3"); if( argc < 2 || argc > 3 ) { cout << "usage: SysPropC <IOR字符串> [環(huán)境變量名]" << endl; return 1; } CORBA::Object_var obj = orb->string_to_object(argv[1]); SysProp_ptr echoref = SysProp::_narrow(obj); if( CORBA::is_nil(echoref) ) { cerr << "沒有對象" << endl; return 1; } const char* message; if (argc==3) { message=argv[2]; } else { message="java.home"; } CORBA::String_var dest = echoref->getProperty(message); cout << (char*)message << "=" <<(char*)dest << endl; orb->destroy(); } catch(...) { cerr << "Caught unknown exception." << endl; } return 0; } | 3.5.3. 編寫dirc.mak,如下所示 TOP = C:\omni OMNI_DYNAMIC_LIB = msvcstub.lib -NODEFAULTLIB:libcmt.lib -NODEFAULTLIB:libcmtd.lib CORBA_CPPFLAGS = -D__WIN32__ -D__x86__ -D__NT__ -D__OSVERSION__=4 CORBA_LIB = omniORB304_rt.lib omnithread2_rt.lib $(OMNI_DYNAMIC_LIB) wsock32.lib advapi32.lib -libpath:$(TOP)\lib\x86_win32 CXXFLAGS = -O2 -MD -GX $(CORBA_CPPFLAGS) $(DIR_CPPFLAGS) CXXLINKOPTIONS = .SUFFIXES: .cc .cc.obj: cl /nologo /c $(CXXFLAGS) /Tp$< all:: SysPropC.exe SysPropC.exe: SysPropSK.obj SysPropC.obj link -nologo $(CXXLINKOPTIONS) -out:$@ $** $(CORBA_LIB) clean:: -del *.obj -del *.exe veryclean:: -del *.obj -del echoSK.* echo.hh -del *.exe SysProp.hh SysPropSK.cc: SysProp.idl $(TOP)\bin\x86_win32\omniidl -T -bcxx -Wbh=.hh -Wbs=SK.cc -Wbtp SysProp.idl | 3.5.4. 編譯程序,執(zhí)行如下命令 nmake -f dirc.mak 3.5.5. 測試 在第4控制臺 SysPropC IOR:XXX JAVA.HOME 本測試如下所示 D:\mywork\t1>syspropc IOR:000000000000001049444c3a53797350726f703a312e3000000000 010000000000000054000101000000000c3139322e3136382e302e31000ca7000000000018afabca fe00000002a999dbeb00000008000000000000000000000001000000010000001400000000000100 20000000000001010000000000 os.name os.name=Windows 2000 | 3.6. 編寫C++的NAME方式客戶機 為了使用NANE方式,必須為OMNI軟件設置注冊表信息,要在注冊表中建立如下數(shù)據(jù)項(regedit)HKEY_LOCAL_MACHINE\SOFTWARE\ORL\omniORB\2.0\NAMESERVICE(字符串)。 NAMESERVICE的值為tnameserv(jdk1.3\bin內的程序)啟動的IOR值(第一次設置時自行添加)。 注意為了使用這種方式每次起動tnameserv后要用新IOR值換去舊的IOR值,我測試過用omini的omniNames.exe程序做服務器,IOR值是不變的,但服務器用JVAV編寫就會出錯。如果起動tnameserv,用 c編寫的服務器及客戶機就可以在上面運行。本例子如下所示 Initial Naming Context: IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f 6e746578743a312e3000000000010000000000000054000101000000000c3139322e3136382e302e 31000ca6000000000018afabcafe00000002a999c474000000080000000000000000000000010000 0001000000140000000000010020000000000001010000000000 TransientNameServer: setting port for initial object references to: 900 Ready. | 那么就要把從 IOR:開始(含IOR:)后面的字符串放進注冊表。 3.6.1. 編寫SysPropCC.cc //使用NAME方式的客戶機 #include <iostream.h> #include "SysProp.hh" static CORBA::Object_ptr getObjectReference(CORBA::ORB_ptr orb); int main (int argc, char **argv) { if( argc != 2 ) { cout << "使用方法: SysPropCC <環(huán)境變量名>" << endl; return 1; } try { CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB3"); CORBA::Object_var obj = getObjectReference(orb); SysProp_ptr echoref = SysProp::_narrow(obj); const char* message; if (argc==2){message=argv[1];} else {message="java.home"; } CORBA::String_var dest = echoref->getProperty(message); cout << (char*)message << "=" <<(char*)dest << endl; orb->destroy(); } catch(...) { cerr << "Caught unknown exception." << endl; } return 0; } ////////////////////////////////////////////////////////////////////// static CORBA::Object_ptr getObjectReference(CORBA::ORB_ptr orb) { CosNaming::NamingContext_var rootContext; try { // 定位服務器: CORBA::Object_var obj; obj = orb->resolve_initial_references("NameService"); // Narrow the reference returned. rootContext = CosNaming::NamingContext::_narrow(obj); if( CORBA::is_nil(rootContext) ) { cerr << " 初始化不成功." << endl; return CORBA::Object::_nil(); } } catch(CORBA::ORB::InvalidName& ex) { cerr << " 沒有找到服務" << endl; return CORBA::Object::_nil(); } CosNaming::Name name; name.length(1); name[0].id = (const char*) "SysProp"; name[0].kind = (const char*) ""; try { // Resolve the name to an object reference. return rootContext->resolve(name); } catch(...) { cerr << "定位不成功." << endl;} return CORBA::Object::_nil(); } | 3.6.2. 編寫dircc.mak TOP = C:\omni DIR_CPPFLAGS = -I. -I$(TOP)\include OMNI_DYNAMIC_LIB = msvcstub.lib -NODEFAULTLIB:libcmt.lib -NODEFAULTLIB:libcmtd.lib CORBA_CPPFLAGS = -D__WIN32__ -D__x86__ -D__NT__ -D__OSVERSION__=4 CORBA_LIB = omniORB304_rt.lib omnithread2_rt.lib $(OMNI_DYNAMIC_LIB) wsock32.lib advapi32.lib -libpath:$(TOP)\lib\x86_win32 CXXFLAGS = -O2 -MD -GX $(CORBA_CPPFLAGS) $(DIR_CPPFLAGS) CXXLINKOPTIONS = .SUFFIXES: .cc .cc.obj: cl /nologo /c $(CXXFLAGS) /Tp$< all:: SysPropCc.exe SysPropCc.exe: SysPropSK.obj SysPropCc.obj link -nologo $(CXXLINKOPTIONS) -out:$@ $** $(CORBA_LIB) clean:: -del *.obj -del *.exe veryclean:: -del *.obj -del echoSK.* echo.hh -del *.exe SysProp.hh SysPropSK.cc: SysProp.idl $(TOP)\bin\x86_win32\omniidl -T -bcxx -Wbh=.hh -Wbs=SK.cc -Wbtp SysProp.idl | 3.6.3. 編譯文件 nmake -f dircc.mak 3.6.4. 測試 在第5控制臺 SysPropCC JAVA.HOME, 測試結果如下所示 D:\mywork\t1>syspropcc java.home java.home=D:\bea\jdk130\jre |
小結 另還使用了j2sdkee1.2.1進行測試,由于j2sdkee1.2.1起動時的nameserver的PORT是1050 所以啟動服務要加參數(shù),對于本文中的程序運行時要如下所示: java SysPropServer -ORBInitialPort 1050 如果用IOR的方式,也可以用omni的omniNames.exe程序做命名服務器,用C++編寫服務器,客戶機使用Java和C++編寫,c++編寫服務器的例子可見omni的文檔。 本次只是簡單進行了測試,可以使大家了解一下,CORBA的這種特性,多種不同程序語言進行協(xié)作編程的具體運作過程,希望可以拋磚引玉。
參考資料 《JAVA2核心技術 卷II:高級特性》(對于omniORB是2.0的例子) 《The omniORB version 3.0 User's Guid》(omniORB軟件內自帶)
關于作者 | | | 王輝,具有八年的編程及系統(tǒng)管理經驗,所使用的語言為C和Java 編程語言。目前在深圳一家公司做分析員,使用C和JAVA為DB2數(shù)據(jù)庫編程。可通過 ddxxkk@21cn.com聯(lián)系。 | |