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

View File

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

View File

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

View File

@ -151,7 +151,8 @@ class RegisterClassHelper
public: public:
RegisterClassHelper(const String& name, DynamicObject::Factory factory) 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) void Logger::ForwardLogEntry(const LogEntry& entry)
{ {
bool processed = false;
DynamicObject::Ptr object; DynamicObject::Ptr object;
BOOST_FOREACH(tie(tuples::ignore, object), DynamicObject::GetObjects("Logger")) { BOOST_FOREACH(tie(tuples::ignore, object), DynamicObject::GetObjects("Logger")) {
Logger::Ptr logger = dynamic_pointer_cast<Logger>(object); Logger::Ptr logger = dynamic_pointer_cast<Logger>(object);
if (entry.Severity >= logger->GetMinSeverity()) if (entry.Severity >= logger->GetMinSeverity())
logger->m_Impl->ProcessLogEntry(entry); logger->m_Impl->ProcessLogEntry(entry);
processed = true;
} }
if (!processed && entry.Severity >= LogInformation)
StreamLogger::ProcessLogEntry(std::cout, entry);
} }
String Logger::SeverityToString(LogSeverity severity) 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. * Processes a log entry and outputs it to a stream.
* *
* @param stream The output stream.
* @param entry The log entry. * @param entry The log entry.
*/ */
void StreamLogger::ProcessLogEntry(const LogEntry& entry) void StreamLogger::ProcessLogEntry(std::ostream& stream, const LogEntry& entry)
{ {
char timestamp[100]; char timestamp[100];
@ -60,7 +61,18 @@ void StreamLogger::ProcessLogEntry(const LogEntry& entry)
strftime(timestamp, sizeof(timestamp), "%Y/%m/%d %H:%M:%S", &tmnow); strftime(timestamp, sizeof(timestamp), "%Y/%m/%d %H:%M:%S", &tmnow);
*m_Stream << "[" << timestamp << "] " stream << "[" << timestamp << "] "
<< Logger::SeverityToString(entry.Severity) << "/" << entry.Facility << ": " << Logger::SeverityToString(entry.Severity) << "/" << entry.Facility << ": "
<< entry.Message << std::endl; << 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); ~StreamLogger(void);
void OpenFile(const String& filename); void OpenFile(const String& filename);
static void ProcessLogEntry(std::ostream& stream, const LogEntry& entry);
protected: protected:
virtual void ProcessLogEntry(const LogEntry& entry); virtual void ProcessLogEntry(const LogEntry& entry);

View File

@ -20,6 +20,9 @@
#include <i2-icinga.h> #include <i2-icinga.h>
#ifndef _WIN32 #ifndef _WIN32
# include "icinga-version.h"
# define ICINGA_VERSION GIT_MESSAGE
# include <ltdl.h> # include <ltdl.h>
#endif /* _WIN32 */ #endif /* _WIN32 */
@ -38,6 +41,48 @@ int main(int argc, char **argv)
LTDL_SET_PRELOADED_SYMBOLS(); LTDL_SET_PRELOADED_SYMBOLS();
#endif /* _WIN32 */ #endif /* _WIN32 */
IcingaApplication::Ptr instance = boost::make_shared<IcingaApplication>(); #ifndef _WIN32
return instance->Run(argc, argv); 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::DefaultPidPath = "icinga.pid";
const String IcingaApplication::DefaultStatePath = "icinga.state"; 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. * The entry point for the Icinga application.
@ -42,26 +59,13 @@ IcingaApplication::IcingaApplication(void)
*/ */
int IcingaApplication::Main(const vector<String>& args) int IcingaApplication::Main(const vector<String>& args)
{ {
/* create console logger */ Logger::Write(LogInformation, "icinga", "In IcingaApplication::Main()");
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 */
m_StartTime = Utility::GetTime(); m_StartTime = Utility::GetTime();
if (args.size() < 2) { if (args.size() == 1 && args[0] == "--help") {
stringstream msgbuf; stringstream msgbuf;
msgbuf << "Syntax: " << args[0] << " [-S] [-L logfile] [-d] [--] <config-file>"; msgbuf << "Syntax: " << args[0] << " ... -d";
Logger::Write(LogInformation, "icinga", msgbuf.str()); Logger::Write(LogInformation, "icinga", msgbuf.str());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -92,68 +96,24 @@ int IcingaApplication::Main(const vector<String>& args)
throw_exception(invalid_argument("Unknown option: " + arg)); 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()) m_CertificateFile = Get("cert");
throw_exception(invalid_argument("No config file was specified on the command line.")); m_CAFile = Get("ca");
m_Node = Get("node");
m_Service = Get("service");
String componentDirectory = Utility::DirName(GetExePath()) + "/../lib/icinga2"; m_PidPath = Get("pidpath");
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");
if (m_PidPath.IsEmpty()) if (m_PidPath.IsEmpty())
m_PidPath = DefaultPidPath; m_PidPath = DefaultPidPath;
m_StatePath = icingaConfig->Get("statepath"); m_StatePath = Get("statepath");
if (m_StatePath.IsEmpty()) if (m_StatePath.IsEmpty())
m_StatePath = DefaultStatePath; m_StatePath = DefaultStatePath;
m_Macros = icingaConfig->Get("macros"); m_Macros = Get("macros");
String logpath = icingaConfig->Get("logpath"); String logpath = Get("logpath");
if (!logpath.IsEmpty()) { if (!logpath.IsEmpty()) {
ConfigItemBuilder::Ptr fileLogConfig = boost::make_shared<ConfigItemBuilder>(); ConfigItemBuilder::Ptr fileLogConfig = boost::make_shared<ConfigItemBuilder>();
fileLogConfig->SetType("Logger"); fileLogConfig->SetType("Logger");

View File

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