TAO教程系列之一:用TAO创建股票报价系统

用TAO创建股票报价系统

Building a Stock Quoter with TAO- A Tutorial

本教程译自$(ACE_ROOT%)/TAO/ docs/tutorials/Quoter。由Stone Jiang试译。原文

revision history:

date: 2007-11-05 by Stone Jiang

本教程是围绕一个单一的应用程序组织的,它允许客户端程序可以通过报价服务获取股票报价。该应用程序是基于Doug Schmidt和Steve Vinoski为C++ Report杂志的对象互联专栏的序列文章而开发的。

这篇教程从简单的客户程序及服务程序到剖析TAO的高级特性而逐步构建,这些高级特性包括了:asynchronous method invocation,reliable oneways, real-time Evnet Service, Interoperable Naming Service等等。

在您阅读本教程时,您可以自由地从提供的链接处获取到源文件。但是,如果您选择了编译和运行示例程序,这些源文件是作为您下载的TAO源文件的一部分。您可以从这下目录

$TAO_ROOT/docs/tutorials/Quoter

找到源代码,里面还包含了编译时需要的Makefile文件。由于本在线教有可能使用了与您不同版本的ACE+TAO源代码,因此单独下载这些源代码可能不能正确编译。

  1. 介绍- 一个很简单的客户端
  2. 介绍-一个很简单的服务端
  3. 介绍-改进服务端,通过POA策略之显示激活和用户自定义对象ID
  4. 介绍-改进服务端,通过POA策略之持久化对象引用
  5. 实现仓库
  6. TAO的命名服务
  7. 异步方法调用-CORBA为没有耐心的客户端提供的解决方案
  8. 按需激活
  9. TAO的Cos事件服务
  10. TAO的RT事件服务
  11. RTCORBA

1.介绍—— 一个很简单的客户端

我们将从一个相当简单的IDL接口开始我们的教程:我们想要创建一个股票报价服务,可以通过某些接口查询股票的价格。为了使学习之旅更加有趣,我们将用不同的CORBA对象表示不同的股票。哦,这看上去有些夸张,但是这些趣味性会激发我们更多的学习欲望,尤其在学习开始的时候。

定义IDL接口

对于股票报价系统,最起码的操当属查询股票价格的操作了,表示如下:

interface Stock

{

double price();

};

但通常,股票有符号和全称,于是我们再增加两个属性来便于查询:

interface Stock

{

double price();

readonly attribute string symbol;

readonly attribute string full_name;

};

我们还需要某个接口,通过它可以根据股票的符号访问它的对象引用。习惯上,我们把这种接口称其为工厂(Factory),它看上去像这样:

interface Stock_Factory

{

Stock get_stock (in string stock_symbole);

};

请注意参数怎样具有方向性的:方向性用in,out, inout表示。仅输入用(in)表示,仅输出用(out)表示,既输入也输出用(inout)表示。到这里我们还有一个问题,如果股票的符号无效会怎么办呢?CORBA的做法是抛出一个异常:

exception Invalid_Stock_Symbol{};

我们再把异常作为Stock_Factory接口的一部分:

interface Stock_Factory

{

Stock get_stock (in string stock_symbole)

raise (Invalid_Stock_Symbol);

};

最后,我们把上面所有的IDL组织在一起并放进一个模块中以避免对名字空间的污染,于是得到了Quoter.idl 文件。

//filename: Quoter.idl

module Quoter
{
exception Invalid_Stock_Symbol {};
// Used to report an invalid stock name
// Forward declare the Stock interface
interface Stock;
interface Stock_Factory
{
// = TITLE
// A factory class for the stock quoter interfaces
//
// = DESCRIPTION
// Return the Quoter interfaces based on their names
//
Stock get_stock (in string stock_symbol)
raises (Invalid_Stock_Symbol);
};

interface Stock
{
// = TITLE
// A simple interface to query the name and price of stock
//
// = DESCRIPTION
// Return the price and name of a single stock
//
readonly attribute string symbol;
// Get the stock symbol.
readonly attribute string full_name;
// Get the name.
double price ();
// Get the price
};
};

生成的文件

让我们花几分钟来查看生成的代码。你无须经常这样做,事实上你根本无须这样做。但是我们这样做是为了学习的需要,并帮助您打消对IDL编译器的神密感。

为了生成代码,你必须调用IDL编译器,操作如下:

$ACE_ROOT/TAO/TAO_IDL/tao_idl Quoter.idl。

在Windows下,tao_idl.exe在%ACE_ROOT%/bin中。

%ACE_ROOT%/bin/tao_idl Quoter.idl

如果把%ACE_ROOT%/bin 添加至环境变量PATH中,则可以简化为

tao_idl Quoter.idl

IDL编译器完整的文档和选项都包含在compiler.html 中。

TAO为每个IDL文件生成9个文件。

QuoterC.h, QuoterC.inl 和 QuoterC.cpp 包含了客户端的接口。注意,内联(inline)函数放在独立的文件中,这样你可以有选择的编译它们,以便生成更小的代码。单纯的客户端只需要链接从QuoterC.cpp生成的目标文件。

与之相似,

QuoterS.h, QuoterS.inl 和 QuoterS.cpp 包含了服务端的skeletons。服务端必须链接从QuoterS.cpp和QuoterC.cpp生成的目标文件。

最后,

