From f45f6ccd82800dc95f0d1f8d4df6bc07e74e4d96 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 25 Aug 2015 13:53:43 +0200 Subject: [PATCH] Implement support for tracking dependencies between config objects refs #9096 --- lib/base/CMakeLists.txt | 2 +- lib/base/application.cpp | 2 +- lib/base/configobject.cpp | 6 + lib/base/configobject.hpp | 7 +- lib/base/configobject.ti | 6 + lib/base/dependencygraph.cpp | 55 +++++++++ lib/base/dependencygraph.hpp | 50 ++++++++ lib/base/filelogger.cpp | 2 +- lib/base/logger.cpp | 4 +- lib/base/object.cpp | 5 + lib/base/object.hpp | 2 + lib/base/scriptutils.cpp | 8 ++ lib/base/scriptutils.hpp | 1 + lib/base/streamlogger.cpp | 2 +- lib/base/type.cpp | 4 +- lib/base/type.hpp | 5 +- lib/checker/checkercomponent.cpp | 4 +- lib/compat/compatlogger.cpp | 2 +- lib/compat/externalcommandlistener.cpp | 2 +- lib/compat/statusdatawriter.cpp | 2 +- lib/db_ido/dbconnection.cpp | 2 +- lib/demo/demo.cpp | 2 +- lib/icinga/checkable.cpp | 2 +- lib/icinga/dependency.cpp | 5 +- lib/icinga/dependency.ti | 37 ++++-- lib/icinga/host.cpp | 4 +- lib/icinga/host.ti | 8 +- lib/icinga/hostgroup.ti | 8 +- lib/icinga/icingastatuswriter.cpp | 2 +- lib/icinga/notification.cpp | 5 +- lib/icinga/notification.ti | 39 +++--- lib/icinga/scheduleddowntime.cpp | 5 +- lib/icinga/scheduleddowntime.ti | 15 ++- lib/icinga/service.cpp | 2 +- lib/icinga/service.ti | 8 +- lib/icinga/servicegroup.ti | 8 +- lib/icinga/timeperiod.cpp | 2 +- lib/icinga/user.cpp | 6 +- lib/icinga/user.ti | 20 +-- lib/icinga/usergroup.ti | 8 +- lib/livestatus/livestatuslistener.cpp | 4 +- lib/notification/notificationcomponent.cpp | 2 +- lib/perfdata/gelfwriter.cpp | 2 +- lib/perfdata/graphitewriter.cpp | 2 +- lib/perfdata/opentsdbwriter.cpp | 2 +- lib/perfdata/perfdatawriter.cpp | 2 +- lib/remote/apilistener.cpp | 2 +- lib/remote/statusqueryhandler.cpp | 27 ++++- lib/remote/zone.ti | 8 +- tools/mkclass/class_lexer.ll | 3 + tools/mkclass/class_parser.yy | 39 +++++- tools/mkclass/classcompiler.cpp | 134 +++++++++++++++++++-- tools/mkclass/classcompiler.hpp | 18 ++- 53 files changed, 452 insertions(+), 152 deletions(-) create mode 100644 lib/base/dependencygraph.cpp create mode 100644 lib/base/dependencygraph.hpp diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 853068f47..2754ea39a 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -26,7 +26,7 @@ set(base_SOURCES application.cpp application.thpp application-version.cpp array.cpp array-script.cpp boolean.cpp boolean-script.cpp console.cpp context.cpp convert.cpp debuginfo.cpp dictionary.cpp dictionary-script.cpp - configobject.cpp configobject.thpp configobject-script.cpp configtype.cpp + configobject.cpp configobject.thpp configobject-script.cpp configtype.cpp dependencygraph.cpp exception.cpp fifo.cpp filelogger.cpp filelogger.thpp initialize.cpp json.cpp json-script.cpp loader.cpp logger.cpp logger.thpp math-script.cpp netstring.cpp networkstream.cpp number.cpp number-script.cpp object.cpp diff --git a/lib/base/application.cpp b/lib/base/application.cpp index a11e722a7..1363410b0 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -102,7 +102,7 @@ void Application::Stop(void) } else ClosePidFile(true); - ConfigObject::Stop(); + ObjectImpl::Stop(); } Application::~Application(void) diff --git a/lib/base/configobject.cpp b/lib/base/configobject.cpp index 8ac18d65c..1d895676c 100644 --- a/lib/base/configobject.cpp +++ b/lib/base/configobject.cpp @@ -231,6 +231,8 @@ void ConfigObject::Unregister(void) void ConfigObject::Start(void) { + ObjectImpl::Start(); + ASSERT(!OwnsLock()); ObjectLock olock(this); @@ -260,6 +262,8 @@ void ConfigObject::Activate(void) void ConfigObject::Stop(void) { + ObjectImpl::Stop(); + ASSERT(!OwnsLock()); ObjectLock olock(this); @@ -501,5 +505,7 @@ void ConfigObject::DumpModifiedAttributes(const boost::functionGetObject(name); } diff --git a/lib/base/configobject.hpp b/lib/base/configobject.hpp index 5885621f2..fae5e0520 100644 --- a/lib/base/configobject.hpp +++ b/lib/base/configobject.hpp @@ -64,8 +64,8 @@ public: void Deactivate(void); void SetAuthority(bool authority); - virtual void Start(void); - virtual void Stop(void); + virtual void Start(void) override; + virtual void Stop(void) override; virtual void Pause(void); virtual void Resume(void); @@ -83,6 +83,8 @@ public: return static_pointer_cast(object); } + static ConfigObject::Ptr GetObject(const String& type, const String& name); + static void DumpObjects(const String& filename, int attributeTypes = FAState); static void RestoreObjects(const String& filename, int attributeTypes = FAState); static void StopObjects(void); @@ -95,7 +97,6 @@ protected: explicit ConfigObject(void); private: - static ConfigObject::Ptr GetObject(const String& type, const String& name); static void RestoreObject(const String& message, int attributeTypes); }; diff --git a/lib/base/configobject.ti b/lib/base/configobject.ti index 3aa6cc79c..e3c6f857e 100644 --- a/lib/base/configobject.ti +++ b/lib/base/configobject.ti @@ -55,6 +55,12 @@ public: m_DebugInfo = di; } + inline virtual void Start(void) + { } + + inline virtual void Stop(void) + { } + private: DebugInfo m_DebugInfo; }; diff --git a/lib/base/dependencygraph.cpp b/lib/base/dependencygraph.cpp new file mode 100644 index 000000000..5bb9d656a --- /dev/null +++ b/lib/base/dependencygraph.cpp @@ -0,0 +1,55 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "base/dependencygraph.hpp" +#include + +using namespace icinga; + +boost::mutex DependencyGraph::m_Mutex; +std::map > DependencyGraph::m_Dependencies; + +void DependencyGraph::AddDependency(Object *parent, Object *child) +{ + boost::mutex::scoped_lock lock(m_Mutex); + m_Dependencies[child][parent]++; +} + +void DependencyGraph::RemoveDependency(Object *parent, Object *child) +{ + boost::mutex::scoped_lock lock(m_Mutex); + m_Dependencies[child][parent]--; +} + +std::vector DependencyGraph::GetParents(const Object::Ptr& child) +{ + std::vector objects; + + boost::mutex::scoped_lock lock(m_Mutex); + std::map >::const_iterator it = m_Dependencies.find(child.get()); + + if (it != m_Dependencies.end()) { + typedef std::pair kv_pair; + BOOST_FOREACH(const kv_pair& kv, it->second) { + objects.push_back(kv.first); + } + } + + return objects; +} diff --git a/lib/base/dependencygraph.hpp b/lib/base/dependencygraph.hpp new file mode 100644 index 000000000..6b4e4bccb --- /dev/null +++ b/lib/base/dependencygraph.hpp @@ -0,0 +1,50 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef DEPENDENCYGRAPH_H +#define DEPENDENCYGRAPH_H + +#include "base/i2-base.hpp" +#include "base/object.hpp" +#include + +namespace icinga { + +/** + * A graph that tracks dependencies between objects. + * + * @ingroup base + */ +class I2_BASE_API DependencyGraph +{ +public: + static void AddDependency(Object *parent, Object *child); + static void RemoveDependency(Object *parent, Object *child); + static std::vector GetParents(const Object::Ptr& child); + +private: + DependencyGraph(void); + + static boost::mutex m_Mutex; + static std::map > m_Dependencies; +}; + +} + +#endif /* DEPENDENCYGRAPH_H */ diff --git a/lib/base/filelogger.cpp b/lib/base/filelogger.cpp index 4a80f131e..afd4c606b 100644 --- a/lib/base/filelogger.cpp +++ b/lib/base/filelogger.cpp @@ -46,7 +46,7 @@ void FileLogger::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&) */ void FileLogger::Start(void) { - StreamLogger::Start(); + ObjectImpl::Start(); ReopenLogFile(); diff --git a/lib/base/logger.cpp b/lib/base/logger.cpp index 27151a9cf..c7cf16169 100644 --- a/lib/base/logger.cpp +++ b/lib/base/logger.cpp @@ -54,7 +54,7 @@ void Logger::StaticInitialize(void) */ void Logger::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); boost::mutex::scoped_lock lock(m_Mutex); m_Loggers.insert(this); @@ -67,7 +67,7 @@ void Logger::Stop(void) m_Loggers.erase(this); } - ConfigObject::Stop(); + ObjectImpl::Stop(); } std::set Logger::GetLoggers(void) diff --git a/lib/base/object.cpp b/lib/base/object.cpp index ed7cd9efc..38fd82fd4 100644 --- a/lib/base/object.cpp +++ b/lib/base/object.cpp @@ -92,6 +92,11 @@ Value Object::GetField(int id) const BOOST_THROW_EXCEPTION(std::runtime_error("Invalid field ID.")); } +void Object::Validate(int types, const ValidationUtils& utils) +{ + /* Nothing to do here. */ +} + void Object::ValidateField(int id, const Value& value, const ValidationUtils& utils) { /* Nothing to do here. */ diff --git a/lib/base/object.hpp b/lib/base/object.hpp index e6b0875d0..b56377bf5 100644 --- a/lib/base/object.hpp +++ b/lib/base/object.hpp @@ -103,6 +103,8 @@ public: virtual intrusive_ptr GetReflectionType(void) const; + virtual void Validate(int types, const ValidationUtils& utils); + virtual void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty); virtual Value GetField(int id) const; virtual void ValidateField(int id, const Value& value, const ValidationUtils& utils); diff --git a/lib/base/scriptutils.cpp b/lib/base/scriptutils.cpp index c43525176..085c7260c 100644 --- a/lib/base/scriptutils.cpp +++ b/lib/base/scriptutils.cpp @@ -26,6 +26,7 @@ #include "base/objectlock.hpp" #include "base/configtype.hpp" #include "base/application.hpp" +#include "base/dependencygraph.hpp" #include #include #include @@ -57,6 +58,7 @@ REGISTER_SAFE_SCRIPTFUNCTION(get_time, &Utility::GetTime); REGISTER_SAFE_SCRIPTFUNCTION(basename, &Utility::BaseName); REGISTER_SAFE_SCRIPTFUNCTION(dirname, &Utility::DirName); REGISTER_SAFE_SCRIPTFUNCTION(msi_get_component_path, &ScriptUtils::MsiGetComponentPathShim); +REGISTER_SAFE_SCRIPTFUNCTION(track_parents, &ScriptUtils::TrackParents); String ScriptUtils::CastString(const Value& value) { @@ -304,3 +306,9 @@ String ScriptUtils::MsiGetComponentPathShim(const String& component) return String(); #endif /* _WIN32 */ } + +Array::Ptr ScriptUtils::TrackParents(const Object::Ptr& child) +{ + return Array::FromVector(DependencyGraph::GetParents(child)); +} + diff --git a/lib/base/scriptutils.hpp b/lib/base/scriptutils.hpp index 204ca7761..c4a0d477f 100644 --- a/lib/base/scriptutils.hpp +++ b/lib/base/scriptutils.hpp @@ -51,6 +51,7 @@ public: static Array::Ptr GetObjects(const Type::Ptr& type); static void Assert(const Value& arg); static String MsiGetComponentPathShim(const String& component); + static Array::Ptr TrackParents(const Object::Ptr& parent); private: ScriptUtils(void); diff --git a/lib/base/streamlogger.cpp b/lib/base/streamlogger.cpp index 63ac98f6a..0791a2ad4 100644 --- a/lib/base/streamlogger.cpp +++ b/lib/base/streamlogger.cpp @@ -39,7 +39,7 @@ StreamLogger::StreamLogger(void) void StreamLogger::Stop(void) { - Logger::Stop(); + ObjectImpl::Stop(); // make sure we flush the log data on shutdown, even if we don't call the destructor if (m_Stream) diff --git a/lib/base/type.cpp b/lib/base/type.cpp index 52300ebf4..4d61da62b 100644 --- a/lib/base/type.cpp +++ b/lib/base/type.cpp @@ -159,9 +159,9 @@ int TypeType::GetFieldId(const String& name) const Field TypeType::GetFieldInfo(int id) const { if (id == 0) - return Field(0, "Object", "prototype", NULL, 0); + return Field(0, "Object", "prototype", NULL, 0, 0); else if (id == 1) - return Field(1, "Object", "base", NULL, 0); + return Field(1, "Object", "base", NULL, 0, 0); throw std::runtime_error("Invalid field ID."); } diff --git a/lib/base/type.hpp b/lib/base/type.hpp index 6417a5c84..4084e8b99 100644 --- a/lib/base/type.hpp +++ b/lib/base/type.hpp @@ -49,9 +49,10 @@ struct Field const char *Name; const char *RefTypeName; int Attributes; + int ArrayRank; - Field(int id, const char *type, const char *name, const char *reftype, int attributes) - : ID(id), TypeName(type), Name(name), RefTypeName(reftype), Attributes(attributes) + Field(int id, const char *type, const char *name, const char *reftype, int attributes, int arrayRank) + : ID(id), TypeName(type), Name(name), RefTypeName(reftype), Attributes(attributes), ArrayRank(arrayRank) { } }; diff --git a/lib/checker/checkercomponent.cpp b/lib/checker/checkercomponent.cpp index 175981384..fa7bf978a 100644 --- a/lib/checker/checkercomponent.cpp +++ b/lib/checker/checkercomponent.cpp @@ -74,7 +74,7 @@ void CheckerComponent::OnConfigLoaded(void) void CheckerComponent::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); m_Thread = boost::thread(boost::bind(&CheckerComponent::CheckThreadProc, this)); @@ -97,7 +97,7 @@ void CheckerComponent::Stop(void) m_ResultTimer->Stop(); m_Thread.join(); - ConfigObject::Stop(); + ObjectImpl::Stop(); } void CheckerComponent::CheckThreadProc(void) diff --git a/lib/compat/compatlogger.cpp b/lib/compat/compatlogger.cpp index ff0f38ba6..3f0b67947 100644 --- a/lib/compat/compatlogger.cpp +++ b/lib/compat/compatlogger.cpp @@ -59,7 +59,7 @@ void CompatLogger::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&) */ void CompatLogger::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); Checkable::OnNewCheckResult.connect(bind(&CompatLogger::CheckResultHandler, this, _1, _2)); Checkable::OnNotificationSentToUser.connect(bind(&CompatLogger::NotificationSentHandler, this, _1, _2, _3, _4, _5, _6, _7, _8)); diff --git a/lib/compat/externalcommandlistener.cpp b/lib/compat/externalcommandlistener.cpp index c13696738..ec5beca15 100644 --- a/lib/compat/externalcommandlistener.cpp +++ b/lib/compat/externalcommandlistener.cpp @@ -48,7 +48,7 @@ void ExternalCommandListener::StatsFunc(const Dictionary::Ptr& status, const Arr */ void ExternalCommandListener::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); #ifndef _WIN32 m_CommandThread = boost::thread(boost::bind(&ExternalCommandListener::CommandPipeThread, this, GetCommandPath())); diff --git a/lib/compat/statusdatawriter.cpp b/lib/compat/statusdatawriter.cpp index 20f080472..97f2528d7 100644 --- a/lib/compat/statusdatawriter.cpp +++ b/lib/compat/statusdatawriter.cpp @@ -72,7 +72,7 @@ void StatusDataWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr */ void StatusDataWriter::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); m_StatusTimer = new Timer(); m_StatusTimer->SetInterval(GetUpdateInterval()); diff --git a/lib/db_ido/dbconnection.cpp b/lib/db_ido/dbconnection.cpp index 9c239eefb..986995b18 100644 --- a/lib/db_ido/dbconnection.cpp +++ b/lib/db_ido/dbconnection.cpp @@ -58,7 +58,7 @@ void DbConnection::OnConfigLoaded(void) void DbConnection::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); DbObject::OnQuery.connect(boost::bind(&DbConnection::ExecuteQuery, this, _1)); ConfigObject::OnActiveChanged.connect(boost::bind(&DbConnection::UpdateObject, this, _1)); diff --git a/lib/demo/demo.cpp b/lib/demo/demo.cpp index 221a138b4..6ecd8008b 100644 --- a/lib/demo/demo.cpp +++ b/lib/demo/demo.cpp @@ -35,7 +35,7 @@ REGISTER_APIFUNCTION(HelloWorld, demo, &Demo::DemoMessageHandler); */ void Demo::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); m_DemoTimer = new Timer(); m_DemoTimer->SetInterval(5); diff --git a/lib/icinga/checkable.cpp b/lib/icinga/checkable.cpp index bccb68fed..76f586307 100644 --- a/lib/icinga/checkable.cpp +++ b/lib/icinga/checkable.cpp @@ -47,7 +47,7 @@ void Checkable::Start(void) if (GetNextCheck() < now + 300) UpdateNextCheck(); - ConfigObject::Start(); + ObjectImpl::Start(); } void Checkable::OnStateLoaded(void) diff --git a/lib/icinga/dependency.cpp b/lib/icinga/dependency.cpp index de7ebf390..b4d364011 100644 --- a/lib/icinga/dependency.cpp +++ b/lib/icinga/dependency.cpp @@ -82,7 +82,7 @@ void Dependency::OnConfigLoaded(void) void Dependency::OnAllConfigLoaded(void) { - ConfigObject::OnAllConfigLoaded(); + ObjectImpl::OnAllConfigLoaded(); Host::Ptr childHost = Host::GetByName(GetChildHostName()); @@ -125,7 +125,7 @@ void Dependency::OnAllConfigLoaded(void) void Dependency::Stop(void) { - ConfigObject::Stop(); + ObjectImpl::Stop(); GetChild()->RemoveDependency(this); GetParent()->RemoveReverseDependency(this); @@ -232,3 +232,4 @@ void Dependency::ValidateStates(const Array::Ptr& value, const ValidationUtils& if (!GetParentServiceName().IsEmpty() && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("states"), "State filter is invalid for service dependency.")); } + diff --git a/lib/icinga/dependency.ti b/lib/icinga/dependency.ti index 2d7089663..a15853042 100644 --- a/lib/icinga/dependency.ti +++ b/lib/icinga/dependency.ti @@ -19,6 +19,7 @@ #include "icinga/customvarobject.hpp" #include "icinga/checkable.hpp" +#impl_include "icinga/service.hpp" library icinga; @@ -40,14 +41,38 @@ class Dependency : CustomVarObject < DependencyNameComposer load_after Service; [config, required] name(Host) child_host_name; - [config] String child_service_name; + [config] String child_service_name { + track {{{ + if (!oldValue.IsEmpty()) { + Service::Ptr service = Service::GetByNamePair(GetParentHostName(), oldValue); + DependencyGraph::RemoveDependency(this, service.get()); + } + + if (!newValue.IsEmpty()) { + Service::Ptr service = Service::GetByNamePair(GetParentHostName(), newValue); + DependencyGraph::RemoveDependency(this, service.get()); + } + }}} + }; [config, required] name(Host) parent_host_name; - [config] String parent_service_name; + [config] String parent_service_name { + track {{{ + if (!oldValue.IsEmpty()) { + Service::Ptr service = Service::GetByNamePair(GetParentHostName(), oldValue); + DependencyGraph::RemoveDependency(this, service.get()); + } + + if (!newValue.IsEmpty()) { + Service::Ptr service = Service::GetByNamePair(GetParentHostName(), newValue); + DependencyGraph::RemoveDependency(this, service.get()); + } + }}} + }; [config] name(TimePeriod) period (PeriodRaw); - [config] Array::Ptr states; + [config] array(double) states; int state_filter_real (StateFilter); [config] bool ignore_soft_states { @@ -60,10 +85,4 @@ class Dependency : CustomVarObject < DependencyNameComposer }; }; -validator Dependency { - Array states { - Number "*"; - }; -}; - } diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index db0530471..955bba397 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -36,7 +36,7 @@ REGISTER_TYPE(Host); void Host::OnAllConfigLoaded(void) { - Checkable::OnAllConfigLoaded(); + ObjectImpl::OnAllConfigLoaded(); HostGroup::EvaluateObjectRules(this); @@ -73,7 +73,7 @@ void Host::CreateChildObjects(const Type::Ptr& childType) void Host::Stop(void) { - Checkable::Stop(); + ObjectImpl::Stop(); Array::Ptr groups = GetGroups(); diff --git a/lib/icinga/host.ti b/lib/icinga/host.ti index c8cf945b3..06c96dcc1 100644 --- a/lib/icinga/host.ti +++ b/lib/icinga/host.ti @@ -27,7 +27,7 @@ namespace icinga class Host : Checkable { - [config] Array::Ptr groups { + [config] array(name(HostGroup)) groups { default {{{ return new Array(); }}} }; @@ -55,10 +55,4 @@ class Host : Checkable }; -validator Host { - Array groups { - name(HostGroup) "*"; - }; -}; - } diff --git a/lib/icinga/hostgroup.ti b/lib/icinga/hostgroup.ti index 514167a90..2b8d4175c 100644 --- a/lib/icinga/hostgroup.ti +++ b/lib/icinga/hostgroup.ti @@ -35,16 +35,10 @@ class HostGroup : CustomVarObject }}} }; - [config] Array::Ptr groups; + [config] array(name(HostGroup)) groups; [config] String notes; [config] String notes_url; [config] String action_url; }; -validator HostGroup { - Array groups { - name(HostGroup) "*"; - }; -}; - } diff --git a/lib/icinga/icingastatuswriter.cpp b/lib/icinga/icingastatuswriter.cpp index b9963783c..44e8f3c8c 100644 --- a/lib/icinga/icingastatuswriter.cpp +++ b/lib/icinga/icingastatuswriter.cpp @@ -58,7 +58,7 @@ void IcingaStatusWriter::StatsFunc(const Dictionary::Ptr& status, const Array::P */ void IcingaStatusWriter::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); m_StatusTimer = new Timer(); m_StatusTimer->SetInterval(GetUpdateInterval()); diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index 5488345c5..5c25ffbbe 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -115,7 +115,7 @@ void Notification::OnAllConfigLoaded(void) void Notification::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); Checkable::Ptr obj = GetCheckable(); @@ -125,7 +125,7 @@ void Notification::Start(void) void Notification::Stop(void) { - ConfigObject::Stop(); + ObjectImpl::Stop(); Checkable::Ptr obj = GetCheckable(); @@ -676,3 +676,4 @@ Endpoint::Ptr Notification::GetCommandEndpoint(void) const { return Endpoint::GetByName(GetCommandEndpointRaw()); } + diff --git a/lib/icinga/notification.ti b/lib/icinga/notification.ti index 28c8b4b70..dc83ce7b1 100644 --- a/lib/icinga/notification.ti +++ b/lib/icinga/notification.ti @@ -18,6 +18,7 @@ ******************************************************************************/ #include "icinga/customvarobject.hpp" +#impl_include "icinga/service.hpp" library icinga; @@ -43,15 +44,27 @@ class Notification : CustomVarObject < NotificationNameComposer default {{{ return 1800; }}} }; [config] name(TimePeriod) period (PeriodRaw); - [config, protected] Array::Ptr users (UsersRaw); - [config, protected] Array::Ptr user_groups (UserGroupsRaw); + [config, protected] array(name(User)) users (UsersRaw); + [config, protected] array(name(UserGroup)) user_groups (UserGroupsRaw); [config] Dictionary::Ptr times; - [config] Array::Ptr types; + [config] array(double) types; int type_filter_real (TypeFilter); - [config] Array::Ptr states; + [config] array(double) states; int state_filter_real (StateFilter); [config, protected, required] name(Host) host_name; - [config, protected] String service_name; + [config, protected] String service_name { + track {{{ + if (!oldValue.IsEmpty()) { + Service::Ptr service = Service::GetByNamePair(GetHostName(), oldValue); + DependencyGraph::RemoveDependency(this, service.get()); + } + + if (!newValue.IsEmpty()) { + Service::Ptr service = Service::GetByNamePair(GetHostName(), newValue); + DependencyGraph::RemoveDependency(this, service.get()); + } + }}} + }; [state] Array::Ptr notified_users { default {{{ return new Array(); }}} @@ -66,26 +79,10 @@ class Notification : CustomVarObject < NotificationNameComposer }; validator Notification { - Array users { - name(User) "*"; - }; - - Array user_groups { - name(UserGroup) "*"; - }; - Dictionary times { Number begin; Number end; }; - - Array types { - Number "*"; - }; - - Array states { - Number "*"; - }; }; } diff --git a/lib/icinga/scheduleddowntime.cpp b/lib/icinga/scheduleddowntime.cpp index 908493fb0..763e9fa7e 100644 --- a/lib/icinga/scheduleddowntime.cpp +++ b/lib/icinga/scheduleddowntime.cpp @@ -90,7 +90,7 @@ void ScheduledDowntime::StaticInitialize(void) void ScheduledDowntime::OnAllConfigLoaded(void) { - CustomVarObject::OnAllConfigLoaded(); + ObjectImpl::OnAllConfigLoaded(); if (!GetCheckable()) BOOST_THROW_EXCEPTION(ScriptError("ScheduledDowntime '" + GetName() + "' references a host/service which doesn't exist.", GetDebugInfo())); @@ -98,7 +98,7 @@ void ScheduledDowntime::OnAllConfigLoaded(void) void ScheduledDowntime::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); CreateNextDowntime(); } @@ -231,3 +231,4 @@ void ScheduledDowntime::ValidateRanges(const Dictionary::Ptr& value, const Valid } } } + diff --git a/lib/icinga/scheduleddowntime.ti b/lib/icinga/scheduleddowntime.ti index c41363aeb..dedefc3dc 100644 --- a/lib/icinga/scheduleddowntime.ti +++ b/lib/icinga/scheduleddowntime.ti @@ -18,6 +18,7 @@ ******************************************************************************/ #include "icinga/customvarobject.hpp" +#impl_include "icinga/service.hpp" library icinga; @@ -39,7 +40,19 @@ class ScheduledDowntime : CustomVarObject < ScheduledDowntimeNameComposer load_after Service; [config, protected, required] name(Host) host_name; - [config, protected] String service_name; + [config, protected] String service_name { + track {{{ + if (!oldValue.IsEmpty()) { + Service::Ptr service = Service::GetByNamePair(GetHostName(), oldValue); + DependencyGraph::RemoveDependency(this, service.get()); + } + + if (!newValue.IsEmpty()) { + Service::Ptr service = Service::GetByNamePair(GetHostName(), newValue); + DependencyGraph::RemoveDependency(this, service.get()); + } + }}} + }; [config, required] String author; [config, required] String comment; diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index aa713a6c0..35a83e971 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -61,7 +61,7 @@ Dictionary::Ptr ServiceNameComposer::ParseName(const String& name) const void Service::OnAllConfigLoaded(void) { - Checkable::OnAllConfigLoaded(); + ObjectImpl::OnAllConfigLoaded(); m_Host = Host::GetByName(GetHostName()); diff --git a/lib/icinga/service.ti b/lib/icinga/service.ti index 49e96bf55..d3f939bdc 100644 --- a/lib/icinga/service.ti +++ b/lib/icinga/service.ti @@ -40,7 +40,7 @@ class Service : Checkable < ServiceNameComposer { load_after Host; - [config] Array::Ptr groups { + [config] array(name(ServiceGroup)) groups { default {{{ return new Array(); }}} }; @@ -70,10 +70,4 @@ class Service : Checkable < ServiceNameComposer }; }; -validator Service { - Array groups { - name(ServiceGroup) "*"; - }; -}; - } diff --git a/lib/icinga/servicegroup.ti b/lib/icinga/servicegroup.ti index fb37b69e4..eb69b56dc 100644 --- a/lib/icinga/servicegroup.ti +++ b/lib/icinga/servicegroup.ti @@ -35,16 +35,10 @@ class ServiceGroup : CustomVarObject }}} }; - [config] Array::Ptr groups; + [config] array(name(ServiceGroup)) groups; [config] String notes; [config] String notes_url; [config] String action_url; }; -validator ServiceGroup { - Array groups { - name(ServiceGroup) "*"; - }; -}; - } diff --git a/lib/icinga/timeperiod.cpp b/lib/icinga/timeperiod.cpp index 7a37ee80a..651a0d3f4 100644 --- a/lib/icinga/timeperiod.cpp +++ b/lib/icinga/timeperiod.cpp @@ -46,7 +46,7 @@ void TimePeriod::StaticInitialize(void) void TimePeriod::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); /* Pre-fill the time period for the next 24 hours. */ double now = Utility::GetTime(); diff --git a/lib/icinga/user.cpp b/lib/icinga/user.cpp index ff4d9661f..f8f170495 100644 --- a/lib/icinga/user.cpp +++ b/lib/icinga/user.cpp @@ -32,7 +32,7 @@ REGISTER_TYPE(User); void User::OnConfigLoaded(void) { - ConfigObject::OnConfigLoaded(); + ObjectImpl::OnConfigLoaded(); SetTypeFilter(FilterArrayToInt(GetTypes(), ~0)); SetStateFilter(FilterArrayToInt(GetStates(), ~0)); @@ -40,7 +40,7 @@ void User::OnConfigLoaded(void) void User::OnAllConfigLoaded(void) { - ConfigObject::OnAllConfigLoaded(); + ObjectImpl::OnAllConfigLoaded(); UserGroup::EvaluateObjectRules(this); @@ -62,7 +62,7 @@ void User::OnAllConfigLoaded(void) void User::Stop(void) { - ConfigObject::Stop(); + ObjectImpl::Stop(); Array::Ptr groups = GetGroups(); diff --git a/lib/icinga/user.ti b/lib/icinga/user.ti index 5c7a9ed80..a3940e323 100644 --- a/lib/icinga/user.ti +++ b/lib/icinga/user.ti @@ -35,13 +35,13 @@ class User : CustomVarObject return m_DisplayName; }}} }; - [config] Array::Ptr groups { + [config] array(name(UserGroup)) groups { default {{{ return new Array(); }}} }; [config] name(TimePeriod) period (PeriodRaw); - [config] Array::Ptr types; + [config] array(double) types; int type_filter_real (TypeFilter); - [config] Array::Ptr states; + [config] array(double) states; int state_filter_real (StateFilter); [config] String email; @@ -54,18 +54,4 @@ class User : CustomVarObject [state] double last_notification; }; -validator User { - Array groups { - name(UserGroup) "*"; - }; - - Array types { - Number "*"; - }; - - Array states { - Number "*"; - }; -}; - } diff --git a/lib/icinga/usergroup.ti b/lib/icinga/usergroup.ti index 11cda143b..f1c480a41 100644 --- a/lib/icinga/usergroup.ti +++ b/lib/icinga/usergroup.ti @@ -35,13 +35,7 @@ class UserGroup : CustomVarObject }}} }; - [config] Array::Ptr groups; -}; - -validator UserGroup { - Array groups { - name(UserGroup) "*"; - }; + [config] array(name(UserGroup)) groups; }; } diff --git a/lib/livestatus/livestatuslistener.cpp b/lib/livestatus/livestatuslistener.cpp index 98aa99db6..da55f6f90 100644 --- a/lib/livestatus/livestatuslistener.cpp +++ b/lib/livestatus/livestatuslistener.cpp @@ -64,7 +64,7 @@ void LivestatusListener::StatsFunc(const Dictionary::Ptr& status, const Array::P */ void LivestatusListener::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); if (GetSocketType() == "tcp") { TcpSocket::Ptr socket = new TcpSocket(); @@ -121,7 +121,7 @@ void LivestatusListener::Start(void) void LivestatusListener::Stop(void) { - ConfigObject::Stop(); + ObjectImpl::Stop(); m_Listener->Close(); diff --git a/lib/notification/notificationcomponent.cpp b/lib/notification/notificationcomponent.cpp index ccd2f6154..b85e8ecd4 100644 --- a/lib/notification/notificationcomponent.cpp +++ b/lib/notification/notificationcomponent.cpp @@ -51,7 +51,7 @@ void NotificationComponent::StatsFunc(const Dictionary::Ptr& status, const Array */ void NotificationComponent::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); Checkable::OnNotificationsRequested.connect(boost::bind(&NotificationComponent::SendNotificationsHandler, this, _1, _2, _3, _4, _5)); diff --git a/lib/perfdata/gelfwriter.cpp b/lib/perfdata/gelfwriter.cpp index 10a9a948f..053a8f8dd 100644 --- a/lib/perfdata/gelfwriter.cpp +++ b/lib/perfdata/gelfwriter.cpp @@ -40,7 +40,7 @@ REGISTER_TYPE(GelfWriter); void GelfWriter::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); m_ReconnectTimer = new Timer(); m_ReconnectTimer->SetInterval(10); diff --git a/lib/perfdata/graphitewriter.cpp b/lib/perfdata/graphitewriter.cpp index 8a48ceb66..dd36104e7 100644 --- a/lib/perfdata/graphitewriter.cpp +++ b/lib/perfdata/graphitewriter.cpp @@ -60,7 +60,7 @@ void GraphiteWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&) void GraphiteWriter::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); m_ReconnectTimer = new Timer(); m_ReconnectTimer->SetInterval(10); diff --git a/lib/perfdata/opentsdbwriter.cpp b/lib/perfdata/opentsdbwriter.cpp index b8c51414b..8fa0f7131 100644 --- a/lib/perfdata/opentsdbwriter.cpp +++ b/lib/perfdata/opentsdbwriter.cpp @@ -60,7 +60,7 @@ void OpenTsdbWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&) void OpenTsdbWriter::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); m_ReconnectTimer = new Timer(); m_ReconnectTimer->SetInterval(10); diff --git a/lib/perfdata/perfdatawriter.cpp b/lib/perfdata/perfdatawriter.cpp index 0f9cc664c..44e000d02 100644 --- a/lib/perfdata/perfdatawriter.cpp +++ b/lib/perfdata/perfdatawriter.cpp @@ -51,7 +51,7 @@ void PerfdataWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&) void PerfdataWriter::Start(void) { - ConfigObject::Start(); + ObjectImpl::Start(); Checkable::OnNewCheckResult.connect(boost::bind(&PerfdataWriter::CheckResultHandler, this, _1, _2)); diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index f743daf48..3c27a38c5 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -102,7 +102,7 @@ void ApiListener::Start(void) return; } - ConfigObject::Start(); + ObjectImpl::Start(); { boost::mutex::scoped_lock(m_LogLock); diff --git a/lib/remote/statusqueryhandler.cpp b/lib/remote/statusqueryhandler.cpp index 153c86ddf..71df3a21a 100644 --- a/lib/remote/statusqueryhandler.cpp +++ b/lib/remote/statusqueryhandler.cpp @@ -21,6 +21,8 @@ #include "remote/httputility.hpp" #include "remote/filterutility.hpp" #include "base/serializer.hpp" +#include "base/dependencygraph.hpp" +#include "base/configtype.hpp" #include #include @@ -72,11 +74,16 @@ bool StatusQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re } BOOST_FOREACH(const ConfigObject::Ptr& obj, objs) { + Dictionary::Ptr result1 = new Dictionary(); + results->Add(result1); + + Dictionary::Ptr resultAttrs = new Dictionary(); + result1->Set("attrs", resultAttrs); + BOOST_FOREACH(const String& joinType, joinTypes) { String prefix = joinType; boost::algorithm::to_lower(prefix); - Dictionary::Ptr result1 = new Dictionary(); for (int fid = 0; fid < type->GetFieldCount(); fid++) { Field field = type->GetFieldInfo(fid); String aname = prefix + "." + field.Name; @@ -85,9 +92,23 @@ bool StatusQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re Value val = static_cast(obj)->GetField(fid); Value sval = Serialize(val, FAConfig | FAState); - result1->Set(aname, sval); + resultAttrs->Set(aname, sval); } - results->Add(result1); + } + + Array::Ptr used_by = new Array(); + result1->Set("used_by", used_by); + + BOOST_FOREACH(const Object::Ptr& pobj, DependencyGraph::GetParents((obj))) { + ConfigObject::Ptr configObj = dynamic_pointer_cast(pobj); + + if (!configObj) + continue; + + Dictionary::Ptr refInfo = new Dictionary(); + refInfo->Set("type", configObj->GetType()->GetName()); + refInfo->Set("name", configObj->GetName()); + used_by->Add(refInfo); } } diff --git a/lib/remote/zone.ti b/lib/remote/zone.ti index f6224e406..86296aa92 100644 --- a/lib/remote/zone.ti +++ b/lib/remote/zone.ti @@ -27,14 +27,8 @@ namespace icinga class Zone : ConfigObject { [config] name(Zone) parent (ParentRaw); - [config] Array::Ptr endpoints (EndpointsRaw); + [config] array(name(Endpoint)) endpoints (EndpointsRaw); [config] bool global; }; -validator Zone { - Array endpoints { - name(Endpoint) "*"; - }; -}; - } diff --git a/tools/mkclass/class_lexer.ll b/tools/mkclass/class_lexer.ll index 8769a8eeb..fb9c88ae4 100644 --- a/tools/mkclass/class_lexer.ll +++ b/tools/mkclass/class_lexer.ll @@ -130,6 +130,7 @@ static char *lb_steal(lex_buf *lb) [ \t\r\n] /* ignore whitespace */ #include { return T_INCLUDE; } +#impl_include { return T_IMPL_INCLUDE; } class { return T_CLASS; } namespace { return T_NAMESPACE; } code { return T_CODE; } @@ -147,9 +148,11 @@ no_storage { yylval->num = FANoStorage; return T_FIELD_ATTRIBUTE; } validator { return T_VALIDATOR; } required { return T_REQUIRED; } name { return T_NAME; } +array { return T_ARRAY; } default { yylval->num = FTDefault; return T_FIELD_ACCESSOR_TYPE; } get { yylval->num = FTGet; return T_FIELD_ACCESSOR_TYPE; } set { yylval->num = FTSet; return T_FIELD_ACCESSOR_TYPE; } +track { yylval->num = FTTrack; return T_FIELD_ACCESSOR_TYPE; } \"[^\"]+\" { yylval->text = strdup(yytext + 1); yylval->text[strlen(yylval->text) - 1] = '\0'; return T_STRING; } \<[^>]+\> { yylval->text = strdup(yytext + 1); yylval->text[strlen(yylval->text) - 1] = '\0'; return T_ANGLE_STRING; } [a-zA-Z_][:a-zA-Z0-9\-_]* { yylval->text = strdup(yytext); return T_IDENTIFIER; } diff --git a/tools/mkclass/class_parser.yy b/tools/mkclass/class_parser.yy index fdb6d948d..a17c0f82c 100644 --- a/tools/mkclass/class_parser.yy +++ b/tools/mkclass/class_parser.yy @@ -56,7 +56,8 @@ using namespace icinga; Validator *validator; } -%token T_INCLUDE "include (T_INCLUDE)" +%token T_INCLUDE "#include (T_INCLUDE)" +%token T_IMPL_INCLUDE "#impl_include (T_IMPL_INCLUDE)" %token T_CLASS "class (T_CLASS)" %token T_CODE "code (T_CODE)" %token T_LOAD_AFTER "load_after (T_LOAD_AFTER)" @@ -65,6 +66,7 @@ using namespace icinga; %token T_VALIDATOR "validator (T_VALIDATOR)" %token T_REQUIRED "required (T_REQUIRED)" %token T_NAME "name (T_NAME)" +%token T_ARRAY "array (T_ARRAY)" %token T_STRING "string (T_STRING)" %token T_ANGLE_STRING "angle_string (T_ANGLE_STRING)" %token T_FIELD_ATTRIBUTE "field_attribute (T_FIELD_ATTRIBUTE)" @@ -83,6 +85,8 @@ using namespace icinga; %type type_base_specifier %type include %type angle_include +%type impl_include +%type angle_impl_include %type code %type T_FIELD_ATTRIBUTE %type field_attribute @@ -147,6 +151,16 @@ statement: include context->HandleAngleInclude($1, yylloc); std::free($1); } + | impl_include + { + context->HandleImplInclude($1, yylloc); + std::free($1); + } + | angle_impl_include + { + context->HandleAngleImplInclude($1, yylloc); + std::free($1); + } | class { context->HandleClass(*$1, yylloc); @@ -178,6 +192,18 @@ angle_include: T_INCLUDE T_ANGLE_STRING } ; +impl_include: T_IMPL_INCLUDE T_STRING + { + $$ = $2; + } + ; + +angle_impl_include: T_IMPL_INCLUDE T_ANGLE_STRING + { + $$ = $2; + } + ; + namespace: T_NAMESPACE identifier '{' { context->HandleNamespaceBegin($2, yylloc); @@ -290,8 +316,14 @@ field_type: identifier $$ = new FieldType(); $$->IsName = true; $$->TypeName = $3; + $$->ArrayRank = 0; free($3); } + | T_ARRAY '(' field_type ')' + { + $$ = $3; + $$->ArrayRank++; + } ; class_field: field_attribute_list field_type identifier alternative_name_specifier field_accessor_list ';' @@ -328,7 +360,10 @@ class_field: field_attribute_list field_type identifier alternative_name_specifi case FTDefault: field->DefaultAccessor = it->Accessor; break; - } + case FTTrack: + field->TrackAccessor = it->Accessor; + break; + } } delete $5; diff --git a/tools/mkclass/classcompiler.cpp b/tools/mkclass/classcompiler.cpp index d079e5694..12de1cbb3 100644 --- a/tools/mkclass/classcompiler.cpp +++ b/tools/mkclass/classcompiler.cpp @@ -73,6 +73,16 @@ void ClassCompiler::HandleAngleInclude(const std::string& path, const ClassDebug m_Header << "#include <" << path << ">" << std::endl << std::endl; } +void ClassCompiler::HandleImplInclude(const std::string& path, const ClassDebugInfo&) +{ + m_Impl << "#include \"" << path << "\"" << std::endl << std::endl; +} + +void ClassCompiler::HandleAngleImplInclude(const std::string& path, const ClassDebugInfo&) +{ + m_Impl << "#include <" << path << ">" << std::endl << std::endl; +} + void ClassCompiler::HandleNamespaceBegin(const std::string& name, const ClassDebugInfo&) { m_Header << "namespace " << name << std::endl @@ -356,7 +366,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) nameref = "NULL"; m_Impl << "\t\t" << "case " << num << ":" << std::endl - << "\t\t\t" << "return Field(" << num << ", \"" << ftype << "\", \"" << it->Name << "\", " << nameref << ", " << it->Attributes << ");" << std::endl; + << "\t\t\t" << "return Field(" << num << ", \"" << ftype << "\", \"" << it->Name << "\", " << nameref << ", " << it->Attributes << ", " << it->Type.ArrayRank << ");" << std::endl; num++; } @@ -454,7 +464,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) m_Impl << "template class ObjectImpl<" << klass.Name << ">;" << std::endl << std::endl; /* Validate */ - m_Header << "\t" << "virtual void Validate(int types, const ValidationUtils& utils);" << std::endl; + m_Header << "\t" << "virtual void Validate(int types, const ValidationUtils& utils) override;" << std::endl; m_Impl << "void ObjectImpl<" << klass.Name << ">::Validate(int types, const ValidationUtils& utils)" << std::endl << "{" << std::endl; @@ -488,10 +498,21 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) } if (field.Type.IsName) { - m_Impl << "\t" << "String ref = value;" << std::endl - << "\t" << "if (!ref.IsEmpty() && !utils.ValidateName(\"" << field.Type.TypeName << "\", ref))" << std::endl + if (field.Type.ArrayRank > 0) { + m_Impl << "\t" << "if (value) {" << std::endl + << "\t\t" << "ObjectLock olock(value);" << std::endl + << "\t\t" << "BOOST_FOREACH(const String& ref, value) {" << std::endl; + } else + m_Impl << "\t" << "String ref = value;" << std::endl; + + m_Impl << "\t" << "if (!ref.IsEmpty() && !utils.ValidateName(\"" << field.Type.TypeName << "\", ref))" << std::endl << "\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(dynamic_cast(this), boost::assign::list_of(\"" << field.Name << "\"), \"Object '\" + ref + \"' of type '" << field.Type.TypeName << "' does not exist.\"));" << std::endl; + + if (field.Type.ArrayRank > 0) { + m_Impl << "\t\t" << "}" << std::endl + << "\t" << "}" << std::endl; + } } } @@ -521,7 +542,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) /* SetField */ m_Header << "protected:" << std::endl - << "\t" << "virtual void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty);" << std::endl; + << "\t" << "virtual void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty) override;" << std::endl; m_Impl << "void ObjectImpl<" << klass.Name << ">::SetField(int id, const Value& value, bool suppress_events, const Value& cookie)" << std::endl << "{" << std::endl; @@ -565,7 +586,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) /* GetField */ m_Header << "protected:" << std::endl - << "\t" << "virtual Value GetField(int id) const;" << std::endl; + << "\t" << "virtual Value GetField(int id) const override;" << std::endl; m_Impl << "Value ObjectImpl<" << klass.Name << ">::GetField(int id) const" << std::endl << "{" << std::endl; @@ -598,7 +619,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) /* ValidateField */ m_Header << "protected:" << std::endl - << "\t" << "virtual void ValidateField(int id, const Value& value, const ValidationUtils& utils);" << std::endl; + << "\t" << "virtual void ValidateField(int id, const Value& value, const ValidationUtils& utils) override;" << std::endl; m_Impl << "void ObjectImpl<" << klass.Name << ">::ValidateField(int id, const Value& value, const ValidationUtils& utils)" << std::endl << "{" << std::endl; @@ -642,7 +663,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) /* NotifyField */ m_Header << "protected:" << std::endl - << "\t" << "virtual void NotifyField(int id, const Value& cookie = Empty);" << std::endl; + << "\t" << "virtual void NotifyField(int id, const Value& cookie = Empty) override;" << std::endl; m_Impl << "void ObjectImpl<" << klass.Name << ">::NotifyField(int id, const Value& cookie)" << std::endl << "{" << std::endl; @@ -723,17 +744,105 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) m_Impl << "void ObjectImpl<" << klass.Name << ">::Set" << it->GetFriendlyName() << "(" << it->Type.GetArgumentType() << " value, bool suppress_events, const Value& cookie)" << std::endl << "{" << std::endl; + if (it->Type.IsName || !it->TrackAccessor.empty()) + m_Impl << "\t" << "Value oldValue = Get" << it->GetFriendlyName() << "();" << std::endl; + + if (it->SetAccessor.empty() && !(it->Attributes & FANoStorage)) m_Impl << "\t" << "m_" << it->GetFriendlyName() << " = value;" << std::endl; else m_Impl << it->SetAccessor << std::endl << std::endl; - + + if (it->Type.IsName || !it->TrackAccessor.empty()) { + m_Impl << "\t" << "ConfigObject *dobj = dynamic_cast(this);" << std::endl; + + if (it->Name != "active") { + m_Impl << "\t" << "if (!dobj || dobj->IsActive())" << std::endl + << "\t"; + } + + m_Impl << "\t" << "Track" << it->GetFriendlyName() << "(oldValue, value);" << std::endl; + } + m_Impl << "\t" << "if (!suppress_events)" << std::endl << "\t\t" << "Notify" << it->GetFriendlyName() << "(cookie);" << std::endl << "}" << std::endl << std::endl; } } + m_Header << "protected:" << std::endl; + + bool needs_tracking = false; + + /* tracking */ + for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { + if (!it->Type.IsName && it->TrackAccessor.empty()) + continue; + + needs_tracking = true; + + m_Header << "\t" << "virtual void Track" << it->GetFriendlyName() << "(" << it->Type.GetArgumentType() << " oldValue, " << it->Type.GetArgumentType() << " newValue);"; + + m_Impl << "void ObjectImpl<" << klass.Name << ">::Track" << it->GetFriendlyName() << "(" << it->Type.GetArgumentType() << " oldValue, " << it->Type.GetArgumentType() << " newValue)" << std::endl + << "{" << std::endl; + + if (!it->TrackAccessor.empty()) + m_Impl << "\t" << it->TrackAccessor << std::endl; + + if (it->Type.ArrayRank > 0) { + m_Impl << "\t" << "if (oldValue) {" << std::endl + << "\t\t" << "ObjectLock olock(oldValue);" << std::endl + << "\t\t" << "BOOST_FOREACH(const String& ref, oldValue) {" << std::endl + << "\t\t\t" << "DependencyGraph::RemoveDependency(this, ConfigObject::GetObject(\"" << it->Type.TypeName << "\", ref).get());" << std::endl + << "\t\t" << "}" << std::endl + << "\t" << "}" << std::endl + << "\t" << "if (newValue) {" << std::endl + << "\t\t" << "ObjectLock olock(newValue);" << std::endl + << "\t\t" << "BOOST_FOREACH(const String& ref, newValue) {" << std::endl + << "\t\t\t" << "DependencyGraph::AddDependency(this, ConfigObject::GetObject(\"" << it->Type.TypeName << "\", ref).get());" << std::endl + << "\t\t" << "}" << std::endl + << "\t" << "}" << std::endl; + } else { + m_Impl << "\t" << "if (!oldValue.IsEmpty())" << std::endl + << "\t\t" << "DependencyGraph::RemoveDependency(this, ConfigObject::GetObject(\"" << it->Type.TypeName << "\", oldValue).get());" << std::endl + << "\t" << "if (!newValue.IsEmpty())" << std::endl + << "\t\t" << "DependencyGraph::AddDependency(this, ConfigObject::GetObject(\"" << it->Type.TypeName << "\", newValue).get());" << std::endl; + } + + m_Impl << "}" << std::endl << std::endl; + } + + /* start/stop */ + if (needs_tracking) { + m_Header << "virtual void Start(void) override;" << std::endl + << "virtual void Stop(void) override;" << std::endl; + + m_Impl << "void ObjectImpl<" << klass.Name << ">::Start(void)" << std::endl + << "{" << std::endl + << "\t" << klass.Parent << "::Start();" << std::endl << std::endl; + + for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { + if (!(it->Type.IsName)) + continue; + + m_Impl << "\t" << "Track" << it->GetFriendlyName() << "(Empty, Get" << it->GetFriendlyName() << "());" << std::endl; + } + + m_Impl << "}" << std::endl << std::endl + << "void ObjectImpl<" << klass.Name << ">::Stop(void)" << std::endl + << "{" << std::endl + << "\t" << klass.Parent << "::Stop();" << std::endl << std::endl; + + for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { + if (!(it->Type.IsName)) + continue; + + m_Impl << "\t" << "Track" << it->GetFriendlyName() << "(Get" << it->GetFriendlyName() << "(), Empty);" << std::endl; + } + + m_Impl << "}" << std::endl << std::endl; + } + /* notify */ for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { std::string prot; @@ -787,8 +896,10 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) m_Header << "private:" << std::endl; for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { - if (!(it->Attributes & FANoStorage)) - m_Header << "\t" << it->Type.GetRealType() << " m_" << it->GetFriendlyName() << ";" << std::endl; + if (it->Attributes & FANoStorage) + continue; + + m_Header << "\t" << it->Type.GetRealType() << " m_" << it->GetFriendlyName() << ";" << std::endl; } /* signal */ @@ -1178,6 +1289,7 @@ void ClassCompiler::CompileStream(const std::string& path, std::istream& input, << "#include \"base/objectlock.hpp\"" << std::endl << "#include \"base/utility.hpp\"" << std::endl << "#include \"base/convert.hpp\"" << std::endl + << "#include \"base/dependencygraph.hpp\"" << std::endl << "#include " << std::endl << "#include " << std::endl << "#ifdef _MSC_VER" << std::endl diff --git a/tools/mkclass/classcompiler.hpp b/tools/mkclass/classcompiler.hpp index 81cf1701d..db845afc4 100644 --- a/tools/mkclass/classcompiler.hpp +++ b/tools/mkclass/classcompiler.hpp @@ -42,7 +42,8 @@ enum FieldAccessorType { FTGet, FTSet, - FTDefault + FTDefault, + FTTrack }; struct FieldAccessor @@ -75,13 +76,21 @@ struct FieldType { bool IsName; std::string TypeName; + int ArrayRank; + + FieldType(void) + : IsName(false), ArrayRank(0) + { } inline std::string GetRealType(void) const { + if (ArrayRank > 0) + return "Array::Ptr"; + if (IsName) return "String"; - else - return TypeName; + + return TypeName; } inline std::string GetArgumentType(void) const @@ -106,6 +115,7 @@ struct Field std::string SetAccessor; bool PureSetAccessor; std::string DefaultAccessor; + std::string TrackAccessor; Field(void) : Attributes(0), PureGetAccessor(false), PureSetAccessor(false) @@ -203,6 +213,8 @@ public: void HandleInclude(const std::string& path, const ClassDebugInfo& locp); void HandleAngleInclude(const std::string& path, const ClassDebugInfo& locp); + void HandleImplInclude(const std::string& path, const ClassDebugInfo& locp); + void HandleAngleImplInclude(const std::string& path, const ClassDebugInfo& locp); void HandleClass(const Klass& klass, const ClassDebugInfo& locp); void HandleValidator(const Validator& validator, const ClassDebugInfo& locp); void HandleNamespaceBegin(const std::string& name, const ClassDebugInfo& locp); -- 2.50.1