預警:本篇中有大量截圖+代碼/代碼段
關於CORBA組件的應用老師上課時給我們講了個例子:沒登錄qq時不能用qq截圖,只有登錄上了qq才能用。也就是啓動了qq服務端之後才能在客戶端使用服務端上的服務。本篇中的兩個實例也是出於類似的應用。
一、配置環境
我參考的是這篇文章https://www.jianshu.com/p/1fbc600de9cf,如果不在ecplise運行配置到第三步就可以了。
我在配置的時候遇到了坑,那就是按上面教程配置好後出現了報錯org.omg.CORBA不存在。這裏要求java的jdk版本一定不能太高了(我也不知道爲嘛),筆記本上面jdk是10以上的就不行,要改成低版本的!這裏提供一個低版本的jdk:https://pan.baidu.com/s/1rAz3H-DfSOvaVpxuQGoU0w 提取碼:cukz
我在改的時候還犯錯了:直接在path裏面加上了低版本的路徑,這樣也是不行的,在編譯Server和Client時我遇到的報錯是:
建議重新多建一個JAVA_HOME,具體過程可以百度“java多個版本jdk環境變量配置”。
二、實例
ps:我的代碼都是寫在記事本里面的,沒有用eclipse
【實例一:Java版CORBA程序1——HelloWorld】
它的作用是輸出一個字符串"HelloWorld+班級+姓名"
1.編寫IDL接口HelloWorld.idl:
module sample{
interface HelloWorld{
//二選一
wstring sayHello();//處理多字節的字符串,例如:中文
string sayHello();//處理ASCII類型的字符串
};
};
此處的module就類似於C++的namespace,裏面定義了一個接口HelloWorld。
寫好後在控制檯執行 ps:在控制檯執行下面所有文件都要在文件路徑下進行!此處截圖演示,後面也是一樣的。
idlj –fall HelloWorld.idl
比如我的HelloWorld是放在桌面的HelloWorld文件夾裏,那麼就應該
如果不進去會報錯找不到文件
運行完成後的控制檯是這樣的
此時再看HelloWorld.idl所在的文件夾,已經多出來了一個文件夾
裏面有六個文件
2.編寫並編譯服務端程序:HelloWorldServer.java
代碼如下
import sample.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
import org.omg.CORBA.portable.*;
import org.omg.PortableServer.*;
class HelloWorldServant extends HelloWorldPOA{ //對象實現類
//就算返回的字符串裏有中文,也不用成public wstring sayHello()會報錯找不到wstring類
public String sayHello(){
//return "\nHello World!\n";
return "\nHello World!\n軟工菜雞";//返回的字符串中有中文
}
}
public class HelloWorldServer{ //服務程序
public static void main(String args[]){
try{
//初始化ORB
ORB orb = ORB.init(args, null);
//取根POA的引用
org.omg.CORBA.Object poaobj = orb.resolve_initial_references ("RootPOA");
org.omg.PortableServer.POA rootPOA = org.omg.PortableServer.POAHelper.narrow(poaobj);
org.omg.PortableServer.POAManager manager = rootPOA.the_POAManager();
//創建伺服對象
HelloWorldServant objRef = new HelloWorldServant();
HelloWorld obj = objRef._this(orb);
//綁定命名服務
NamingContext ncRef = NamingContextHelper.narrow(orb.resolve_initial_references("NameService"));
NameComponent nc = new NameComponent("Hello", "");
NameComponent path[] = {nc};
ncRef.rebind(path, obj);
//激活POA管理器
manager.activate();
//等待處理客戶程序的請求,運行成功的話在控制檯顯示此內容
System.out.println("HelloWorld is running!");
orb.run();
}catch (Exception e) { //運行失敗捕獲異常
System.err.println("ERROR: " + e);
e.printStackTrace(System.out);
}
}
}
現在我們來分析一下HelloWorldServant,它作爲對象實現類,主要作用是實現客戶端的服務。它繼承自HelloWorldPOA類,打開HelloWorldPOA.java我們可以看到
public abstract class HelloWorldPOA extends org.omg.PortableServer.Servant
它是一個抽象類,繼承自一個Servant類。
再看這一部分函數:
public HelloWorld _this()
{
return HelloWorldHelper.narrow(
super._this_object());
}
public HelloWorld _this(org.omg.CORBA.ORB orb)
{
return HelloWorldHelper.narrow(
super._this_object(orb));
}
它們都返回了HelloWorldHelper中的narrow()函數,那麼這個函數是幹什麼的呢?打開HelloWorldHelper.java,我們發現它裏面首先定義了一個_id:
private static String _id = "IDL:sample/HelloWorld:1.0";//IDL:接口描述語言
找到narrow()函數,它的內容是:
public static sample.HelloWorld narrow (org.omg.CORBA.Object obj)
{
if (obj == null)
return null;//obj爲空就返回null
else if (obj instanceof sample.HelloWorld)
return (sample.HelloWorld)obj;//如果obj是HelloWorld的一個實例就返回轉換後的obj
else if (!obj._is_a (id ()))
throw new org.omg.CORBA.BAD_PARAM ();//我發現網上有好多人都遇到了這個異常
else
{
org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();
sample._HelloWorldStub stub = new sample._HelloWorldStub ();
stub._set_delegate(delegate);
return stub;
}
}
在百度第二個else if用法時我發現了好多人都遇到這個錯,而且還沒人解答,於是查找資料後我給出了一個解決錯誤的猜想:https://blog.csdn.net/d52370/article/details/90544386
總的來說HelloWorldServant就是提供一個客戶端實現的方法。
編譯HelloWorldServer。有警告不影響。
這樣就生成了HelloWorldServer和HelloWorldServant類
同時我們也發現sample包裏多了6個class
3.編寫並編譯客戶端程序: HelloWorldClient.java
代碼如下:
import sample.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
public class HelloWorldClient {
public static void main(String args[]) {
try{
ORB orb = ORB.init(args, null);//初始化一個ORB類
org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
NameComponent nc = new NameComponent("Hello",""); //定義一個組件
NameComponent path[] = {nc};
/*根據上面的分析ncRef.resolve(path)是sample.HelloWorld的一個實例,因此返回它被轉化成的helloWorld類*/
HelloWorld helloWorld = HelloWorldHelper.narrow(ncRef.resolve(path));
String hello = helloWorld.sayHello();
System.out.println(hello); //輸出sayHello()裏面的內容,具體內容在Servant裏面被定義
} catch (Exception e) {//捕獲異常
System.out.println("ERROR : " + e) ;
e.printStackTrace(System.out);
}
}
}
編譯HelloWorldClient.java
4.運行
(1)在控制檯當前路徑下輸入:
tnameserv -ORBInitialPort 100
啓動名字服務器,數字是端口號,可以自己任意設置。
顯示是:
(2)重新打開一個控制檯,進入到文件所在路徑,輸入:
java HelloWorldServer -ORBInitialPort 100
啓動服務端程序。
顯示是:
(3)再打開一個控制檯,進入到文件所在路徑,輸入:
java HelloWorldClient -ORBInitialPort 100
啓動客戶端程序。
顯示是:
【實例二:JAVA版CORBA程序2——Counter】
這是一個計數器,它可以自增自減並輸出自己的值。
1.編寫IDL接口counter.idl
代碼如下:
module CounterApp{
interface Counter{
readonly attribute long value; //只讀屬性
void inc();
void dec();
};
};
同樣在控制檯執行,我是在桌面建了一個Counter文件夾
生成六個文件
2.編寫並編譯對象實現代碼:CounterImpl.java //Impl是Implement的縮寫
代碼如下:
import CounterApp.*;
public class CounterImpl extends CounterPOA {
private int count;
public CounterImpl(){
count = 0; //初始化
}
public void inc(){
count++; //自加
}
public void dec(){
count - -; //自減
}
public int value(){
return count; //返回數值
}
}
編譯它
3.編寫並編譯服務端程序: Server.java
代碼如下:
import CounterApp.*;
import java.io.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
import org.omg.CORBA.portable.*;
import org.omg.PortableServer.*;
public class Server {
public static void main(String[] args){
try{
//初始化ORB
ORB orb = ORB.init(args, null);
//取根POA的引用
org.omg.CORBA.Object poaobj = orb.resolve_initial_references ("RootPOA");
org.omg.PortableServer.POA rootPOA = org.omg.PortableServer.POAHelper.narrow(poaobj);
org.omg.PortableServer.POAManager manager = rootPOA.the_POAManager();
//創建伺服對象
CounterImpl c_impl = new CounterImpl();
Counter c = c_impl._this(orb);
NamingContext ncRef = NamingContextHelper.narrow(orb.resolve_initial_references("NameService"));
//綁定命名服務
NameComponent nc = new NameComponent("Count", "");
NameComponent path[] = {nc};
ncRef.rebind(path, c);
//寫入文件
FileOutputStream file = new FileOutputStream("Counter.ref");//把數字存進這個文件裏面
PrintWriter writer = new PrintWriter(file);
String ref = orb.object_to_string(c);
writer.println(ref);
writer.flush();
file.close();
//等待處理客戶程序的請求
System.out.println("Server started."+" Stop:Ctrl-c");
rootPOA.the_POAManager().activate();
orb.run();
}catch(IOException ex){//捕獲文件異常
System.out.println("File error:"+ex.getMessage());
System.exit(2);
}catch(Exception ex){//捕獲其他異常
System.out.println("Exception: "+ex.getMessage());
System.exit(1);
}
}
}
編譯它
4.編寫並編譯客戶端程序: Client.java
代碼如下:
import CounterApp.*;
import java.util.*;
import java.io.*;
import org.omg.CORBA.*;
import org.omg.CosNaming.*;
public class Client {
public static void main(String[] args){
try{
//初始化ORB
ORB orb = ORB.init(args, null);
//以下分析同實例一
org.omg.CORBA.Object obj = orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(obj);
NameComponent nc = new NameComponent("Count","");
NameComponent path[] = {nc};
String ref = null;
try{
//從Counter.ref中讀取數據
Scanner reader = new Scanner(new File("Counter.ref"));
ref = reader.nextLine();
}catch(IOException ex){
System.out.println("File error: "+ex.getMessage());
System.exit(2);
}
obj = orb.string_to_object(ref);
if(obj == null){ //初始化失敗
System.out.println("Invalid IOR");
System.exit(4);
}
Counter c = null;
try{
c = CounterHelper.narrow(obj);
}catch(BAD_PARAM ex){
System.out.println("Narrowing failed");
System.exit(3);
}
int inp = -1;
do{
System.out.print("Counter value: "+c.value()+"\nAction(+/-/e)?");
System.out.flush();
do{
try{
inp = System.in.read();
}catch(IOException ioe){}
}while(inp != '+' && inp != '-' && inp != 'e');
if(inp == '+')
c.inc(); //自加
else if(inp == '-')
c.dec(); //自減
}while(inp != 'e');
}catch(Exception ex){
System.out.println("Exception: "+ex.getMessage()); //輸入錯誤
System.exit(1);
}
}
}
編譯它
5.運行
(1)在控制檯的當前路徑下運行,啓動名字服務器
tnameserv -ORBInitialPort 100
(2)打開一個新的控制檯,進入文件所在路徑,運行
java Server -ORBInitialPort 100
啓動服務端程序。
此時按住Ctrl+c可結束服務
(3)再打開一個新的控制檯,進入文件所在路徑,運行
java Client -ORBInitialPort 100
啓動客戶端程序。
此時可進行操作了,輸入"+"爲自增輸入"-"爲自減輸入"e"爲退出
同時我們可以發現已經生成了一個新的文件,也就是下次進入服務的Counter value會從這個文件裏面讀出,同時繼續。
三、結構組件圖