第六章 编写客户端程序
第六章 编写客户端程序 基本知识 DII DSI 接口库 上下文
基本知识
请求 请求:是一个CORBA伪对象,与一个请求对象相关联的信息包括目标对象引用,操作名字,0或多个参数,可选的上下文,以及用于存放返回结果或异常信息的环境 封装一个请求有两种格式 静态:请求对象由IDL桩隐式发送 动态:显式创建一个请求对象,选择一种通信方式发送并获取响应结果,由接口Request描述;本章关注的部分
Request接口 定义了对一个CORBA对象的某一操作的一次调用请求 调用一个操作之前必须首先获取目标对象引用 见P140
调用类型 静态调用:客户程序在编译前就知道IDL接口信息 动态调用: 编写客户程序时尚未确定IDL接口类型 运行时利用接口库服务提供的信息创建一个请求对象 大部分任务是建立请求对象,而不是调用过程本身
选择调用类型 简单性 灵活性 调用性能 通信方式 小结:能用SII则用SII DII需由程序员手工完成IDL桩所完成的任务
编写客户程序——静态 客户程序Client.java 初始化ORB 绑定到服务对象 调用服务对象提供的服务 public class Client { public static void main(String[] args) org.omg.CORBA.ORB orb=org.omg.CORBA.ORB.init(args, null); // 利用POA全称与对象标识"BankManager"查找帐户管理员 Bank.AccountManager manager = Bank.AccountManagerHelper.bind( orb, "/BankPOA", "BankManager".getBytes()); String name = args.length > 0 ? args[0] : "David Zeng"; // 请求帐户管理员找出一个指定名字的帐户,无此帐户则新开一个 Bank.Account account = manager.open(name); System.out.println(name + “的帐户余额为” + account.getBalance() + "元"); account.deposit(200); System.out.println(“存款200元后,余额为” + account.getBalance() + “元”); if (account.withdraw(600)) { System.out.println(“取款600元后,余额为” + } else { System.out.println("余额不足600元,取款失败,余额保持不变"); } 初始化ORB 绑定到服务对象 调用服务对象提供的服务 客户程序Client.java
创建一个调用manager的open()操作的请求对象 编写客户程序——动态 初始化org public class Client(){ public static void main(String[] args){ org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null); org.omg.CORBA.Object manager = ((com.inprise.vbroker.orb.ORB) orb).bind( “BankPOA”,”BankManager”.getBytes(),null,null); org.omg.CORBA.Request requestOpen = manager._request(“open”); String name = args.length>0?args[0]:”David”;. requestOpen.add_in_arg().insert_string(name); requestOpen.set_return_type(orb.get_primitive_tc(org.omg.CORBA.TCKind.tk_objeref)); requsetOpen.invoke(); org.omg.CORBA.Object account = requestOpen.result().value().extract_Object(); org.omg.CORBA.Request requestBalance = account._request(“getBalance”); requestBalance.set_return_type(orb.get_primitive_tc(org.omg.CORBA.TCKind.tk_float)); requestBalance.invoke(); float bal=requestBalance.resutl().value().extract_float(); System.out.println(“帐户”+name+”的余额为”+bal); } 获取帐户管理员的通用对象引用 创建一个调用manager的open()操作的请求对象 设置请求的实际参数 设置请求的返回结果的类型 以同步通信方式发送请求 提取返回结果 继续以DII方式调用account 的getBalance()方法
2 动态调用机制
动态调用机制 基本接口 动态调用过程 编程
基本接口
动态调用接口 CORBA::Object:定义每个CORBA对象都必须支持的方法 CORBA:: ORB:定义客户端和服务端都能使用的操作,是内核实现的方法 CORBA:: Request:定义远程对象操作 CORBA:: List:用于构建参数列表,维护一个由构造类型NameValue组成的数据项列表
伪对象 IDL对象类由IDL语言描述并需要向BOA和ORB核心登记和管理,而且IDL对 象类都继承基类CORBAObject 伪对象类尽管也可以由IDL语言描述,但是,它不需要向 BOA和ORB核心登记,也不需要继承CORBAObject类。 伪对象类包括Environment、NameVal ue、NVList、Request、ServerRequest、Context、Princial、TypeCode、BOA和ORB。
CORBA::ORB 见P141 Create_list:生成一个空的NVlist对象 Create_operation_list:生成NVlist对象,利用客户描述的操作参数自动进行填充,并将已赋值的NVlist返回给客户
CORBA::Object(对象引用) 创建一个请求,在目标对象上执行 _request: 创建一个请求对象后,可调用该请求对象的add_in_arg(),add_name_in_arg()等方法为调用操作设置一个或多个实参 _create_request:调用前必须先创建操作的实参
CORBA::Request(操作请求) Add_arg:在请求格式中添加参数 Invoke:完成一个调用/返回操作 Send:根据请求中的信息发起操作,不需等待操作结果就将控制返回给调用者 Pull_reponse:检查请求操作是否完成 Get_reponse:回送执行请求的结果信息
NVList(参数与返回值) 用于构造参数列表的接口 Add_item:把一个新的参数加到指定的列表中 Free_memory:
动态调用过程
clien Server InterfaceDef OperationDef CORBA:ORB CORBA:Nvlist 1 get_interface 2 lookup_name 3describe 4 create_list 5 add_item (1-n) 6 create_request 7 invoke 8 delete 9 free InterfaceDef OperationDef CORBA:ORB CORBA:Nvlist CORBA:Object ORB:Request 动态调用环境 接口库环境
一:查询接口定义信息 1 客户调用目标对象中的get_interface操作。调用后,客户得到一个存放在接口库中的InterfaceDef对象,从而获得目标对象的接口描述信息
二:寻找操作的描述 2 以InteraceDef对象作为接口库导航的入口点,找出有关的接口对象以及它所支持的操作。(通过调用InterfaceDef的lookup_name操作,得到OperationDef对象) 3 调用OperationDef对象中的describe(),获得需调用的操作的完整描述,即操作的全部IDL定义
三. 准备参数列表 4 调用ORB接口中用于动态调用的create_list方法,创建一个空闲的参数列表,即Nvlist对象 5 调用Nvlist上的add_item(),将各个请求参数逐一填入参数列表中
四。创建请求对象 6 调用目标对象上的,从CORBA::Object接口继承的create_request操作,构造一个请求对象。在请求中指出操作名,参数和返回结果参数
五。调用已建立的请求 7 调用ORB::Request对象中的invoke操作,发起对请求的调用 六 善后 8 执行ORB::Request对象的delete操作,释放请求对象和相关的内存空间 9 调用ORB::NVlist对象上free操作,释放列表结构的相关空间
创建一个调用manager的open()操作的请求对象 实例 初始化org public class Client(){ public static void main(String[] args){ org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null); org.omg.CORBA.Object manager = ((com.inprise.vbroker.orb.ORB) orb).bind( “BankPOA”,”BankManager”.getBytes(),null,null); org.omg.CORBA.Request requestOpen = manager._request(“open”); String name = args.length>0?args[0]:”David”;. requestOpen.add_in_arg().insert_string(name); requestOpen.set_return_type(orb.get_primitive_tc(org.omg.CORBA.TCKind.tk_objeref)); requsetOpen.invoke(); org.omg.CORBA.Object account = requestOpen.result().value().extract_Object(); org.omg.CORBA.Request requestBalance = account._request(“getBalance”); requestBalance.set_return_type(orb.get_primitive_tc(org.omg.CORBA.TCKind.tk_float)); requestBalance.invoke(); float bal=requestBalance.resutl().value().extract_float(); System.out.println(“帐户”+name+”的余额为”+bal); } 获取帐户管理员的通用对象引用 创建一个调用manager的open()操作的请求对象 设置请求的实际参数 设置请求的返回结果的类型 以同步通信方式发送请求 提取返回结果 继续以DII方式调用account 的getBalance()方法
客户端DII编程
实现规范 客户可以使用DII对任意多个接口的对象发起调用 对一个接口的一部分对象发起调用,另一部分用静态存根调用 用一组API获得和解析一个调用,实现并返回结果
编写DII 获取目标对象引用 创建请求对象 传递实际参数并指定返回类型 发送请求并等待响应 提取结果
步骤 向目标对象询问描述目标对象接口信息的对象,并将其放在接口库中 从接口库的对象中,找到所要调用的操作的描述 建立调用参数表,并逐一填入参数 创建请求。请求中包含目标对象的引用,方法名,参数表和返回值 调用请求,并作善后处理
1 获取目标对象引用 因为编译客户程序时目标对象的接口类型通常尚未确定,因而没有可用帮助类 扩展两个bind()方法 public org.omg.CORBA.Object bind( String fullPoaName, //POA全名 byte[] objectId, //对象标识 String hostName, //对象所在主机名字 BindOptions options //绑定选项 ) String repositoryId, //接口库标识 String objectName, //对象名 org.omg.CORBA.Object manager = ((com.inprise.vbroker.orb.ORB) orb).bind( “BankPOA”,”BankManager”.getBytes(),null,null);
2 创建请求对象 对目标对象引用的某一操作的一次调用 发起请求时,请求对象被打包通过ORB发送到服务端 使用SII时,该过程对程序员是透明的 使用DII,必须由程序员自己显式创建并发送请求对象 必须由程序员自己显式地创建并发送请求对象,_request()/_create_request org.omg.CORBA.Request request = manager._request(“open”);
3 传递实参并指定返回类型 请求对象中操作的实际参数表被表示为一个NVlist实例,是若干NamedValue对象的列表,每个NamedValue是一三元组(名字,值,标志)
4 发送请求和提取结果 invoke():一直阻塞,直到响应消息返回 Return_value(): send_deferred():非阻塞 poll_responds+get_response() 延迟同时发送多个请求对象:send_multiple_requests_deferred,以一个请求对象序列作为参数 poll_nex_response()+get_next_response()
创建一个调用manager的open()操作的请求对象 2.4 实例 初始化org public class Client(){ public static void main(String[] args){ org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null); org.omg.CORBA.Object manager = ((com.inprise.vbroker.orb.ORB) orb).bind( “BankPOA”,”BankManager”.getBytes(),null,null); org.omg.CORBA.Request requestOpen = manager._request(“open”); String name = args.length>0?args[0]:”David”;. requestOpen.add_in_arg().insert_string(name); requestOpen.set_return_type(orb.get_primitive_tc(org.omg.CORBA.TCKind.tk_objeref)); requsetOpen.invoke(); org.omg.CORBA.Object account = requestOpen.result().value().extract_Object(); org.omg.CORBA.Request requestBalance = account._request(“getBalance”); requestBalance.set_return_type(orb.get_primitive_tc(org.omg.CORBA.TCKind.tk_float)); requestBalance.invoke(); float bal=requestBalance.resutl().value().extract_float(); System.out.println(“帐户”+name+”的余额为”+bal); } 获取帐户管理员的通用对象引用 创建一个调用manager的open()操作的请求对象 设置请求的实际参数 设置请求的返回结果的类型 以同步通信方式发送请求 提取返回结果 继续以DII方式调用account 的getBalance()方法
_Accountstub.java——DII public boolean withdraw (float amount) { org.omg.CORBA.Request _request = this._request("withdraw"); _request.set_return_type(_orb().get_primitive_tc(org.omg.CORBA.TCKind.tk_boolean)); org.omg.CORBA.Any __param_amount = _request.add_in_arg(); __param_amount.insert_float((float)amount); _request.invoke(); java.lang.Exception _exception = _request.env().exception(); if (_exception != null) { throw (org.omg.CORBA.SystemException) _exception; } boolean _result; _result = _request.return_value().extract_boolean(); return _result;
_Accountstub.java——SII public boolean withdraw (float amount) { while (true) { if (!_is_local()) { org.omg.CORBA.portable.OutputStream _output = null; org.omg.CORBA.portable.InputStream _input = null; boolean _result; try { _output = this._request("withdraw", true); _output.write_float((float)amount); _input = this._invoke(_output); _result = _input.read_boolean(); return _result; } catch (org.omg.CORBA.portable.ApplicationException _exception) { final org.omg.CORBA.portable.InputStream in = _exception.getInputStream(); java.lang.String _exception_id = _exception.getId(); throw new org.omg.CORBA.UNKNOWN("Unexpected User Exception: " + _exception_id); catch (org.omg.CORBA.portable.RemarshalException _exception) { continue; finally { this._releaseReply(_input); } else { final org.omg.CORBA.portable.ServantObject _so = _servant_preinvoke("withdraw", _opsClass); if (_so == null) { final Bank.AccountOperations _self = (Bank.AccountOperations)_so.servant; return _self.withdraw(amount);
DSI
动态框架 DSI允许server在运行时刻动态对任意对象予以实现 可以实现任意多个接口的对象 可以实现一个接口中的一部分对象,另一部分由静态框架实现 不必继承由IDL编译器生成的框架(POA类),也不必使用纽带机制间接使用POA类
原理 Impl ServerRequest IR ServerRequest POA
开发步骤 利用回调方法invoke()接收从ORB转发来的一个ServerRequest类型的请求对象 从请求对象中提取实际参数向上调用真正执行服务的伺服对象的方法 将结果或异常填入请求对象中
1 对象ServerRequest 作用:实现动态框架功能 Class ServerRequest { public: const char * operation() const; void argument(NVList_prt& parmaters); Context_ptr ctx(); void set_result(const Any& value); void set_exception(const Any& value); }; 作用:实现动态框架功能
arguments(inout NVlist nv): 得到输入型参数的值。 准备:按照对应操作的IDL定义为NVlist进行初始赋值,包括设置每个元素的类型码和标志(flags,输入输出类型) 执行后,NVlist得到输入型参数值 完成功能后,在NVList中设置返回的值 set_result():设置返回结果的值 set_exception(): 抛出异常
2 请求实现 DSI方式下,对象的请求交给动态实现例程DIR(Dynamic Implementation Routine) 完成:接收请求,获得请求的操作信息,动态决定如何实现对象,最后返回结果 DIR: 不是某一特定接口的实现对象 通过对象适配器获得一个请求,并进一步获得该请求的操作信息 动态决定如何实现该对象 通过对象适配器返回输出结果 需要实现DIR的例程要由特定对象实现(实现DynamicImplementation接口) Interface DynamicImplementation{ void invoke(); }
3 程序实例 import org.omg.CORBA.*; import org.omg.PortableServer.*; public class BankImpl extends org.omg.PortableServer.DynamicImplementation { // 属性的定义 protected java.util.Map accountList; // 所有账户的列表 protected ORB orb; // 用于创建请求对象的ORB伪对象 protected POA poa; // 用于创建新对象引用的POA public BankImpl(ORB orb, POA poa) { accountList = new java.util.HashMap(); this.orb = orb; this.poa = poa; } // ORB回调方法,是每一个采用DSI的对象实现都必须实现的方法 需要实现该接口 构造方法,管理员开始 时管理的账户清单为空
public void invoke(ServerRequest request) { // 根据当前对象引用取对象标识(对Account而言,对象标识就是账户名字) String objectId = new String(_object_id()); System.out.println(objectId + " executing " + request.operation() + "() ..."); // 根据请求对象中的操作名字分别处理 if (request.operation().equals("open")) { // 开设账户操作 try { org.omg.CORBA.NVList paramList = orb.create_list(0); org.omg.CORBA.Any param = orb.create_any(); param.insert_string(""); paramList.add_value("name", param, org.omg.CORBA.ARG_IN.value); request.arguments(paramList); String name = paramList.item(0).value().extract_string(); // 执行开户操作,在账户清单中查找或开设指定名字的账户 if (accountList.get(name) == null) { java.util.Random random = new java.util.Random(); float balance = Math.abs(random.nextInt()) % 100000 / 100f; accountList.put(name, new Float(balance)); System.out.println("新开账户:" + name); } org.omg.CORBA.Object account = poa.create_reference_with_id( name.getBytes(), "IDL:Bank/Account:1.0"); org.omg.CORBA.Any result = orb.create_any(); result.insert_Object(account); request.set_result(result); } catch(Exception exc) { exc.printStackTrace(); 从请求对象中获取实际参数 创建一any型,并填入“ ” 往请求对象中填写返回结果
} else if (request.operation().equals("deposit")) { // 存款操作 try { // 根据对象标识取账户余额 Float balance = (Float) accountList.get(objectId); if (balance == null) throw new org.omg.CORBA.BAD_PARAM(); // 从请求对象中获取参数 org.omg.CORBA.NVList paramList = orb.create_list(0); org.omg.CORBA.Any param = orb.create_any(); param.insert_float(0); paramList.add_value("amount", param, org.omg.CORBA.ARG_IN.value); request.arguments(paramList); float amount = paramList.item(0).value().extract_float(); // 执行存款操作 accountList.remove(objectId); accountList.put(objectId, new Float(balance.floatValue() + amount)); // 该操作没有返回结果 } catch(Exception exc) { exc.printStackTrace(); }
} else if (request.operation().equals("withdraw")) { // 取款操作 try { // 根据对象标识取账户余额 Float balance = (Float) accountList.get(objectId); if (balance == null) throw new org.omg.CORBA.BAD_PARAM(); // 从请求对象中获取参数 org.omg.CORBA.NVList paramList = orb.create_list(0); org.omg.CORBA.Any param = orb.create_any(); param.insert_float(0); paramList.add_value("amount", param, org.omg.CORBA.ARG_IN.value); request.arguments(paramList); float amount = paramList.item(0).value().extract_float(); // 执行取款操作并填写请求对象中的返回结果 org.omg.CORBA.Any result = orb.create_any(); if (balance.floatValue() < amount) result.insert_boolean(false); else { accountList.remove(objectId); accountList.put(objectId, new Float(balance.floatValue() - amount)); result.insert_boolean(true); } request.set_result(result); } catch(Exception exc) { exc.printStackTrace();
} else if (request.operation().equals("getBalance")) { // 查询余额操作 try { // 根据对象标识取账户余额 Float balance = (Float) accountList.get(objectId); if (balance == null) throw new org.omg.CORBA.BAD_PARAM(); // 从请求对象中获取参数(尽管该操作的参数表为空,也必须执行一次下列操作) request.arguments(orb.create_list(0)); // 执行查询操作并填写请求对象中的返回结果 org.omg.CORBA.Any result = orb.create_any(); result.insert_float(balance.floatValue()); request.set_result(result); } catch(Exception exc) { exc.printStackTrace(); } } else { // 非法的操作名字 throw new org.omg.CORBA.BAD_OPERATION(); public String[] _all_interfaces(org.omg.PortableServer.POA poa, byte[] oid) { return null;
接口库
接口库 对象接口定义也可由一个专门的服务进程来存储或管理,即组织成可运行的接口库(IR)服务,由其提供对象接口的相关信息 接口库与IDL文件表达的信息完全相同,更适于客户程序或对象实现在运行时可动态访问 常见的用法:查找一个对象引用所有IDL接口定义
接口库 DII DSI 作用 得到远程对象引用,但不知该对象是否支持自己所需操作,及所需参数 DynamicImplementation接口获得ServerRequest对象,对该操作的参数信息一无所知 作用 保存IDL接口信息 提供IDL接口信息的动态查询,修改和删除
上下文
请求的上下文 定义:客户程序发送请求时附带传递的一种特殊参数 作用:用于描述客户程序的运行环境 属性:(属性名,属性值) 使用: 见P165 客户程序创建一个上下文对象并设置其中的属性,附在一个请求中发送给ORB 请求和上下文对象发给对象实现 对象实现根据客户不同属性作出不同处理 见P165