void handle_get(DBClientBase *client, DBOperation *operation) { BSONObj obj; try { obj = client->findOne(m_obj_collection, BSON("_id" << operation->doid())); } catch(mongo::DBException &e) { m_log->error() << "Unexpected error occurred while trying to" " retrieve object with DOID " << operation->doid() << ": " << e.what() << endl; operation->on_failure(); return; } if(obj.isEmpty()) { m_log->warning() << "Got queried for non-existent object with DOID " << operation->doid() << endl; operation->on_failure(); return; } DBObjectSnapshot *snap = format_snapshot(operation->doid(), obj); if(!snap || !operation->verify_class(snap->m_dclass)) { operation->on_failure(); } else { operation->on_complete(snap); } }
void set_field(doid_t do_id, const Field* field, const val_t &value) { m_log->trace() << "Setting field on obj-" << do_id << endl; YAML::Node document; if(!load(do_id, document)) { return; } // Get the fields from the file that are not being updated const Class* dcc = g_dcf->get_class_by_name(document["class"].as<string>()); ObjectData dbo(dcc->get_id()); YAML::Node existing = document["fields"]; for(auto it = existing.begin(); it != existing.end(); ++it) { const Field* field = dcc->get_field_by_name(it->first.as<string>()); if(!field) { m_log->warning() << "Field '" << it->first.as<string>() << "', loaded from '" << filename(do_id) << "', does not exist." << endl; continue; } vector<uint8_t> value = read_yaml_field(field, it->second, do_id); if(value.size() > 0) { dbo.fields[field] = value; } } dbo.fields[field] = value; write_yaml_object(do_id, dcc, dbo); }
bool set_field_if_equals(doid_t do_id, const Field* field, const val_t &equal, val_t &value) { m_log->trace() << "Setting field if equal on obj-" << do_id << endl; YAML::Node document; if(!load(do_id, document)) { value = vector<uint8_t>(); return false; } // Get current field values from the file const Class* dcc = g_dcf->get_class_by_name(document["class"].as<string>()); ObjectData dbo(dcc->get_id()); YAML::Node existing = document["fields"]; for(auto it = existing.begin(); it != existing.end(); ++it) { const Field* field = dcc->get_field_by_name(it->first.as<string>()); if(!field) { m_log->warning() << "Field '" << it->first.as<string>() << "', loaded from '" << filename(do_id) << "', does not exist." << endl; continue; } vector<uint8_t> value = read_yaml_field(field, it->second, do_id); if(value.size() > 0) { dbo.fields[field] = value; } } auto found = dbo.fields.find(field); if(found == dbo.fields.end() || found->second != equal) { value = dbo.fields[field]; return false; } dbo.fields[field] = value; write_yaml_object(do_id, dcc, dbo); return true; }
// Get a DBObjectSnapshot from a MongoDB BSON object; returns NULL if failure. DBObjectSnapshot *format_snapshot(doid_t doid, const BSONObj &obj) { m_log->trace() << "Formatting database snapshot of " << doid << ": " << obj << endl; try { string dclass_name = obj["dclass"].String(); const dclass::Class *dclass = g_dcf->get_class_by_name(dclass_name); if(!dclass) { m_log->error() << "Encountered unknown database object: " << dclass_name << "(" << doid << ")" << endl; return NULL; } BSONObj fields = obj["fields"].Obj(); DBObjectSnapshot *snap = new DBObjectSnapshot(); snap->m_dclass = dclass; for(auto it = fields.begin(); it.more(); ++it) { const char *name = (*it).fieldName(); const dclass::Field *field = dclass->get_field_by_name(name); if(!field) { m_log->warning() << "Encountered unexpected field " << name << " while formatting " << dclass_name << "(" << doid << "); ignored." << endl; continue; } { DatagramPtr dg = Datagram::create(); bson2bamboo(field->get_type(), *it, *dg); snap->m_fields[field].resize(dg->size()); memcpy(snap->m_fields[field].data(), dg->get_data(), dg->size()); } } return snap; } catch(mongo::DBException &e) { m_log->error() << "Unexpected error while trying to format" " database snapshot for " << doid << ": " << e.what() << endl; return NULL; } }
bool get_object(doid_t do_id, ObjectData &dbo) { m_log->trace() << "Getting obj-" << do_id << " ..." << endl; // Open file for object YAML::Node document; if(!load(do_id, document)) { return false; } // Read object's DistributedClass const Class* dcc = g_dcf->get_class_by_name(document["class"].as<string>()); dbo.dc_id = dcc->get_id(); // Read object's fields YAML::Node fields = document["fields"]; for(auto it = fields.begin(); it != fields.end(); ++it) { const Field* field = dcc->get_field_by_name(it->first.as<string>()); if(!field) { m_log->warning() << "Field '" << it->first.as<string>() << "', loaded from '" << filename(do_id) << "', does not exist." << endl; continue; } vector<uint8_t> value = read_yaml_field(field, it->second, do_id); if(value.size() > 0) { dbo.fields[field] = value; } } return true; }
virtual void handle_datagram(Datagram &in_dg, DatagramIterator &dgi) { channel_t sender = dgi.read_uint64(); unsigned short msg_type = dgi.read_uint16(); switch(msg_type) { case DBSERVER_CREATE_STORED_OBJECT: { unsigned int context = dgi.read_uint32(); // Start response with generic header Datagram resp; resp.add_server_header(sender, m_control_channel, DBSERVER_CREATE_STORED_OBJECT_RESP); resp.add_uint32(context); // Get DistributedClass unsigned short dc_id = dgi.read_uint16(); DCClass *dcc = gDCF->get_class(dc_id); if(!dcc) { m_log->error() << "Invalid DCClass when creating object: #" << dc_id << std::endl; resp.add_uint32(0); send(resp); return; } // Unpack fields to be passed to database DatabaseObject dbo(dc_id); unsigned short field_count = dgi.read_uint16(); m_log->spam() << "Unpacking fields..." << std::endl; try { for(unsigned int i = 0; i < field_count; ++i) { unsigned short field_id = dgi.read_uint16(); DCField *field = dcc->get_field_by_index(field_id); if(field) { if(field->is_db()) { dgi.unpack_field(field, dbo.fields[field]); } else { std::string tmp; dgi.unpack_field(field, tmp); } } } } catch(std::exception &e) { m_log->error() << "Error while unpacking fields, msg may be truncated. e.what(): " << e.what() << std::endl; resp.add_uint32(0); send(resp); return; } // Check for required fields, and populate with defaults m_log->spam() << "Checking all required fields exist..." << std::endl; for(int i = 0; i < dcc->get_num_inherited_fields(); ++i) { DCField *field = dcc->get_inherited_field(i); if(field->is_required() && field->is_db() && !field->as_molecular_field()) { if(dbo.fields.find(field) == dbo.fields.end()) { if(!field->has_default_value()) { m_log->error() << "Field " << field->get_name() << " missing when trying to create " "object of type " << dcc->get_name(); resp.add_uint32(0); send(resp); return; } else { dbo.fields[field] = field->get_default_value(); } } } } // Create object in database m_log->spam() << "Creating stored object..." << std::endl; unsigned int do_id = m_db_engine->create_object(dbo); if(do_id == 0 || do_id < m_min_id || do_id > m_max_id) { m_log->error() << "Ran out of DistributedObject ids while creating new object." << std::endl; resp.add_uint32(0); send(resp); return; } m_log->spam() << "... created with ID: " << do_id << std::endl; resp.add_uint32(do_id); send(resp); } break; case DBSERVER_SELECT_STORED_OBJECT_ALL: { unsigned int context = dgi.read_uint32(); Datagram resp; resp.add_server_header(sender, m_control_channel, DBSERVER_SELECT_STORED_OBJECT_ALL_RESP); resp.add_uint32(context); unsigned int do_id = dgi.read_uint32(); DatabaseObject dbo; if(m_db_engine->get_object(do_id, dbo)) { resp.add_uint8(1); resp.add_uint16(dbo.dc_id); resp.add_uint16(dbo.fields.size()); for(auto it = dbo.fields.begin(); it != dbo.fields.end(); ++it) { resp.add_uint16(it->first->get_number()); resp.add_data(it->second); } } else { resp.add_uint8(0); } send(resp); } break; case DBSERVER_DELETE_STORED_OBJECT: { if(dgi.read_uint32() == DBSERVER_DELETE_STORED_OBJECT_VERIFY_CODE) { unsigned int do_id = dgi.read_uint32(); m_db_engine->delete_object(do_id); m_log->debug() << "Deleted object with ID: " << do_id << std::endl; } else { m_log->warning() << "Wrong delete verify code." << std::endl; } } break; default: m_log->error() << "Recieved unknown MsgType: " << msg_type << std::endl; }; }
bool set_fields_if_equals(doid_t do_id, const map_t &equals, map_t &values) { m_log->trace() << "Setting fields if equals on obj-" << do_id << endl; YAML::Node document; if(!load(do_id, document)) { values.clear(); return false; } // Get current field values from the file const Class* dcc = g_dcf->get_class_by_name(document["class"].as<string>()); ObjectData dbo(dcc->get_id()); YAML::Node existing = document["fields"]; for(auto it = existing.begin(); it != existing.end(); ++it) { const Field* field = dcc->get_field_by_name(it->first.as<string>()); if(!field) { m_log->warning() << "Field '" << it->first.as<string>() << "', loaded from '" << filename(do_id) << "', does not exist." << endl; continue; } vector<uint8_t> value = read_yaml_field(field, it->second, do_id); if(value.size() > 0) { dbo.fields[field] = value; } } // Check if equals matches current values bool fail = false; for(auto it = equals.begin(); it != equals.end(); ++it) { auto found = dbo.fields.find(it->first); if(found == dbo.fields.end()) { values.erase(it->first); fail = true; } else if(it->second != found->second) { values.erase(it->first); fail = true; } } // Return current values on failure if(fail) { for(auto it = values.begin(); it != values.end(); ++it) { it->second = dbo.fields[it->first]; } return false; } // Update existing values on success for(auto it = values.begin(); it != values.end(); ++it) { dbo.fields[it->first] = it->second; } write_yaml_object(do_id, dcc, dbo); return true; }