TAO教程之三:介绍-改进服务端,通过POA策略之显示激活和用户自定义对象ID

3.介绍-改进服务端,通过POA策略之显示激活和用户自定义对象ID

介绍——改善服务端

在本节,我们将在之前提到的“简单服务器”之上进行改进。我们将会讨论如何使把POA策略赋给对象ID。

在先前的示例中,我们用了 Quoter_Stock_Factory_i 的两个域来表示两支股票。如果我们想创建上百支股票,那么这种方法就缺少扩展性。我们需要用某个集合来保持对股票对象的跟踪,比如为股票符号建立索引。一种解决办法是使用STL map或与之差不多的集合类,但这样明显太浪费了。毕意,ORB已经以对象ID为索引建立了对象的集合。如果仅能自己选择ID,那么我们的问题就能得已解决。一个好消息是:POA允许这样做;一个坏消息是我们必须创建子POA。那这是为什么呢?这是因为RootPOA的ID是被ORB分配了的,我们不想要与它们冲突。此外,创建一个独立的POA比管理它更容易,因为多个应用程序的组件可以获得它们自己的POA,它们能被视为私有的命字空间。

子POA的创建

如前所述,我们可以访问RootPOA:

CORBA::Object_var poa_object =
orb->resolve_initial_references ("RootPOA");
PortableServer::POA_var poa =
PortableServer::POA::_narrow (poa_object.in ());

现在我们为子POA创建策略。在这个应用场景下,我们想使用USER_ID策略,这样就可以把它赋自己的这些ID。我们还想使用 NO_IMPLICIT_ACTIVATION 策略,以便于对我们的POA有更多的控制。POA具有许多我们可以控制的策略,但我们在示例中只使用默认的策略。在$TAO_ROOT/examples/POA/目录下有许多示例演示了如何使用这些策略。

策略存储在序列中的,所以我们首先创建序列并初始化它的长度。

CORBA::PolicyList policies (2);
policies.length (2);

接下来我们创建策略:

policies[0] =
poa->create_id_assignment_policy (PortableServer::USER_ID);
policies[1] =
poa->create_implicit_activation_policy (PortableServer::NO_IMPLICIT_ACTIVATION);

再然后我们创建子POA:

PortableServer::POA_var stock_factory_poa =
poa->create_POA ("Stock_Factory_POA",
poa_manager.in (),
policies);

注意的是,我们与RootPOA共用了POA管理器,于是我们只需要用一个POA管理就可以控制这两个POA的状态。新的POA复制了策略,所以我们为了避免内存泄漏,所以需要销毁它们。

for (CORBA::ULong i = 0; i != policies.length (); ++i)

{
policies[i]->destroy ();
}

在子POA中激活对象

现在我们必须使用这个POA来激活股票对象了。为了使示例简化,我们假设我们可以从标准输入设备(stdin)中读入这些股票信息,比如这样:

while (!std::cin.eof () && std::cin.peek () != EOF)

{
const int max_symbol_length = 8;
char symbol[max_symbol_length];
const int max_full_name_length = 64;
char full_name[max_full_name_length];
double price;
std::cin.getline (symbol, max_symbol_length, '\n');
std::cin.getline (full_name, max_full_name, '\n');
std::cin >> price;
std::cin.ignore (1, '\n');
// The interesting stuff goes here!
}

为每个符号、全称和价格的三元组中, 我们创建股票实现的对象:

PortableServer::ServantBase_var servant =
new Quoter_Stock_i (symbol, full_name, price);

ServantBase_var 充当的角色很象自动指针,在发生异常的情况下,它可以小心的管理对象的回收操作。这次我们不能使用_this() 来激活对象,这是因数我们要想创建自己对象ID:

PortableServer::ObjectId_var oid =
PortableServer::string_to_ObjectId (symbol);

然后我们通过id来激活对象:

stock_factory_poa->activate_object_with_id (oid.in (),
servant.in ());

请小心,不要在这些对象上调用_this(),如果这样就会在RootPOA中激活它们,被激活了两次!虽然在不同的POA中(甚于有时还在同一个POA中)多次激活一个对象是完全合法的,它是在现在的应用情景中我们并不需要这样做。

修改Stock Factory

现在我们必须实现Stock Factory的不同版本。我们传一个子POA的引用到构造函数中并维护这个引用:

