Refactored Application class to use DynamicObject as a base class.

This commit is contained in:
Gunnar Beutner 2012-08-14 12:51:51 +02:00
parent 8b87e30197
commit 160219f4d3
10 changed files with 153 additions and 119 deletions

View File

@ -18,13 +18,14 @@
******************************************************************************/
#include "i2-base.h"
#ifndef _WIN32
# include <ltdl.h>
#endif /* _WIN32 */
using namespace icinga;
Application::Ptr Application::m_Instance;
Application *Application::m_Instance = NULL;
bool Application::m_ShuttingDown = false;
bool Application::m_Debugging = false;
boost::thread::id Application::m_MainThreadID;
@ -32,9 +33,12 @@ boost::thread::id Application::m_MainThreadID;
/**
* Constructor for the Application class.
*/
Application::Application(void)
: m_PidFile(NULL)
Application::Application(const Dictionary::Ptr& serializedUpdate)
: DynamicObject(serializedUpdate), m_PidFile(NULL)
{
if (!IsLocal())
throw_exception(runtime_error("Application objects must be local."));
#ifdef _WIN32
/* disable GUI-based error messages for LoadLibrary() */
SetErrorMode(SEM_FAILCRITICALERRORS);
@ -42,8 +46,6 @@ Application::Application(void)
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
throw_exception(Win32Exception("WSAStartup failed", WSAGetLastError()));
#else /* _WIN32 */
lt_dlinit();
#endif /* _WIN32 */
char *debugging = getenv("_DEBUG");
@ -53,6 +55,9 @@ Application::Application(void)
if (IsDebuggerPresent())
m_Debugging = true;
#endif /* _WIN32 */
assert(m_Instance == NULL);
m_Instance = this;
}
/**
@ -60,6 +65,8 @@ Application::Application(void)
*/
Application::~Application(void)
{
m_Instance = NULL;
m_ShuttingDown = true;
DynamicObject::DeactivateObjects();
@ -78,7 +85,10 @@ Application::~Application(void)
*/
Application::Ptr Application::GetInstance(void)
{
return m_Instance;
if (m_Instance)
return m_Instance->GetSelf();
else
return Application::Ptr();
}
/**
@ -130,20 +140,14 @@ void Application::Shutdown(void)
/**
* Retrieves the full path of the executable.
*
* @param argv0 The first command-line argument.
* @returns The path.
*/
String Application::GetExePath(void) const
String Application::GetExePath(const String& argv0)
{
static String result;
if (!result.IsEmpty())
return result;
String executablePath;
#ifndef _WIN32
String argv0 = m_Arguments[0];
char buffer[MAXPATHLEN];
if (getcwd(buffer, sizeof(buffer)) == NULL)
throw_exception(PosixException("getcwd failed", errno));
@ -189,17 +193,15 @@ String Application::GetExePath(void) const
if (realpath(executablePath.CStr(), buffer) == NULL)
throw_exception(PosixException("realpath failed", errno));
result = buffer;
return buffer;
#else /* _WIN32 */
char FullExePath[MAXPATHLEN];
if (!GetModuleFileName(NULL, FullExePath, sizeof(FullExePath)))
throw_exception(Win32Exception("GetModuleFileName() failed", GetLastError()));
result = FullExePath;
return FullExePath;
#endif /* _WIN32 */
return result;
}
/**
@ -217,6 +219,11 @@ bool Application::IsMainThread(void)
return (boost::this_thread::get_id() == m_MainThreadID);
}
void Application::SetMainThread(void)
{
m_MainThreadID = boost::this_thread::get_id();
}
#ifndef _WIN32
/**
* Signal handler for SIGINT. Prepares the application for cleanly
@ -270,12 +277,6 @@ int Application::Run(int argc, char **argv)
{
int result;
assert(!Application::m_Instance);
m_MainThreadID = boost::this_thread::get_id();
Application::m_Instance = GetSelf();
#ifndef _WIN32
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
@ -294,28 +295,24 @@ int Application::Run(int argc, char **argv)
DynamicObject::BeginTx();
if (IsDebugging()) {
try {
result = Main(m_Arguments);
Application::m_Instance.reset();
} else {
try {
result = Main(m_Arguments);
DynamicObject::FinishTx();
DynamicObject::DeactivateObjects();
Application::m_Instance.reset();
} catch (const exception& ex) {
Application::m_Instance.reset();
assert(m_Instance == NULL);
} catch (const exception& ex) {
Logger::Write(LogCritical, "base", "---");
Logger::Write(LogCritical, "base", "Exception: " + Utility::GetTypeName(typeid(ex)));
Logger::Write(LogCritical, "base", "Message: " + String(ex.what()));
Logger::Write(LogCritical, "base", "---");
Logger::Write(LogCritical, "base", "Exception: " + Utility::GetTypeName(typeid(ex)));
Logger::Write(LogCritical, "base", "Message: " + String(ex.what()));
if (IsDebugging())
throw;
return EXIT_FAILURE;
}
return EXIT_FAILURE;
}
DynamicObject::FinishTx();
return result;
}

View File

@ -29,12 +29,12 @@ class Component;
*
* @ingroup base
*/
class I2_BASE_API Application : public Object {
class I2_BASE_API Application : public DynamicObject {
public:
typedef shared_ptr<Application> Ptr;
typedef weak_ptr<Application> WeakPtr;
Application(void);
Application(const Dictionary::Ptr& serializedUpdate);
~Application(void);
static Application::Ptr GetInstance(void);
@ -48,16 +48,18 @@ public:
static bool IsDebugging(void);
static bool IsMainThread(void);
static void SetMainThread(void);
void UpdatePidFile(const String& filename);
void ClosePidFile(void);
static String GetExePath(const String& argv0);
protected:
void RunEventLoop(void);
String GetExePath(void) const;
private:
static Application::Ptr m_Instance; /**< The application instance. */
static Application *m_Instance; /**< The application instance. */
static bool m_ShuttingDown; /**< Whether the application is in the process of
shutting down. */

View File

@ -473,6 +473,11 @@ DynamicObject::ClassMap& DynamicObject::GetClasses(void)
return classes;
}
bool DynamicObject::ClassExists(const String& name)
{
return (GetClasses().find(name) != GetClasses().end());
}
void DynamicObject::RegisterClass(const String& type, DynamicObject::Factory factory)
{
if (GetObjects(type).first != GetObjects(type).second)

View File

@ -151,7 +151,8 @@ class RegisterClassHelper
public:
RegisterClassHelper(const String& name, DynamicObject::Factory factory)
{
DynamicObject::RegisterClass(name, factory);
if (!DynamicObject::ClassExists(name))
DynamicObject::RegisterClass(name, factory);
}
};

View File

@ -110,13 +110,20 @@ LogSeverity Logger::GetMinSeverity(void) const
*/
void Logger::ForwardLogEntry(const LogEntry& entry)
{
bool processed = false;
DynamicObject::Ptr object;
BOOST_FOREACH(tie(tuples::ignore, object), DynamicObject::GetObjects("Logger")) {
Logger::Ptr logger = dynamic_pointer_cast<Logger>(object);
if (entry.Severity >= logger->GetMinSeverity())
logger->m_Impl->ProcessLogEntry(entry);
processed = true;
}
if (!processed && entry.Severity >= LogInformation)
StreamLogger::ProcessLogEntry(std::cout, entry);
}
String Logger::SeverityToString(LogSeverity severity)

View File

@ -49,9 +49,10 @@ void StreamLogger::OpenFile(const String& filename)
/**
* Processes a log entry and outputs it to a stream.
*
* @param stream The output stream.
* @param entry The log entry.
*/
void StreamLogger::ProcessLogEntry(const LogEntry& entry)
void StreamLogger::ProcessLogEntry(std::ostream& stream, const LogEntry& entry)
{
char timestamp[100];
@ -60,7 +61,18 @@ void StreamLogger::ProcessLogEntry(const LogEntry& entry)
strftime(timestamp, sizeof(timestamp), "%Y/%m/%d %H:%M:%S", &tmnow);
*m_Stream << "[" << timestamp << "] "
stream << "[" << timestamp << "] "
<< Logger::SeverityToString(entry.Severity) << "/" << entry.Facility << ": "
<< entry.Message << std::endl;
}
/**
* Processes a log entry and outputs it to a stream.
*
* @param entry The log entry.
*/
void StreamLogger::ProcessLogEntry(const LogEntry& entry)
{
ProcessLogEntry(*m_Stream, entry);
}

View File

@ -18,6 +18,9 @@ public:
~StreamLogger(void);
void OpenFile(const String& filename);
static void ProcessLogEntry(std::ostream& stream, const LogEntry& entry);
protected:
virtual void ProcessLogEntry(const LogEntry& entry);

View File

@ -20,6 +20,9 @@
#include <i2-icinga.h>
#ifndef _WIN32
# include "icinga-version.h"
# define ICINGA_VERSION GIT_MESSAGE
# include <ltdl.h>
#endif /* _WIN32 */
@ -38,6 +41,48 @@ int main(int argc, char **argv)
LTDL_SET_PRELOADED_SYMBOLS();
#endif /* _WIN32 */
IcingaApplication::Ptr instance = boost::make_shared<IcingaApplication>();
return instance->Run(argc, argv);
#ifndef _WIN32
lt_dlinit();
#endif /* _WIN32 */
Application::SetMainThread();
#ifdef _WIN32
Logger::Write(LogInformation, "icinga", "Icinga application loader");
#else /* _WIN32 */
Logger::Write(LogInformation, "icinga", "Icinga application loader (version: " ICINGA_VERSION ")");
#endif /* _WIN32 */
if (argc < 3 || strcmp(argv[1], "-c") != 0) {
stringstream msgbuf;
msgbuf << "Syntax: " << argv[0] << " -c <config-file> ...";
Logger::Write(LogInformation, "base", msgbuf.str());
return EXIT_FAILURE;
}
String configFile = argv[2];
String componentDirectory = Utility::DirName(Application::GetExePath(argv[0])) + "/../lib/icinga2";
Component::AddSearchDir(componentDirectory);
DynamicObject::BeginTx();
/* load config file */
vector<ConfigItem::Ptr> configItems = ConfigCompiler::CompileFile(configFile);
Logger::Write(LogInformation, "icinga", "Executing config items...");
BOOST_FOREACH(const ConfigItem::Ptr& item, configItems) {
item->Commit();
}
DynamicObject::FinishTx();
Application::Ptr app = Application::GetInstance();
if (!app)
throw_exception(runtime_error("Configuration must create an Application object."));
return app->Run(argc - 2, &(argv[2]));
}

View File

@ -31,8 +31,25 @@ using namespace icinga;
const String IcingaApplication::DefaultPidPath = "icinga.pid";
const String IcingaApplication::DefaultStatePath = "icinga.state";
IcingaApplication::IcingaApplication(void)
{ }
IcingaApplication::IcingaApplication(const Dictionary::Ptr& serializedUpdate)
: Application(serializedUpdate)
{
/* load cibsync config component */
ConfigItemBuilder::Ptr cibsyncComponentConfig = boost::make_shared<ConfigItemBuilder>();
cibsyncComponentConfig->SetType("Component");
cibsyncComponentConfig->SetName("cibsync");
cibsyncComponentConfig->SetLocal(true);
cibsyncComponentConfig->Compile()->Commit();
cibsyncComponentConfig.reset();
/* load convenience config component */
ConfigItemBuilder::Ptr convenienceComponentConfig = boost::make_shared<ConfigItemBuilder>();
convenienceComponentConfig->SetType("Component");
convenienceComponentConfig->SetName("convenience");
convenienceComponentConfig->SetLocal(true);
convenienceComponentConfig->Compile()->Commit();
convenienceComponentConfig.reset();
}
/**
* The entry point for the Icinga application.
@ -42,26 +59,13 @@ IcingaApplication::IcingaApplication(void)
*/
int IcingaApplication::Main(const vector<String>& args)
{
/* create console logger */
ConfigItemBuilder::Ptr consoleLogConfig = boost::make_shared<ConfigItemBuilder>();
consoleLogConfig->SetType("Logger");
consoleLogConfig->SetName("console");
consoleLogConfig->SetLocal(true);
consoleLogConfig->AddExpression("type", OperatorSet, "console");
consoleLogConfig->Compile()->Commit();
consoleLogConfig.reset();
#ifdef _WIN32
Logger::Write(LogInformation, "icinga", "Icinga component loader");
#else /* _WIN32 */
Logger::Write(LogInformation, "icinga", "Icinga component loader (version: " ICINGA_VERSION ")");
#endif /* _WIN32 */
Logger::Write(LogInformation, "icinga", "In IcingaApplication::Main()");
m_StartTime = Utility::GetTime();
if (args.size() < 2) {
if (args.size() == 1 && args[0] == "--help") {
stringstream msgbuf;
msgbuf << "Syntax: " << args[0] << " [-S] [-L logfile] [-d] [--] <config-file>";
msgbuf << "Syntax: " << args[0] << " ... -d";
Logger::Write(LogInformation, "icinga", msgbuf.str());
return EXIT_FAILURE;
}
@ -92,68 +96,24 @@ int IcingaApplication::Main(const vector<String>& args)
throw_exception(invalid_argument("Unknown option: " + arg));
}
}
configFile = arg;
if (it + 1 != args.end())
throw_exception(invalid_argument("Trailing command line arguments after config filename."));
}
if (configFile.IsEmpty())
throw_exception(invalid_argument("No config file was specified on the command line."));
m_CertificateFile = Get("cert");
m_CAFile = Get("ca");
m_Node = Get("node");
m_Service = Get("service");
String componentDirectory = Utility::DirName(GetExePath()) + "/../lib/icinga2";
Component::AddSearchDir(componentDirectory);
/* load cibsync config component */
ConfigItemBuilder::Ptr cibsyncComponentConfig = boost::make_shared<ConfigItemBuilder>();
cibsyncComponentConfig->SetType("Component");
cibsyncComponentConfig->SetName("cibsync");
cibsyncComponentConfig->SetLocal(true);
cibsyncComponentConfig->Compile()->Commit();
cibsyncComponentConfig.reset();
/* load convenience config component */
ConfigItemBuilder::Ptr convenienceComponentConfig = boost::make_shared<ConfigItemBuilder>();
convenienceComponentConfig->SetType("Component");
convenienceComponentConfig->SetName("convenience");
convenienceComponentConfig->SetLocal(true);
convenienceComponentConfig->Compile()->Commit();
convenienceComponentConfig.reset();
/* load config file */
vector<ConfigItem::Ptr> configItems = ConfigCompiler::CompileFile(configFile);
Logger::Write(LogInformation, "icinga", "Executing config items...");
BOOST_FOREACH(const ConfigItem::Ptr& item, configItems) {
item->Commit();
}
DynamicObject::Ptr icingaConfig = DynamicObject::GetObject("Application", "icinga");
if (!icingaConfig)
throw_exception(runtime_error("Configuration must contain an 'Application' object named 'icinga'."));
if (!icingaConfig->IsLocal())
throw_exception(runtime_error("'icinga' application object must be 'local'."));
m_CertificateFile = icingaConfig->Get("cert");
m_CAFile = icingaConfig->Get("ca");
m_Node = icingaConfig->Get("node");
m_Service = icingaConfig->Get("service");
m_PidPath = icingaConfig->Get("pidpath");
m_PidPath = Get("pidpath");
if (m_PidPath.IsEmpty())
m_PidPath = DefaultPidPath;
m_StatePath = icingaConfig->Get("statepath");
m_StatePath = Get("statepath");
if (m_StatePath.IsEmpty())
m_StatePath = DefaultStatePath;
m_Macros = icingaConfig->Get("macros");
m_Macros = Get("macros");
String logpath = icingaConfig->Get("logpath");
String logpath = Get("logpath");
if (!logpath.IsEmpty()) {
ConfigItemBuilder::Ptr fileLogConfig = boost::make_shared<ConfigItemBuilder>();
fileLogConfig->SetType("Logger");

View File

@ -34,7 +34,7 @@ public:
typedef shared_ptr<IcingaApplication> Ptr;
typedef weak_ptr<IcingaApplication> WeakPtr;
IcingaApplication(void);
IcingaApplication(const Dictionary::Ptr& serializedUpdate);
int Main(const vector<String>& args);
@ -69,6 +69,8 @@ private:
void DumpProgramState(void);
};
REGISTER_CLASS(IcingaApplication);
}
#endif /* ICINGAAPPLICATION_H */