1 /******************************************************************************
3 * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License *
7 * as published by the Free Software Foundation; either version 2 *
8 * of the License, or (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software Foundation *
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ******************************************************************************/
22 using namespace icinga;
24 double DynamicObject::m_CurrentTx = 0;
25 set<DynamicObject::Ptr> DynamicObject::m_ModifiedObjects;
27 boost::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnRegistered;
28 boost::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnUnregistered;
29 boost::signal<void (const set<DynamicObject::Ptr>&)> DynamicObject::OnTransactionClosing;
31 DynamicObject::DynamicObject(const Dictionary::Ptr& serializedObject)
34 RegisterAttribute("__name", Attribute_Config);
35 RegisterAttribute("__type", Attribute_Config);
36 RegisterAttribute("__local", Attribute_Config);
37 RegisterAttribute("__abstract", Attribute_Config);
38 RegisterAttribute("__source", Attribute_Local);
39 RegisterAttribute("methods", Attribute_Config);
41 if (!serializedObject->Contains("configTx"))
42 BOOST_THROW_EXCEPTION(invalid_argument("Serialized object must contain a config snapshot."));
44 /* apply config state from the config item/remote update;
45 * The DynamicObject::Create function takes care of restoring
46 * non-config state after the object has been fully constructed */
47 InternalApplyUpdate(serializedObject, Attribute_Config, true);
50 Dictionary::Ptr DynamicObject::BuildUpdate(double sinceTx, int attributeTypes) const
52 DynamicObject::AttributeConstIterator it;
54 Dictionary::Ptr attrs = boost::make_shared<Dictionary>();
56 for (it = m_Attributes.begin(); it != m_Attributes.end(); it++) {
57 if (it->second.Type == Attribute_Transient)
60 if ((it->second.Type & attributeTypes) == 0)
63 if (it->second.Tx == 0)
66 if (it->second.Tx < sinceTx && !(it->second.Type == Attribute_Config && m_ConfigTx >= sinceTx))
69 Dictionary::Ptr attr = boost::make_shared<Dictionary>();
70 attr->Set("data", it->second.Data);
71 attr->Set("type", it->second.Type);
72 attr->Set("tx", it->second.Tx);
74 attrs->Set(it->first, attr);
77 Dictionary::Ptr update = boost::make_shared<Dictionary>();
78 update->Set("attrs", attrs);
80 if (m_ConfigTx >= sinceTx && attributeTypes & Attribute_Config)
81 update->Set("configTx", m_ConfigTx);
82 else if (attrs->GetLength() == 0)
83 return Dictionary::Ptr();
88 void DynamicObject::ApplyUpdate(const Dictionary::Ptr& serializedUpdate,
91 InternalApplyUpdate(serializedUpdate, allowedTypes, false);
94 void DynamicObject::InternalApplyUpdate(const Dictionary::Ptr& serializedUpdate,
95 int allowedTypes, bool suppressEvents)
98 if ((allowedTypes & Attribute_Config) != 0 &&
99 serializedUpdate->Contains("configTx")) {
100 configTx = serializedUpdate->Get("configTx");
102 if (configTx > m_ConfigTx)
103 ClearAttributesByType(Attribute_Config);
106 Dictionary::Ptr attrs = serializedUpdate->Get("attrs");
108 Dictionary::Iterator it;
109 for (it = attrs->Begin(); it != attrs->End(); it++) {
110 if (!it->second.IsObjectType<Dictionary>())
113 Dictionary::Ptr attr = it->second;
115 int type = attr->Get("type");
117 if ((type & ~allowedTypes) != 0)
120 Value data = attr->Get("data");
121 double tx = attr->Get("tx");
123 if (type & Attribute_Config)
124 RegisterAttribute(it->first, Attribute_Config);
126 if (!HasAttribute(it->first))
127 RegisterAttribute(it->first, static_cast<DynamicAttributeType>(type));
129 InternalSetAttribute(it->first, data, tx, suppressEvents, true);
133 void DynamicObject::RegisterAttribute(const String& name,
134 DynamicAttributeType type)
136 DynamicAttribute attr;
140 pair<DynamicObject::AttributeIterator, bool> tt;
141 tt = m_Attributes.insert(make_pair(name, attr));
144 tt.first->second.Type = type;
147 void DynamicObject::Set(const String& name, const Value& data)
149 InternalSetAttribute(name, data, GetCurrentTx());
152 void DynamicObject::Touch(const String& name)
154 InternalSetAttribute(name, InternalGetAttribute(name), GetCurrentTx());
157 Value DynamicObject::Get(const String& name) const
159 return InternalGetAttribute(name);
162 void DynamicObject::InternalSetAttribute(const String& name, const Value& data,
163 double tx, bool suppressEvent, bool allowEditConfig)
165 DynamicAttribute attr;
166 attr.Type = Attribute_Transient;
170 pair<DynamicObject::AttributeIterator, bool> tt;
171 tt = m_Attributes.insert(make_pair(name, attr));
175 if (!allowEditConfig && (tt.first->second.Type & Attribute_Config))
176 BOOST_THROW_EXCEPTION(runtime_error("Config properties are immutable: '" + name + "'."));
178 if (!tt.second && tx >= tt.first->second.Tx) {
179 oldValue = tt.first->second.Data;
180 tt.first->second.Data = data;
181 tt.first->second.Tx = tx;
184 if (tt.first->second.Type & Attribute_Config)
187 if (!suppressEvent) {
188 m_ModifiedObjects.insert(GetSelf());
189 OnAttributeChanged(name, oldValue);
193 Value DynamicObject::InternalGetAttribute(const String& name) const
195 DynamicObject::AttributeConstIterator it;
196 it = m_Attributes.find(name);
198 if (it == m_Attributes.end())
201 return it->second.Data;
204 bool DynamicObject::HasAttribute(const String& name) const
206 return (m_Attributes.find(name) != m_Attributes.end());
209 void DynamicObject::ClearAttributesByType(DynamicAttributeType type)
211 DynamicObject::AttributeIterator at;
212 for (at = m_Attributes.begin(); at != m_Attributes.end(); at++) {
213 if (at->second.Type != type)
217 at->second.Data = Empty;
221 DynamicType::Ptr DynamicObject::GetType(void) const
223 String name = Get("__type");
224 return DynamicType::GetByName(name);
227 String DynamicObject::GetName(void) const
229 return Get("__name");
232 bool DynamicObject::IsLocal(void) const
234 Value value = Get("__local");
242 bool DynamicObject::IsAbstract(void) const
244 Value value = Get("__abstract");
252 void DynamicObject::SetSource(const String& value)
254 Set("__source", value);
257 String DynamicObject::GetSource(void) const
259 return Get("__source");
262 void DynamicObject::Register(void)
264 assert(Application::IsMainThread());
266 DynamicType::Ptr dtype = GetType();
268 DynamicObject::Ptr dobj = dtype->GetObject(GetName());
269 DynamicObject::Ptr self = GetSelf();
270 assert(!dobj || dobj == self);
272 dtype->RegisterObject(self);
274 OnRegistered(GetSelf());
279 void DynamicObject::Start(void)
281 /* Nothing to do here. */
284 void DynamicObject::Unregister(void)
286 assert(Application::IsMainThread());
288 DynamicType::Ptr dtype = GetType();
290 if (!dtype || !dtype->GetObject(GetName()))
293 dtype->UnregisterObject(GetSelf());
295 OnUnregistered(GetSelf());
298 ScriptTask::Ptr DynamicObject::InvokeMethod(const String& method,
299 const vector<Value>& arguments, ScriptTask::CompletionCallback callback)
301 Value value = Get("methods");
303 if (!value.IsObjectType<Dictionary>())
304 return ScriptTask::Ptr();
306 Dictionary::Ptr methods = value;
307 if (!methods->Contains(method))
308 return ScriptTask::Ptr();
310 String funcName = methods->Get(method);
312 ScriptFunction::Ptr func = ScriptFunction::GetByName(funcName);
315 BOOST_THROW_EXCEPTION(invalid_argument("Function '" + funcName + "' does not exist."));
317 ScriptTask::Ptr task = boost::make_shared<ScriptTask>(func, arguments);
318 task->Start(callback);
323 void DynamicObject::DumpObjects(const String& filename)
325 Logger::Write(LogInformation, "base", "Dumping program state to file '" + filename + "'");
327 String tempFilename = filename + ".tmp";
330 fp.open(tempFilename.CStr(), std::ios_base::out);
333 BOOST_THROW_EXCEPTION(runtime_error("Could not open '" + filename + "' file"));
335 StdioStream::Ptr sfp = boost::make_shared<StdioStream>(&fp, false);
338 DynamicType::Ptr type;
339 BOOST_FOREACH(tie(tuples::ignore, type), DynamicType::GetTypes()) {
340 DynamicObject::Ptr object;
341 BOOST_FOREACH(tie(tuples::ignore, object), type->GetObjects()) {
342 if (object->IsLocal())
345 Dictionary::Ptr persistentObject = boost::make_shared<Dictionary>();
347 persistentObject->Set("type", object->GetType()->GetName());
348 persistentObject->Set("name", object->GetName());
350 int types = Attribute_Local | Attribute_Replicated;
352 /* only persist properties for replicated objects or for objects
353 * that are marked as persistent */
354 if (!object->GetSource().IsEmpty() /*|| object->IsPersistent()*/)
355 types |= Attribute_Config;
357 Dictionary::Ptr update = object->BuildUpdate(0, types);
362 persistentObject->Set("update", update);
364 Value value = persistentObject;
365 String json = value.Serialize();
367 NetString::WriteStringToStream(sfp, json);
376 _unlink(filename.CStr());
379 if (rename(tempFilename.CStr(), filename.CStr()) < 0)
380 BOOST_THROW_EXCEPTION(PosixException("rename() failed", errno));
383 void DynamicObject::RestoreObjects(const String& filename)
385 Logger::Write(LogInformation, "base", "Restoring program state from file '" + filename + "'");
388 fp.open(filename.CStr(), std::ios_base::in);
390 StdioStream::Ptr sfp = boost::make_shared<StdioStream>(&fp, false);
393 unsigned long restored = 0;
396 while (NetString::ReadStringFromStream(sfp, &message)) {
397 Dictionary::Ptr persistentObject = Value::Deserialize(message);
399 String type = persistentObject->Get("type");
400 String name = persistentObject->Get("name");
401 Dictionary::Ptr update = persistentObject->Get("update");
403 bool hasConfig = update->Contains("configTx");
405 DynamicType::Ptr dt = DynamicType::GetByName(type);
408 BOOST_THROW_EXCEPTION(invalid_argument("Invalid type: " + type));
410 DynamicObject::Ptr object = dt->GetObject(name);
412 if (hasConfig && !object) {
413 object = dt->CreateObject(update);
416 object->ApplyUpdate(update, Attribute_All);
425 msgbuf << "Restored " << restored << " objects";
426 Logger::Write(LogDebug, "base", msgbuf.str());
429 void DynamicObject::DeactivateObjects(void)
431 DynamicType::TypeMap::iterator tt;
432 for (tt = DynamicType::GetTypes().begin(); tt != DynamicType::GetTypes().end(); tt++) {
433 DynamicType::NameMap::iterator nt;
435 while ((nt = tt->second->GetObjects().begin()) != tt->second->GetObjects().end()) {
436 DynamicObject::Ptr object = nt->second;
438 object->Unregister();
443 double DynamicObject::GetCurrentTx(void)
445 assert(m_CurrentTx != 0);
450 void DynamicObject::BeginTx(void)
452 m_CurrentTx = Utility::GetTime();
455 void DynamicObject::FinishTx(void)
457 OnTransactionClosing(m_ModifiedObjects);
458 m_ModifiedObjects.clear();
463 void DynamicObject::FlushTx(void)
469 void DynamicObject::OnAttributeChanged(const String&, const Value&)
472 DynamicObject::Ptr DynamicObject::GetObject(const String& type, const String& name)
474 DynamicType::Ptr dtype = DynamicType::GetByName(type);
475 return dtype->GetObject(name);
478 const DynamicObject::AttributeMap& DynamicObject::GetAttributes(void) const