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
} else
ClosePidFile(true);
- ConfigObject::Stop();
+ ObjectImpl<Application>::Stop();
}
Application::~Application(void)
void ConfigObject::Start(void)
{
+ ObjectImpl<ConfigObject>::Start();
+
ASSERT(!OwnsLock());
ObjectLock olock(this);
void ConfigObject::Stop(void)
{
+ ObjectImpl<ConfigObject>::Stop();
+
ASSERT(!OwnsLock());
ObjectLock olock(this);
ConfigObject::Ptr ConfigObject::GetObject(const String& type, const String& name)
{
ConfigType::Ptr dtype = ConfigType::GetByName(type);
+ if (!dtype)
+ return ConfigObject::Ptr();
return dtype->GetObject(name);
}
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);
return static_pointer_cast<T>(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);
explicit ConfigObject(void);
private:
- static ConfigObject::Ptr GetObject(const String& type, const String& name);
static void RestoreObject(const String& message, int attributeTypes);
};
m_DebugInfo = di;
}
+ inline virtual void Start(void)
+ { }
+
+ inline virtual void Stop(void)
+ { }
+
private:
DebugInfo m_DebugInfo;
};
--- /dev/null
+/******************************************************************************
+ * 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 <boost/foreach.hpp>
+
+using namespace icinga;
+
+boost::mutex DependencyGraph::m_Mutex;
+std::map<Object *, std::map<Object *, int> > 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<Object::Ptr> DependencyGraph::GetParents(const Object::Ptr& child)
+{
+ std::vector<Object::Ptr> objects;
+
+ boost::mutex::scoped_lock lock(m_Mutex);
+ std::map<Object *, std::map<Object *, int> >::const_iterator it = m_Dependencies.find(child.get());
+
+ if (it != m_Dependencies.end()) {
+ typedef std::pair<Object *, int> kv_pair;
+ BOOST_FOREACH(const kv_pair& kv, it->second) {
+ objects.push_back(kv.first);
+ }
+ }
+
+ return objects;
+}
--- /dev/null
+/******************************************************************************
+ * 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 <map>
+
+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<Object::Ptr> GetParents(const Object::Ptr& child);
+
+private:
+ DependencyGraph(void);
+
+ static boost::mutex m_Mutex;
+ static std::map<Object *, std::map<Object *, int> > m_Dependencies;
+};
+
+}
+
+#endif /* DEPENDENCYGRAPH_H */
*/
void FileLogger::Start(void)
{
- StreamLogger::Start();
+ ObjectImpl<FileLogger>::Start();
ReopenLogFile();
*/
void Logger::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<Logger>::Start();
boost::mutex::scoped_lock lock(m_Mutex);
m_Loggers.insert(this);
m_Loggers.erase(this);
}
- ConfigObject::Stop();
+ ObjectImpl<Logger>::Stop();
}
std::set<Logger::Ptr> Logger::GetLoggers(void)
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. */
virtual intrusive_ptr<Type> 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);
#include "base/objectlock.hpp"
#include "base/configtype.hpp"
#include "base/application.hpp"
+#include "base/dependencygraph.hpp"
#include <boost/foreach.hpp>
#include <boost/regex.hpp>
#include <algorithm>
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)
{
return String();
#endif /* _WIN32 */
}
+
+Array::Ptr ScriptUtils::TrackParents(const Object::Ptr& child)
+{
+ return Array::FromVector(DependencyGraph::GetParents(child));
+}
+
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);
void StreamLogger::Stop(void)
{
- Logger::Stop();
+ ObjectImpl<StreamLogger>::Stop();
// make sure we flush the log data on shutdown, even if we don't call the destructor
if (m_Stream)
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.");
}
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)
{ }
};
void CheckerComponent::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<CheckerComponent>::Start();
m_Thread = boost::thread(boost::bind(&CheckerComponent::CheckThreadProc, this));
m_ResultTimer->Stop();
m_Thread.join();
- ConfigObject::Stop();
+ ObjectImpl<CheckerComponent>::Stop();
}
void CheckerComponent::CheckThreadProc(void)
*/
void CompatLogger::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<CompatLogger>::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));
*/
void ExternalCommandListener::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<ExternalCommandListener>::Start();
#ifndef _WIN32
m_CommandThread = boost::thread(boost::bind(&ExternalCommandListener::CommandPipeThread, this, GetCommandPath()));
*/
void StatusDataWriter::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<StatusDataWriter>::Start();
m_StatusTimer = new Timer();
m_StatusTimer->SetInterval(GetUpdateInterval());
void DbConnection::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<DbConnection>::Start();
DbObject::OnQuery.connect(boost::bind(&DbConnection::ExecuteQuery, this, _1));
ConfigObject::OnActiveChanged.connect(boost::bind(&DbConnection::UpdateObject, this, _1));
*/
void Demo::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<Demo>::Start();
m_DemoTimer = new Timer();
m_DemoTimer->SetInterval(5);
if (GetNextCheck() < now + 300)
UpdateNextCheck();
- ConfigObject::Start();
+ ObjectImpl<Checkable>::Start();
}
void Checkable::OnStateLoaded(void)
void Dependency::OnAllConfigLoaded(void)
{
- ConfigObject::OnAllConfigLoaded();
+ ObjectImpl<Dependency>::OnAllConfigLoaded();
Host::Ptr childHost = Host::GetByName(GetChildHostName());
void Dependency::Stop(void)
{
- ConfigObject::Stop();
+ ObjectImpl<Dependency>::Stop();
GetChild()->RemoveDependency(this);
GetParent()->RemoveReverseDependency(this);
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."));
}
+
#include "icinga/customvarobject.hpp"
#include "icinga/checkable.hpp"
+#impl_include "icinga/service.hpp"
library icinga;
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 {
};
};
-validator Dependency {
- Array states {
- Number "*";
- };
-};
-
}
void Host::OnAllConfigLoaded(void)
{
- Checkable::OnAllConfigLoaded();
+ ObjectImpl<Host>::OnAllConfigLoaded();
HostGroup::EvaluateObjectRules(this);
void Host::Stop(void)
{
- Checkable::Stop();
+ ObjectImpl<Host>::Stop();
Array::Ptr groups = GetGroups();
class Host : Checkable
{
- [config] Array::Ptr groups {
+ [config] array(name(HostGroup)) groups {
default {{{ return new Array(); }}}
};
};
-validator Host {
- Array groups {
- name(HostGroup) "*";
- };
-};
-
}
}}}
};
- [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) "*";
- };
-};
-
}
*/
void IcingaStatusWriter::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<IcingaStatusWriter>::Start();
m_StatusTimer = new Timer();
m_StatusTimer->SetInterval(GetUpdateInterval());
void Notification::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<Notification>::Start();
Checkable::Ptr obj = GetCheckable();
void Notification::Stop(void)
{
- ConfigObject::Stop();
+ ObjectImpl<Notification>::Stop();
Checkable::Ptr obj = GetCheckable();
{
return Endpoint::GetByName(GetCommandEndpointRaw());
}
+
******************************************************************************/
#include "icinga/customvarobject.hpp"
+#impl_include "icinga/service.hpp"
library icinga;
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(); }}}
};
validator Notification {
- Array users {
- name(User) "*";
- };
-
- Array user_groups {
- name(UserGroup) "*";
- };
-
Dictionary times {
Number begin;
Number end;
};
-
- Array types {
- Number "*";
- };
-
- Array states {
- Number "*";
- };
};
}
void ScheduledDowntime::OnAllConfigLoaded(void)
{
- CustomVarObject::OnAllConfigLoaded();
+ ObjectImpl<ScheduledDowntime>::OnAllConfigLoaded();
if (!GetCheckable())
BOOST_THROW_EXCEPTION(ScriptError("ScheduledDowntime '" + GetName() + "' references a host/service which doesn't exist.", GetDebugInfo()));
void ScheduledDowntime::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<ScheduledDowntime>::Start();
CreateNextDowntime();
}
}
}
}
+
******************************************************************************/
#include "icinga/customvarobject.hpp"
+#impl_include "icinga/service.hpp"
library icinga;
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;
void Service::OnAllConfigLoaded(void)
{
- Checkable::OnAllConfigLoaded();
+ ObjectImpl<Service>::OnAllConfigLoaded();
m_Host = Host::GetByName(GetHostName());
{
load_after Host;
- [config] Array::Ptr groups {
+ [config] array(name(ServiceGroup)) groups {
default {{{ return new Array(); }}}
};
};
};
-validator Service {
- Array groups {
- name(ServiceGroup) "*";
- };
-};
-
}
}}}
};
- [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) "*";
- };
-};
-
}
void TimePeriod::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<TimePeriod>::Start();
/* Pre-fill the time period for the next 24 hours. */
double now = Utility::GetTime();
void User::OnConfigLoaded(void)
{
- ConfigObject::OnConfigLoaded();
+ ObjectImpl<User>::OnConfigLoaded();
SetTypeFilter(FilterArrayToInt(GetTypes(), ~0));
SetStateFilter(FilterArrayToInt(GetStates(), ~0));
void User::OnAllConfigLoaded(void)
{
- ConfigObject::OnAllConfigLoaded();
+ ObjectImpl<User>::OnAllConfigLoaded();
UserGroup::EvaluateObjectRules(this);
void User::Stop(void)
{
- ConfigObject::Stop();
+ ObjectImpl<User>::Stop();
Array::Ptr groups = GetGroups();
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;
[state] double last_notification;
};
-validator User {
- Array groups {
- name(UserGroup) "*";
- };
-
- Array types {
- Number "*";
- };
-
- Array states {
- Number "*";
- };
-};
-
}
}}}
};
- [config] Array::Ptr groups;
-};
-
-validator UserGroup {
- Array groups {
- name(UserGroup) "*";
- };
+ [config] array(name(UserGroup)) groups;
};
}
*/
void LivestatusListener::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<LivestatusListener>::Start();
if (GetSocketType() == "tcp") {
TcpSocket::Ptr socket = new TcpSocket();
void LivestatusListener::Stop(void)
{
- ConfigObject::Stop();
+ ObjectImpl<LivestatusListener>::Stop();
m_Listener->Close();
*/
void NotificationComponent::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<NotificationComponent>::Start();
Checkable::OnNotificationsRequested.connect(boost::bind(&NotificationComponent::SendNotificationsHandler, this, _1,
_2, _3, _4, _5));
void GelfWriter::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<GelfWriter>::Start();
m_ReconnectTimer = new Timer();
m_ReconnectTimer->SetInterval(10);
void GraphiteWriter::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<GraphiteWriter>::Start();
m_ReconnectTimer = new Timer();
m_ReconnectTimer->SetInterval(10);
void OpenTsdbWriter::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<OpenTsdbWriter>::Start();
m_ReconnectTimer = new Timer();
m_ReconnectTimer->SetInterval(10);
void PerfdataWriter::Start(void)
{
- ConfigObject::Start();
+ ObjectImpl<PerfdataWriter>::Start();
Checkable::OnNewCheckResult.connect(boost::bind(&PerfdataWriter::CheckResultHandler, this, _1, _2));
return;
}
- ConfigObject::Start();
+ ObjectImpl<ApiListener>::Start();
{
boost::mutex::scoped_lock(m_LogLock);
#include "remote/httputility.hpp"
#include "remote/filterutility.hpp"
#include "base/serializer.hpp"
+#include "base/dependencygraph.hpp"
+#include "base/configtype.hpp"
#include <boost/algorithm/string.hpp>
#include <set>
}
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;
Value val = static_cast<Object::Ptr>(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<ConfigObject>(pobj);
+
+ if (!configObj)
+ continue;
+
+ Dictionary::Ptr refInfo = new Dictionary();
+ refInfo->Set("type", configObj->GetType()->GetName());
+ refInfo->Set("name", configObj->GetName());
+ used_by->Add(refInfo);
}
}
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) "*";
- };
-};
-
}
[ \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; }
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; }
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)"
%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)"
%type <text> type_base_specifier
%type <text> include
%type <text> angle_include
+%type <text> impl_include
+%type <text> angle_impl_include
%type <text> code
%type <num> T_FIELD_ATTRIBUTE
%type <num> field_attribute
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);
}
;
+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);
$$ = 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 ';'
case FTDefault:
field->DefaultAccessor = it->Accessor;
break;
- }
+ case FTTrack:
+ field->TrackAccessor = it->Accessor;
+ break;
+ }
}
delete $5;
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
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++;
}
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;
}
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<ConfigObject *>(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;
+ }
}
}
/* 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;
/* 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;
/* 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;
/* 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;
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<ConfigObject *>(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;
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 */
<< "#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 <boost/foreach.hpp>" << std::endl
<< "#include <boost/assign/list_of.hpp>" << std::endl
<< "#ifdef _MSC_VER" << std::endl
{
FTGet,
FTSet,
- FTDefault
+ FTDefault,
+ FTTrack
};
struct FieldAccessor
{
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
std::string SetAccessor;
bool PureSetAccessor;
std::string DefaultAccessor;
+ std::string TrackAccessor;
Field(void)
: Attributes(0), PureGetAccessor(false), PureSetAccessor(false)
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);