下面对ACE-6.0.0提供的动态服务配置的实例代码进行分析。
1. DLL代码
Service_Config_DLL.cpp 文件 (在\ACE-6.0.0\ACE_wrappers\tests目录下)
// -*- C++ -*-
//=============================================================================
/**
* @file Service_Config_DLL.cpp
*
* $Id: Service_Config_DLL.cpp 91673 2010-09-08 18:49:47Z johnnyw $
*
* This file is related to, and used with, Service_Config_Test. It's
* used when testing the reentrance/thread-safety of the
* Service Configurator, in addition to testing the Service
* Configurator's ability to handle nested processing of Service
* Configurator directives.
*
* @author Ossama Othman <ossama@uci.edu>
*/
//=============================================================================
#include "Service_Config_DLL.h"
#include "ace/Service_Config.h"
#include "ace/OS_NS_stdio.h"
#include "ace/OS_NS_string.h"
static ACE_THR_FUNC_RETURN
invoke_service_config (void *arg)
{
const ACE_TCHAR *directive = reinterpret_cast<const ACE_TCHAR *> (arg);
// Process a Service Configurator directive in the current thread.
// process_directive 方法分析配置语句,调用DLL的导出函数。
if (ACE_Service_Config::process_directive (directive) != 0)
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("(%P|%t) Service_Config_DLL::svc() - ")
ACE_TEXT ("process_directive() failed for:\n")
ACE_TEXT ("\"%s\"\n"),
directive));
return 0;
}
Service_Config_DLL::Service_Config_DLL (void)
{
ACE_OS::memset (this->directive_[0], 0, BUFSIZ * sizeof (ACE_TCHAR));
ACE_OS::memset (this->directive_[1], 0, BUFSIZ * sizeof (ACE_TCHAR));
}
int
Service_Config_DLL::init (int argc, ACE_TCHAR *argv[])
{
if (argc == 2)
{
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("Loading Test_Object_%s and Test_Object_%s\n"),
argv[0],
argv[1]));
ACE_OS::sprintf (this->directive_[0],
#if (ACE_USES_CLASSIC_SVC_CONF == 1)
ACE_TEXT ("dynamic Test_Object_%s Service_Object * Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_%s\""),
#else
ACE_TEXT ("<?xml version='1.0'?> <dynamic id='Test_Object_%s' type='service_object'> <initializer init='_make_Service_Config_DLL'
path='Service_Config_DLL' params='Test_Object_%s'/> </dynamic>"),
#endif
argv[0],
argv[0]);
ACE_OS::sprintf (this->directive_[1],
#if (ACE_USES_CLASSIC_SVC_CONF == 1)
ACE_TEXT ("dynamic Test_Object_%s Service_Object * Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_%s\""),
#else
ACE_TEXT ("<?xml version='1.0'?> <dynamic id='Test_Object_%s' type='service_object'> <initializer init='_make_Service_Config_DLL'
path='Service_Config_DLL' params='Test_Object_%s'/> </dynamic>"),
#endif
argv[1],
argv[1]);
if (ACE_Service_Config::process_directive (this->directive_[0]) != 0)
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("(%P|%t) Service_Config_DLL::init() - ")
ACE_TEXT ("process_directive() failed for:\n")
ACE_TEXT ("\"%s\": %m\n"),
this->directive_[0]));
#if defined (ACE_HAS_THREADS)
// Become an Active Object if more than one argument passed.
// Two arguments indicate two "test objects" to be dynamically
// loaded.
return this->activate (); // 执行activate才能调用svc函数。
#endif /* ACE_HAS_THREADS */
}
else if (argc == 1)
{
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("Service_Config_DLL::init () %@ - %s\n"),
this,
argv[0]));
}
else
{
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ("(%P|%t) Incorrect number of arguments ")
ACE_TEXT ("(%d) passed to Service_Config_DLL::init ()"),
argc),
-1);
}
return 0;
}
int
Service_Config_DLL::fini (void)
{
return 0;
}
int
Service_Config_DLL::svc (void)
{
// spawn 方法创建线程,invoke_service_config 是线程函数。
if (ACE_Thread_Manager::instance ()->spawn (invoke_service_config,
this->directive_[1]) == -1)
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ("(%P|%t) Unable to spawn thread to ")
ACE_TEXT ("invoke Service Configurator.\n")),
-1);
return 0;
}
// The same class (Service_Config_DLL) is used to implement each of the
// Service Objects whose service descriptors are defined below.
// -----------------------------------------------------------------
ACE_STATIC_SVC_DEFINE (Test_Object_1,
ACE_TEXT ("Test_Object_1"),
ACE_SVC_OBJ_T,
&ACE_SVC_NAME (Service_Config_DLL),
ACE_Service_Type::DELETE_THIS
| ACE_Service_Type::DELETE_OBJ,
0)
// -----------------------------------------------------------------
ACE_STATIC_SVC_DEFINE (Test_Object_2,
ACE_TEXT ("Test_Object_2"),
ACE_SVC_OBJ_T,
&ACE_SVC_NAME (Service_Config_DLL),
ACE_Service_Type::DELETE_THIS
| ACE_Service_Type::DELETE_OBJ,
0)
// -----------------------------------------------------------------
ACE_STATIC_SVC_DEFINE (Test_Object_3,
ACE_TEXT ("Test_Object_3"),
ACE_SVC_OBJ_T,
&ACE_SVC_NAME (Service_Config_DLL),
ACE_Service_Type::DELETE_THIS
| ACE_Service_Type::DELETE_OBJ,
0)
// -----------------------------------------------------------------
ACE_STATIC_SVC_DEFINE (Test_Object_4,
ACE_TEXT ("Test_Object_4"),
ACE_SVC_OBJ_T,
&ACE_SVC_NAME (Service_Config_DLL),
ACE_Service_Type::DELETE_THIS
| ACE_Service_Type::DELETE_OBJ,
0)
// -----------------------------------------------------------------
ACE_STATIC_SVC_DEFINE (Test_Object_5,
ACE_TEXT ("Test_Object_5"),
ACE_SVC_OBJ_T,
&ACE_SVC_NAME (Service_Config_DLL),
ACE_Service_Type::DELETE_THIS
| ACE_Service_Type::DELETE_OBJ,
0)
// -----------------------------------------------------------------
ACE_STATIC_SVC_DEFINE (Test_Object_6,
ACE_TEXT ("Test_Object_6"),
ACE_SVC_OBJ_T,
&ACE_SVC_NAME (Service_Config_DLL),
ACE_Service_Type::DELETE_THIS
| ACE_Service_Type::DELETE_OBJ,
0)
// -----------------------------------------------------------------
ACE_STATIC_SVC_DEFINE (Final_Object,
ACE_TEXT ("Final_Object"),
ACE_SVC_OBJ_T,
&ACE_SVC_NAME (Service_Config_DLL),
ACE_Service_Type::DELETE_THIS
| ACE_Service_Type::DELETE_OBJ,
0)
// -----------------------------------------------------------------
ACE_STATIC_SVC_DEFINE (Test_Object_1_More,
ACE_TEXT ("Test_Object_1_More"),
ACE_SVC_OBJ_T,
&ACE_SVC_NAME (Service_Config_DLL),
ACE_Service_Type::DELETE_THIS
| ACE_Service_Type::DELETE_OBJ,
0)
// -----------------------------------------------------------------
ACE_STATIC_SVC_DEFINE (Test_Object_2_More,
ACE_TEXT ("Test_Object_2_More"),
ACE_SVC_OBJ_T,
&ACE_SVC_NAME (Service_Config_DLL),
ACE_Service_Type::DELETE_THIS
| ACE_Service_Type::DELETE_OBJ,
0)
// -----------------------------------------------------------------
// Same factory is used for all service descriptors defined above.
ACE_FACTORY_DEFINE (Service_Config_DLL, Service_Config_DLL)
/*
** Simple service which will refuse to load/initialize correctly.
** The main program should do:
** 1. Try to load this service (which should fail)
** 2. Try to look up this service and call its info() hook; if info()
** can be called, the test has failed.
** Similarly, if fini() is called later, something is very wrong.
*/
/// Initializes object when dynamic linking occurs.
int
Refuses_Init::init (int, ACE_TCHAR *[])
{
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Refuses_Init::init - refusing to init\n")));
return -1;
}
/// Terminates object when dynamic unlinking occurs.
int
Refuses_Init::fini (void)
{
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Refuses_Init::fini should not be called!\n")));
return 0;
}
/// Returns information on a service object.
int
Refuses_Init::info (ACE_TCHAR **info_string, size_t length) const
{
ACE_TCHAR const *msg =
ACE_TEXT ("Refuses_Init service, shouldn't be here!\n");
if (*info_string == 0)
*info_string = ACE_OS::strdup (msg);
else
ACE_OS::strncpy (*info_string, msg, length);
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Refuses_Init::info() called - shouldn't be\n")));
return ACE_OS::strlen (*info_string);
}
ACE_FACTORY_DEFINE (Service_Config_DLL, Refuses_Init)
2. 宿主程序的代码
Service_Config_Test.cpp 文件 (在\ACE-6.0.0\ACE_wrappers\tests目录下)
// -*- C++ -*-
//=============================================================================
/**
* @file Service_Config_Test.cpp
*
* $Id: Service_Config_Test.cpp 91673 2010-09-08 18:49:47Z johnnyw $
*
* This is a simple test to make sure the ACE Service Configurator
* framework is working correctly.
*
* @author David Levine <levine@cs.wustl.edu>
* @author Ossama Othman <ossama@uci.edu>
*/
//=============================================================================
#include "test_config.h"
#include "ace/OS_NS_stdio.h"
#include "ace/OS_NS_errno.h"
#include "ace/OS_NS_Thread.h"
#include "ace/Log_Msg.h"
#include "ace/Object_Manager.h"
#include "ace/Service_Config.h"
#include "ace/Service_Object.h"
#include "ace/Service_Repository.h"
#include "ace/Service_Types.h"
#include "ace/Reactor.h"
#include "ace/Thread_Manager.h"
#include "ace/ARGV.h"
static const u_int VARIETIES = 3;
static u_int error = 0;
class MyRepository : public ACE_Service_Repository
{
public:
array_type& array ()
{
return service_array_;
}
};
/**
* @class Test_Singleton
*
* @brief Test the Singleton
*
* This should be a template class, with singleton instantiations.
* But to avoid having to deal with compilers that want template
* declarations in separate files, it's just a plain class. The
* instance argument differentiates the "singleton" instances. It
* also demonstrates the use of the param arg to the cleanup ()
* function.
*/
class Test_Singleton
{
public:
static Test_Singleton *instance (u_short variety);
~Test_Singleton (void);
private:
u_short variety_;
static u_short current_;
Test_Singleton (u_short variety);
friend class misspelled_verbase_friend_declaration_to_avoid_compiler_warning_with_private_ctor;
};
u_short Test_Singleton::current_ = 0;
extern "C" void
test_singleton_cleanup (void *object, void *)
{
// We can't reliably use ACE_Log_Msg in a cleanup hook. Yet.
/* ACE_DEBUG ((LM_DEBUG, "cleanup %d\n", (u_short) param)); */
delete (Test_Singleton *) object;
}
Test_Singleton *
Test_Singleton::instance (u_short variety)
{
static Test_Singleton *instances[VARIETIES] = { 0 };
if (instances[variety] == 0)
{
ACE_NEW_RETURN (instances[variety],
Test_Singleton (variety),
0);
}
ACE_Object_Manager::at_exit (instances[variety],
test_singleton_cleanup,
reinterpret_cast<void *> (static_cast<size_t> (variety)),
"Test_Singleton");
return instances[variety];
}
Test_Singleton::Test_Singleton (u_short variety)
: variety_ (variety)
{
if (variety_ != current_++)
{
ACE_DEBUG ((LM_ERROR,
ACE_TEXT ("ERROR: instance %u created out of order!\n"),
variety_));
++error;
}
}
// We can't reliably use ACE_Log_Msg in a destructor that is called by
// ACE_Object_Manager. Yet.
Test_Singleton::~Test_Singleton (void)
{
/* ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test_Singleton %u dtor\n"), variety_)); */
if (variety_ != --current_)
{
ACE_OS::fprintf (stderr,
ACE_TEXT ("ERROR: instance %u destroyed out of order!\n"),
variety_);
/* ACE_DEBUG ((LM_ERROR, ACE_TEXT ("ERROR: instance %u destroyed out of order!\n"),
variety_)); */
++error;
}
}
void
testFailedServiceInit (int, ACE_TCHAR *[])
{
static const ACE_TCHAR *refuse_svc =
#if (ACE_USES_CLASSIC_SVC_CONF == 1)
ACE_TEXT ("dynamic Refuses_Svc Service_Object * ")
ACE_TEXT (" Service_Config_DLL:_make_Refuses_Init() \"\"")
#else
ACE_TEXT ("<dynamic id=\"Refuses_Svc\" type=\"Service_Object\">")
ACE_TEXT (" <initializer init=\"_make_Refuses_Init\" path=\"Service_Config_DLL\" params=\"\"/>")
ACE_TEXT ("</dynamic>")
#endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */
;
int error_count = 0;
if ((error_count = ACE_Service_Config::process_directive (refuse_svc)) != 1)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Failed init test should have returned 1; ")
ACE_TEXT ("returned %d instead\n"),
error_count));
}
// Try to find the service; it should not be there.
ACE_Service_Type const *svcp = 0;
if (-1 != ACE_Service_Repository::instance ()->find (ACE_TEXT ("Refuses_Svc"),
&svcp))
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Found service repo entry for Refuses_Svc\n")));
ACE_Service_Type_Impl const *svc_impl = svcp->type ();
ACE_TCHAR msg[1024];
ACE_TCHAR *msgp = msg;
if (svc_impl->info (&msgp, sizeof (msg) / sizeof (ACE_TCHAR)) > 0)
ACE_ERROR ((LM_ERROR, ACE_TEXT ("Refuses_Svc said: %s\n"), msg));
}
else
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("Repo reports no Refuses_Svc; correct.\n")));
}
void
testLoadingServiceConfFileAndProcessNo (int argc, ACE_TCHAR *argv[])
{
ACE_ARGV new_argv;
#if defined (ACE_USES_WCHAR)
// When using full Unicode support, use the version of the Service
// Configurator file appropriate to the platform.
// For example, Windows Unicode uses UTF-16.
//
// iconv(1) found on Linux and Solaris, for example, can
// be used to convert between encodings.
//
// Byte ordering is also an issue, so we should be
// generating this file on-the-fly from the UTF-8 encoded
// file by using functions like iconv(1) or iconv(3).
# if defined (ACE_WIN32)
const ACE_TCHAR svc_conf[] =
ACE_TEXT ("Service_Config_Test.UTF-16")
ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT);
# else
const ACE_TCHAR svc_conf[] =
ACE_TEXT ("Service_Config_Test.WCHAR_T")
ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT);
# endif /* ACE_WIN32 */
#else
// ASCII (UTF-8) encoded Service Configurator file.
const ACE_TCHAR svc_conf[] =
ACE_TEXT ("Service_Config_Test")
ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT);
#endif /* ACE_USES_WCHAR */
// Process the Service Configurator directives in this test's Making
// sure we have more than one option with an argument, to capture
// any errors caused by "reshuffling" of the options.
if (new_argv.add (argv) == -1
|| new_argv.add (ACE_TEXT ("-d")) == -1
|| new_argv.add (ACE_TEXT ("-k")) == -1
|| new_argv.add (ACE_TEXT ("xxx")) == -1
|| new_argv.add (ACE_TEXT ("-p")) == -1
|| new_argv.add (ACE_TEXT ("Service_Config_Test.pid")) == -1
|| new_argv.add (ACE_TEXT ("-f")) == -1
|| new_argv.add (svc_conf) == -1)
{
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("line %l %p\n"),
ACE_TEXT ("new_argv.add")));
++error;
}
// We need this scope to make sure that the destructor for the
// <ACE_Service_Config> gets called.
ACE_Service_Config daemon;
if (daemon.open (new_argv.argc (), new_argv.argv ()) == -1 &&
errno != ENOENT)
{
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("line %l %p\n"),
ACE_TEXT ("daemon.open")));
++error;
}
ACE_Time_Value tv (argc > 1 ? ACE_OS::atoi (argv[1]) : 2);
if (ACE_Reactor::instance()->run_reactor_event_loop (tv) == -1)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("line %l %p\n"),
ACE_TEXT ("run_reactor_event_loop")));
}
// Wait for all threads to complete.
ACE_Thread_Manager::instance ()->wait ();
}
void
testLoadingServiceConfFile (int argc, ACE_TCHAR *argv[])
{
ACE_ARGV new_argv;
#if defined (ACE_USES_WCHAR)
// When using full Unicode support, use the version of the Service
// Configurator file appropriate to the platform.
// For example, Windows Unicode uses UTF-16.
//
// iconv(1) found on Linux and Solaris, for example, can
// be used to convert between encodings.
//
// Byte ordering is also an issue, so we should be
// generating this file on-the-fly from the UTF-8 encoded
// file by using functions like iconv(1) or iconv(3).
# if defined (ACE_WIN32)
const ACE_TCHAR svc_conf[] =
ACE_TEXT ("Service_Config_Test.UTF-16")
ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT);
# else
const ACE_TCHAR svc_conf[] =
ACE_TEXT ("Service_Config_Test.WCHAR_T")
ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT);
# endif /* ACE_WIN32 */
#else
// ASCII (UTF-8) encoded Service Configurator file.
// 确定配置文件名称:
const ACE_TCHAR svc_conf[] =
ACE_TEXT ("Service_Config_Test")
ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT);
#endif /* ACE_USES_WCHAR */
// Process the Service Configurator directives in this test's
// 把命令行参数加入到一个ACE参数对象中,便于后面分析,把配置文件作为参数也加入其中。
if (new_argv.add (argv) == -1
|| new_argv.add (ACE_TEXT ("-f")) == -1
|| new_argv.add (svc_conf) == -1)
{
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("line %l %p\n"),
ACE_TEXT ("new_argv.add")));
++error;
}
// We need this scope to make sure that the destructor for the
// <ACE_Service_Config> gets called.
ACE_Service_Config daemon;
// open方法解析配置文件。
if (daemon.open (new_argv.argc (), new_argv.argv ()) == -1)
{
if (errno == ENOENT)
ACE_DEBUG ((LM_WARNING,
ACE_TEXT ("ACE_Service_Config::open: %p\n"),
svc_conf));
else
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("ACE_Service_Config::open: %p\n"),
ACE_TEXT ("error")));
}
ACE_Time_Value tv (argc > 1 ? ACE_OS::atoi (argv[1]) : 2);
if (ACE_Reactor::instance()->run_reactor_event_loop (tv) == -1)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("line %l %p\n"),
ACE_TEXT ("run_reactor_event_loop")));
}
// Wait for all threads to complete.
// 等待进程内的其他线程退出。
ACE_Thread_Manager::instance ()->wait ();
}
// Loading and unloading the ACE logger service should not smash singletons.
void
testUnloadingACELoggingStrategy (int, ACE_TCHAR *[])
{
static const ACE_TCHAR *load_logger =
#if (ACE_USES_CLASSIC_SVC_CONF == 1)
ACE_TEXT ("dynamic Logger Service_Object * ")
ACE_TEXT (" ACE:_make_ACE_Logging_Strategy() \"\"")
#else
ACE_TEXT ("<dynamic id=\"Logger\" type=\"Service_Object\">")
ACE_TEXT (" <initializer init=\"_make_ACE_Logging_Strategy\" ")
ACE_TEXT (" path=\"ACE\" params=\"\"/>")
ACE_TEXT ("</dynamic>")
#endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */
;
static const ACE_TCHAR *unload_logger =
#if (ACE_USES_CLASSIC_SVC_CONF == 1)
ACE_TEXT ("remove Logger")
#else
ACE_TEXT ("<remove id=\"Logger\" />");
#endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */
;
ACE_Reactor *r1 = ACE_Reactor::instance();
// Ensure no errors are logged while searching for a valid ACE lib name;
// these skew the scoreboard results.
u_long mask = ACE_LOG_MSG->priority_mask (ACE_Log_Msg::PROCESS);
ACE_LOG_MSG->priority_mask (mask & ~LM_ERROR, ACE_Log_Msg::PROCESS);
ACE_DEBUG ((LM_DEBUG, "Was %x, now %x\n", mask, mask & ~LM_ERROR));
int error_count = ACE_Service_Config::process_directive (load_logger);
ACE_LOG_MSG->priority_mask (mask);
if (error_count != 0)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Load ACE Logger should have returned 0; ")
ACE_TEXT ("returned %d instead\n"),
error_count));
}
ACE_Service_Config::process_directive (unload_logger);
ACE_Reactor *r2 = ACE_Reactor::instance ();
if (r1 != r2)
ACE_ERROR ((LM_ERROR, ACE_TEXT ("Reactor before %@, after %@\n"), r1, r2));
}
// @brief The size of a repository is unlimited and can be exceeded
void
testLimits (int , ACE_TCHAR *[])
{
static const ACE_TCHAR *svc_desc1 =
#if (ACE_USES_CLASSIC_SVC_CONF == 1)
ACE_TEXT ("dynamic Test_Object_1_More Service_Object * ")
ACE_TEXT (" Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_1_More\"")
#else
ACE_TEXT ("<dynamic id=\"Test_Object_1_More\" type=\"Service_Object\">")
ACE_TEXT (" <initializer init=\"_make_Service_Config_DLL\" path=\"Service_Config_DLL\" params=\"Test_Object_1_More\"/>")
ACE_TEXT ("</dynamic>")
#endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */
;
static const ACE_TCHAR *svc_desc2 =
#if (ACE_USES_CLASSIC_SVC_CONF == 1)
ACE_TEXT ("dynamic Test_Object_2_More Service_Object * ")
ACE_TEXT (" Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_2_More\"")
#else
ACE_TEXT ("<dynamic id=\"Test_Object_2_More\" type=\"Service_Object\">")
ACE_TEXT (" <initializer init=\"_make_Service_Config_DLL\" path=\"Service_Config_DLL\" params=\"Test_Object_2_More\"/>")
ACE_TEXT ("</dynamic>")
#endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */
;
u_int error0 = error;
// Ensure enough room for one in a own, the repository can extend
ACE_Service_Gestalt one (1, true);
// Add two.
// We cant simply rely on the fact that insertion fails, because it
// is typical to have no easy way of getting detailed error
// information from a parser.
one.process_directive (svc_desc1);
one.process_directive (svc_desc2);
if (-1 == one.find (ACE_TEXT ("Test_Object_1_More"), 0, 0))
{
++error;
ACE_ERROR ((LM_ERROR, ACE_TEXT("Expected to have registered the first service\n")));
}
if (-1 == one.find (ACE_TEXT ("Test_Object_2_More"), 0, 0))
{
++error;
ACE_ERROR ((LM_ERROR, ACE_TEXT("Expected to have registered the second service\n")));
}
ACE_Service_Repository_Iterator sri (*one.current_service_repository (), 0);
size_t index = 0;
for (const ACE_Service_Type *sr;
sri.next (sr) != 0;
sri.advance ())
{
if (index == 0 && ACE_OS::strcmp (sr->name(), ACE_TEXT ("Test_Object_1_More")) != 0)
{
++error;
ACE_ERROR ((LM_ERROR, ACE_TEXT("Service 1 is wrong\n")));
}
if (index == 1 && ACE_OS::strcmp (sr->name(), ACE_TEXT ("Test_Object_2_More")) != 0)
{
++error;
ACE_ERROR ((LM_ERROR, ACE_TEXT("Service 2 is wrong\n")));
}
++index;
}
// Test close
one.current_service_repository ()->close();
if (one.current_service_repository ()->current_size () != 0)
{
++error;
ACE_ERROR ((LM_ERROR, ACE_TEXT("Size of repository should be 0\n")));
}
if (error == error0)
ACE_DEBUG ((LM_DEBUG, ACE_TEXT("Limits test completed successfully\n")));
else
ACE_DEBUG ((LM_DEBUG, ACE_TEXT("Limits test failed\n")));
}
void
testrepository (int, ACE_TCHAR *[])
{
MyRepository repository;
ACE_DLL handle;
ACE_Service_Type s0 (ACE_TEXT ("0"), 0, handle, false);
ACE_Service_Type s1 (ACE_TEXT ("1"), 0, handle, false);
ACE_Service_Type s2 (ACE_TEXT ("2"), 0, handle, false);
ACE_Service_Type s3 (ACE_TEXT ("3"), 0, handle, false);
ACE_Service_Type* result = 0;
repository.insert (&s0);
if (repository.current_size () != 1)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Repository was wrong size %d\n"),
repository.current_size ()));
}
repository.insert (&s1);
if (repository.current_size () != 2)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Repository was wrong size %d\n"),
repository.current_size ()));
}
repository.insert (&s2);
if (repository.current_size () != 3)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Repository was wrong size %d\n"),
repository.current_size ()));
}
if (repository.remove (ACE_TEXT ("1"), &result) != 0)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Remove failed\n")));
}
if (repository.current_size () != 3)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Repository was wrong size %d\n"),
repository.current_size ()));
}
if (repository.array ()[1] != 0)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Element 1 not zero\n"),
repository.current_size ()));
}
repository.insert (&s3);
if (repository.current_size () != 4)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Repository was wrong size %d\n"),
repository.current_size ()));
}
repository.remove (ACE_TEXT ("0"), &result);
if (repository.remove (ACE_TEXT ("1"), &result) != -1)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Double remove didn't return -1\n")));
}
repository.remove (ACE_TEXT ("2"), &result);
repository.remove (ACE_TEXT ("3"), &result);
if (repository.current_size () != 4)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Repository was wrong size %d\n"),
repository.current_size ()));
}
repository.close ();
if (repository.current_size () != 0)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Repository was wrong size %d\n"),
repository.current_size ()));
}
}
// @brief ??
void
testOrderlyInstantiation (int , ACE_TCHAR *[])
{
for (u_int i = 0; i < VARIETIES; ++i)
{
Test_Singleton *s = Test_Singleton::instance (i);
if (s == 0)
{
++error;
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("instance () allocate failed!\n")));
}
}
}
// This test verifies that services loaded by a normal ACE startup can be
// located from a thread spawned outside of ACE's control.
//
// To do this, we need a native thread entry and, thus, it needs special care
// for each platform type. Feel free to add more platforms as needed here and
// in main() where the test is called.
#if defined (ACE_HAS_WTHREADS) || defined (ACE_HAS_PTHREADS_STD)
# if defined (ACE_HAS_WTHREADS)
extern "C" unsigned int __stdcall
# else
extern "C" ACE_THR_FUNC_RETURN
# endif
nonacethreadentry (void *args)
{
ACE_Log_Msg::inherit_hook (0, *(ACE_OS_Log_Msg_Attributes *)args);
if (ACE_Service_Config::instance()->find(ACE_TEXT("Test_Object_1_Thr")) != 0)
{
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("In thr %t cannot find Test_Object_1_Thr ")
ACE_TEXT ("via ACE_Service_Config\n")));
++error;
}
else
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("In thr %t, located Test_Object_1_Thr ")
ACE_TEXT ("via ACE_Service_Config\n")));
if (0 != ACE_Service_Repository::instance()->find
(ACE_TEXT("Test_Object_1_Thr")))
{
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("In thr %t cannot find Test_Object_1_Thr ")
ACE_TEXT ("via ACE_Service_Repository\n")));
++error;
}
else
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("In thr %t, located Test_Object_1_Thr ")
ACE_TEXT ("via ACE_Service_Repository\n")));
return 0;
}
void
testNonACEThread ()
{
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Beginning non-ACE thread lookup test\n")));
static const ACE_TCHAR *svc_desc =
#if (ACE_USES_CLASSIC_SVC_CONF == 1)
ACE_TEXT ("dynamic Test_Object_1_Thr Service_Object * ")
ACE_TEXT (" Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_1_Thr\"")
#else
ACE_TEXT ("<dynamic id=\"Test_Object_1_Thr\" type=\"Service_Object\">")
ACE_TEXT (" <initializer init=\"_make_Service_Config_DLL\" path=\"Service_Config_DLL\" params=\"Test_Object_1_Thr\"/>")
ACE_TEXT ("</dynamic>")
#endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */
;
static const ACE_TCHAR *svc_remove =
#if (ACE_USES_CLASSIC_SVC_CONF == 1)
ACE_TEXT ("remove Test_Object_1_Thr")
#else
ACE_TEXT ("<remove id=\"Test_Object_1_Thr\"/>")
ACE_TEXT ("</remove>")
#endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */
;
if (-1 == ACE_Service_Config::process_directive (svc_desc))
{
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("%p\n"),
ACE_TEXT ("Error loading service")));
++error;
return;
}
// Allow the spawned thread to contribute to the logging output.
ACE_OS_Log_Msg_Attributes log_msg_attrs;
ACE_Log_Msg::init_hook (log_msg_attrs);
u_int errors_before = error;
#if defined (ACE_HAS_WTHREADS) && !defined (ACE_HAS_WINCE)
HANDLE thr_h = (HANDLE)_beginthreadex (0,
0,
&nonacethreadentry,
&log_msg_attrs,
0,
0);
if (thr_h == 0)
{
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("_beginthreadex")));
}
else
{
WaitForSingleObject (thr_h, INFINITE);
CloseHandle (thr_h);
}
#elif defined (ACE_HAS_PTHREADS_STD)
pthread_t thr_id;
int status = pthread_create (&thr_id, 0, nonacethreadentry, &log_msg_attrs);
if (status != 0)
{
errno = status;
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("pthread_create")));
}
else
{
pthread_join (thr_id, 0);
}
#endif
if (error != errors_before) // The test failed; see if we can still see it
{
if (0 != ACE_Service_Config::instance()->find
(ACE_TEXT ("Test_Object_1_Thr")))
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Main thr %t cannot find Test_Object_1_Thr\n")));
else
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("Main thr %t DOES find Test_Object_1_Thr\n")));
}
if (-1 == ACE_Service_Config::process_directive (svc_remove))
{
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("%p\n"),
ACE_TEXT ("Error removing service")));
++error;
return;
}
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Non-ACE thread lookup test completed\n")));
}
#endif /* ACE_HAS_WTHREADS || ACE_HAS_PTHREADS_STD */
int
run_main (int argc, ACE_TCHAR *argv[])
{
ACE_START_TEST (ACE_TEXT ("Service_Config_Test"));
//[] testOrderlyInstantiation (argc, argv); // 先屏掉这句
//[] testFailedServiceInit (argc, argv); // 先屏掉这句
testLoadingServiceConfFile (argc, argv); // 只留这句测试
//[] testLoadingServiceConfFileAndProcessNo (argc, argv); // 先屏掉这句
//[] testUnloadingACELoggingStrategy (argc, argv); // 先屏掉这句
//[] testLimits (argc, argv); // 先屏掉这句
//[] testrepository (argc, argv); // 先屏掉这句
#if defined (ACE_HAS_WTHREADS) || defined (ACE_HAS_PTHREADS_STD)
//[] testNonACEThread(); // 先屏掉这句
#endif
ACE_END_TEST;
getchar(); // 加这句让窗口停留,便于查看输出内容。
return error;
}
3. 配置文件
Service_Config_Test.conf 文件 (在\ACE-6.0.0\ACE_wrappers\tests目录下)
# Dynamically loading each of the Service Objects below causes a
# number of threads to be spawned, each one invoking the Service
# Configurator (e.g. ACE_Service_Config::process_directive(). If the
# Service Configurator is thread safe and reentrant, then parsing of
# this `Service_Config_Test.conf' file should run to completion
# without error.
#
# Test_Object_1 will cause Test_Object_2 and Test_Object_3 to be
# dynamically loaded. Dynamic loading of each of object will occur in
# a separate thread.
dynamic Test_Object_1 Service_Object * Service_Config_DLL:_make_Service_Config_DLL() "2 3" // 引号内是参数
# Test_Object_4 will cause Test_Object_5 and Test_Object_6 to be
# dynamically loaded. Dynamic loading of each of object will occur in
# a separate thread.
dynamic Test_Object_4 Service_Object * Service_Config_DLL:_make_Service_Config_DLL() "5 6" // 引号内是参数
# Final_Object does nothing but print a completion message.
dynamic Final_Object Service_Object * Service_Config_DLL:_make_Service_Config_DLL() "FINAL" // 引号内是参数