QuoterT.h, QuoterT.inl 和 QuoterT.cpp 包含TIE类。有基于组合的方式代替基于继承的skeleton标准(在CORBA 2.2规范之后)。把它们分离在不同的文件中仅仅是因为有的编译器不能处理中同一个源代码文件中同时混合了模板和非模板的代码。因此你根据不需要在所有平台上编译这些文件。然后,编译QuoterS.cpp需要这些文件。还要注意的事,如果您的平台不支持命字空间,那么对于有的IDL接口不能使用TIE方法。

上面提到的扩展名和后缀可以通过使用IDL编译器的选择来修改,要知道更多的细节,可以查阅文档。另外还需要注意的事情是您需要在您的项目中使用一致的扩展名,否则在你的IDL源文件中使用某些#include指令时会出现问题。

创建一个简单的客户端

在IDL接口就绪之后,我们想要编写简单的客户端了。在所有的CORBA客户端或服务端要做的第一件事就是初始化ORB:

int main( int argc, char* argv[])

{

try {

// First initialize the ORB, that will remove some arguments…

CORBA::ORB_var orb =

CORBA::ORB_init(argc, argv,

"" /* the ORB name, it can be anything! */

);

IDL支持在编译时长度未知的变长类型,因此在运行时需要动态分配它们。_var 类型减化了我们对变长类型显示内存管理,还隐藏了定长和变长结构化的类型之间的区别。

ORB初始化可能失败,实际上,所有的CORBA操作都可能抛出CORBA::SystemException 异常,我们用 try/catch语句块来检查可能的失败。 不用说,这是非常天真的;某些失败是临时的,我们需要更好的方法从错误中恢复,但在我们的示例中这已经足够了。因此,在main()函数的最后,我们捕获了CORBA的所有类型的异常。

}
catch (CORBA::Exception &ex)

{
std::cerr << "CORBA exception raised!" << std::endl;
}
return 0;
}

我们不能忘记ORB是一种资源,应用程序必须释放它。直到CORBA 2.3,并没有标准的方法来处理释放ORB这件中。TAO已经采纳了新的规范,于是,我们的客户程序将如下所示:

int main (int argc, char* argv[])
{
try {
// First initialize the ORB, that will remove some arguments...
CORBA::ORB_var orb =
CORBA::ORB_init (argc, argv,
"" /* the ORB name, it can be anything! */);
// the real code goes here!
orb->destroy ();
}
catch (CORBA::Exception &ex) {
std::cerr << "CORBA exception raised!" << std::endl;
}
return 0;
}

关于ORB名字,仅需要几句话来说明:如果在CORBA::init中使用了相同的ORB标识,规范要求ORB返回相同的ORB指针,并且,即使使用了不同的标识,返回相同指针的实现也是自由的。通常这不是问题,这是由于大多数应用程序只实例化单一的ORB。TAO是实际支持多个ORB指针中少数CORBA实现之一。这一点对于real-time应用程序非常重要,在这些应用程序里,每一个ORB可以在与不同的优先级执行。

既然我们已经拥有了ORB指针,我们可以启动此应用程序了。正常情况下,我们使用Naming Service, Trading Service或者Interoperable Naming Service来定位股票工厂,但这里为了简化起见,让我们在第一个参数中使用IOR字符串。

最简单的方法是用第一个参数来得到字符串,然后用string_to_object()函数把它转换成对象引用。

CORBA::Object_var factory_object =
orb->string_to_object (argv[1]);
Quoter::Stock_Factory_var factory =
Quoter::Stock_Factory::_narrow (factory_object.in ());

_narrow()函数用于测试一个引用是否为指定的类型。如果引用是指定的类型,它返回一个非空的引用,否则返回为空。

关于TAO有些有趣的事情:第一,对于对象引用,它支持file:scheme,因此,第一个参数可以是file://a_file_name. 在这种情形下,ORB将打开名为"a_file_name"的文件并从文件中读取IOR。TAO还支持corbaloc:scheme,例如

corbaloc:iiop:[email protected]:12345/Stock_Factory。

因此使用字符串可能是非常强大的引导协议。

如果我们要使用从IDL编译器生成的接口,那就就必须包含正确的头文件。

#include "QuoterC.h"

注意,这是您仅需要包含的头一个文件; IDL 编译器生成的代码包含了所有需要的内部头文件。当我们使用TAO服务时,也不要忘了包含那些适当的头文件。

TAO另一个有趣的特性是它支持 _unchecked_narrow()。 这是CORBA Messaging规范的一部分,在本质上它与_narrow()执行同样的工作,但不同的时,它并不远程的检查类型。如果你在编译时有必要的信息能确定narrow操作的正确性,那么使用不检查的版本会更高效。

现在我们可以使用剩余的参数来找回股票对象:

for (int i = 2; i != argc; ++i) {
try {
// Get the stock object
Quoter::Stock_var stock =
factory->get_stock (argv[i]);

练习1

完成客户端的实现。这一点应该很容易,但是它给了你机会来建立你的开发环境并能让你熟悉构建TAO应用程序的基础知识。

解决方案

查看 client.cpp文件;和你的代码比应该没有多少不同。在你的客户端代码、idl文件和QuoterC.*文件中分别数一下代码的行数,你会想再一次重写所有的代码吗?

测试

若要测试本程序,我们还需要服务端能工作,这是我们进入本教程下一课最好的理由。

你可能感兴趣的:(应用服务器,windows,Scheme,项目管理)