class Quoter_Stock_Factory_i : public POA_Quoter::Stock_Factory
{
public:
Quoter_Stock_Factory (PortableServer::POA_ptr stock_factory_poa)
: stock_factory_poa_ (PortableServer::POA::_duplicate (stock_factory_poa))
{}
Quoter::Stock_ptr get_stock (const char *symbol)
throw (Quoter::Invalid_Stock_Symbol);
private:
PortableServer::POA_var stock_factory_poa_;
};

注意,我们复制了POA,这样才遵从惯用的CORBA对输入参数的内存管理规则。尽管构造函数并不是CORBA操作,这样做我们可以把这个规则使用得更广泛,如果我们坚持使用CORBA这套规则时可以减少误解。

get_stock 操作的实现更为有趣。首先我们依据符号创建对象的ID:

Quoter::Stock_ptr
Quoter_Stock_Factory_i::get_stock (const char *symbol)
throw (Quoter::Invalid_Stock_Symbol)
{
PortableServer::ObjectId_var oid =
PortableServer::string_to_ObjectId (symbol);

接下来在POA中查找对象的ID:

try {
CORBA::Object_var tmp =
this->stock_factory_poa_->id_to_reference (oid.in ());

最后将对象的引用narrow到正常的类型并返回它:

return Quoter::Stock::_narrow (tmp.in ());
}

要是符号无效,POA就找不对正确的对象ID,只好抛出异常:

catch (PortableServer::POA::ObjectNotActive &) {
throw Quoter::Invalid_Stock_Symbol ();
}
}

股票对象的内存管理

迄今为止,我们还尚为讨论伺服代码(servants)的内存管理问题。现在是讨论它的最好时机了,因为股票对象已完全授控于POA了。POA为伺服代码提供了引用计数。你并不需要使用引用计数,如果你遵从了这一点,那绝大多数的内存管理就相当的简单了。那为什么不是所有的时候都可以不使用引用计数呢?那是因为有的应用程序不需要它。举例来说,我们先前的简单服务不需要任何复杂的对象管理,于是所有的对象都是在栈(stack)上创建的。

如果想在POA中使用引用计数,你必须重载_add_ref() 和remove_ref() 这两个方法用来增加或减少引用的数目。 一旦数目回到0,你就可以安全的删除对象(但要记住,计数数目是从1开始的!)。

把这些方法实现为线程安全是一件繁琐的工作。为了减化这个工作,我们混合使用了PortableServer::RefCountServantBase 这个作为基类,像这样:

class Quoter_Stock_i
    :  public virtual POA_Quoter::Stock,
       public virtual PortableServer::RefCountServantBase
{
public:
  Quoter_Stock_i (const char *symbol,
                  const char *full_name,
                  CORBA::Double price);
  // as before
};

TAO中PortableServer::RefCountServantBase 的实现也是线程安全的,于是你可以用这个技术在你的多线程服务中动态的销毁对象。您简单的委托POA来控制,一旦所有线程调用了对象中止,当休眠该对象时,POA将会调用 _remove_ref(),这样如果这个对象还在使用中就不会被删除。也请记住如果你要使用对象请增加它的引用记数。

练习

从简单服务的文件作如下修改:

  • Stock_i.h: 使用引用计数控制伺服代码的内存管理。
  • Stock_Factory_i.h: Apply the changes described above to use a child POA with the appropriate policies.
  • Stock_Factory_i.cpp: Apply the changes described above to use a child POA with the appropriate policies.
  • server.cpp: Create the child POA, initialize the stock objects from the stdin, and create the right stock factory class.

你能够使用相同的Quoter.idl, Stock_i.cpp 和MPC 文件.

解决方案

在你的解决方案在同下面的文件作比较:

Does this solution scale when the number of stock symbols is in the thousands or millions? Find out about Servant Locators and Servant Activators in the POA!

Testing

有一个简单的输入文件可用. 你可以使用简单客户端来检查结果:

$ server < stock_list.txt > ior_file
$ client file://ior_file AAAA BBBB MSFT RHAT CCCC

也测试一下无效的符号!

More Reading

The Henning and Vinoski CORBA book discusses POA policies in detail. Likewise, the Schmidt and Vinoski columns in C++ Report also include several articles about the POA. Finally, the TAO distribution includes examples that illustrate how to use the POA policies.

你可能感兴趣的:(多线程,工作,应用服务器)