static void bson2bamboo(const dclass::DistributedType *type, const BSONElement &element, Datagram &dg) { switch(type->get_type()) { case dclass::Type::T_INT8: { dg.add_int8(element.Int()); } break; case dclass::Type::T_INT16: { dg.add_int16(element.Int()); } break; case dclass::Type::T_INT32: { dg.add_int32(element.Int()); } break; case dclass::Type::T_INT64: { dg.add_int64(element.Int()); } break; case dclass::Type::T_UINT8: { dg.add_uint8(element.Int()); } break; case dclass::Type::T_UINT16: { dg.add_uint16(element.Int()); } break; case dclass::Type::T_UINT32: { dg.add_uint32(element.Int()); } break; case dclass::Type::T_UINT64: { dg.add_uint64(element.Int()); } break; case dclass::Type::T_CHAR: { string str = element.String(); if(str.size() != 1) { throw mongo::DBException("Expected single-length string for char field", 0); } dg.add_uint8(str[0]); } break; case dclass::Type::T_FLOAT32: { dg.add_float32(element.Number()); } break; case dclass::Type::T_FLOAT64: { dg.add_float64(element.Number()); } break; case dclass::Type::T_STRING: { dg.add_data(element.String()); } break; case dclass::Type::T_VARSTRING: { dg.add_string(element.String()); } break; case dclass::Type::T_BLOB: { int len; const uint8_t *rawdata = (const uint8_t *)element.binData(len); dg.add_data(rawdata, len); } break; case dclass::Type::T_VARBLOB: { int len; const uint8_t *rawdata = (const uint8_t *)element.binData(len); dg.add_blob(rawdata, len); } break; case dclass::Type::T_ARRAY: { const dclass::ArrayType *array = type->as_array(); std::vector<BSONElement> data = element.Array(); for(auto it = data.begin(); it != data.end(); ++it) { bson2bamboo(array->get_element_type(), *it, dg); } } break; case dclass::Type::T_VARARRAY: { const dclass::ArrayType *array = type->as_array(); std::vector<BSONElement> data = element.Array(); DatagramPtr newdg = Datagram::create(); for(auto it = data.begin(); it != data.end(); ++it) { bson2bamboo(array->get_element_type(), *it, *newdg); } dg.add_blob(newdg->get_data(), newdg->size()); } break; case dclass::Type::T_STRUCT: { const dclass::Struct *s = type->as_struct(); size_t fields = s->get_num_fields(); for(unsigned int i = 0; i < fields; ++i) { const dclass::Field *field = s->get_field(i); bson2bamboo(field->get_type(), element[field->get_name()], dg); } } break; case dclass::Type::T_METHOD: { const dclass::Method *m = type->as_method(); size_t parameters = m->get_num_parameters(); for(unsigned int i = 0; i < parameters; ++i) { const dclass::Parameter *parameter = m->get_parameter(i); string name = parameter->get_name(); if(name.empty() || element[name].eoo()) { stringstream n; n << "_" << i; name = n.str(); } bson2bamboo(parameter->get_type(), element[name], dg); } } break; case dclass::Type::T_INVALID: default: assert(false); break; } }
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; }; }
void DBStateServer::handle_datagram(Datagram &in_dg, DatagramIterator &dgi) { channel_t sender = dgi.read_uint64(); uint16_t msgtype = dgi.read_uint16(); switch(msgtype) { case DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS: { handle_activate(dgi, false); break; } case DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS_OTHER: { handle_activate(dgi, true); break; } case DBSS_OBJECT_DELETE_DISK: { uint32_t do_id = dgi.read_uint32(); // If object exists broadcast the delete message auto obj_keyval = m_objs.find(do_id); if(obj_keyval != m_objs.end()) { DistributedObject* obj = obj_keyval->second; std::set<channel_t> targets; // Add location to broadcast if(obj->get_location()) { targets.insert(obj->get_location()); } // Add AI to broadcast if(obj->get_ai()) { targets.insert(obj->get_ai()); } // Add owner to broadcast if(obj->get_owner()) { targets.insert(obj->get_owner()); } // Build and send datagram Datagram dg(targets, sender, DBSS_OBJECT_DELETE_DISK); dg.add_uint32(do_id); send(dg); } // Send delete to database Datagram dg(m_db_channel, do_id, DBSERVER_OBJECT_DELETE); dg.add_uint32(do_id); send(dg); break; } case STATESERVER_OBJECT_SET_FIELD: { uint32_t do_id = dgi.read_uint32(); uint32_t field_id = dgi.read_uint16(); DCField* field = g_dcf->get_field_by_index(field_id); if(field && field->is_db()) { m_log->trace() << "Forwarding SetField for field with id " << field_id << ", on object " << do_id << " to database." << std::endl; Datagram dg(m_db_channel, do_id, DBSERVER_OBJECT_SET_FIELD); dg.add_uint32(do_id); dg.add_uint16(field_id); dg.add_data(dgi.read_remainder()); send(dg); } break; } case STATESERVER_OBJECT_SET_FIELDS: { uint32_t do_id = dgi.read_uint32(); uint16_t field_count = dgi.read_uint16(); std::unordered_map<DCField*, std::vector<uint8_t> > db_fields; for(uint16_t i = 0; i < field_count; ++i) { uint16_t field_id = dgi.read_uint16(); DCField* field = g_dcf->get_field_by_index(field_id); if(!field) { m_log->warning() << "Received invalid field in SetFields" " with id " << field_id << std::endl; break; } if(field->is_db()) { dgi.unpack_field(field, db_fields[field]); } } if(db_fields.size() > 0) { m_log->trace() << "Forwarding SetFields on object " << do_id << " to database." << std::endl; Datagram dg(m_db_channel, do_id, DBSERVER_OBJECT_SET_FIELDS); dg.add_uint32(do_id); dg.add_uint16(field_count); for(auto it = db_fields.begin(); it != db_fields.end(); ++it) { dg.add_uint16(it->first->get_number()); dg.add_data(it->second); } send(dg); } break; } case STATESERVER_OBJECT_GET_FIELD: { uint32_t r_context = dgi.read_uint32(); uint32_t r_do_id = dgi.read_uint32(); uint16_t field_id = dgi.read_uint16(); // If object is active or loading, the Object or Loader will handle it if(m_objs.find(r_do_id) != m_objs.end() || m_loading.find(r_do_id) != m_loading.end()) { break; } m_log->trace() << "Received GetField for field with id " << field_id << " on inactive object with id " << r_do_id << std::endl; // Check field is "ram db" or "required" DCField* field = g_dcf->get_field_by_index(field_id); if(!field || !(field->is_required() || (field->is_ram() && field->is_db()))) { Datagram dg(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP); dg.add_uint32(r_context); dg.add_uint8(false); send(dg); } if(field->is_db()) { // Get context for db query uint32_t db_context = m_next_context++; // Prepare reponse datagram if(m_context_datagrams.find(db_context) == m_context_datagrams.end()) { m_context_datagrams[db_context].add_server_header(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP); } m_context_datagrams[db_context].add_uint32(r_context); // Send query to database Datagram dg(m_db_channel, r_do_id, DBSERVER_OBJECT_GET_FIELD); dg.add_uint32(db_context); dg.add_uint32(r_do_id); dg.add_uint16(field_id); send(dg); } else // Field is required and not-db { Datagram dg = Datagram(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP); dg.add_uint32(r_context); dg.add_uint8(true); dg.add_uint16(field_id); dg.add_data(field->get_default_value()); send(dg); } break; } case DBSERVER_OBJECT_GET_FIELD_RESP: { uint32_t db_context = dgi.read_uint32(); // Check context auto dg_keyval = m_context_datagrams.find(db_context); if(dg_keyval == m_context_datagrams.end()) { break; // Not meant for me, handled by LoadingObject } m_log->trace() << "Received GetFieldResp from database." << std::endl; // Get the datagram from the db_context Datagram dg(dg_keyval->second); // Cleanup the context m_context_datagrams.erase(db_context); // Check to make sure the datagram is appropriate DatagramIterator check_dgi = DatagramIterator(dg); uint16_t resp_type = check_dgi.get_msg_type(); if(resp_type != STATESERVER_OBJECT_GET_FIELD_RESP) { if(resp_type == STATESERVER_OBJECT_GET_FIELDS_RESP) { m_log->warning() << "Received GetFieldsResp, but expecting GetFieldResp." << std::endl; } else if(resp_type == STATESERVER_OBJECT_GET_ALL_RESP) { m_log->warning() << "Received GetAllResp, but expecting GetFieldResp." << std::endl; } break; } // Add database field payload to response (don't know dclass, so must copy payload) and send dg.add_data(dgi.read_remainder()); send(dg); break; } case STATESERVER_OBJECT_GET_FIELDS: { uint32_t r_context = dgi.read_uint32(); uint32_t r_do_id = dgi.read_uint32(); uint16_t field_count = dgi.read_uint16(); // If object is active or loading, the Object or Loader will handle it if(m_objs.find(r_do_id) != m_objs.end() || m_loading.find(r_do_id) != m_loading.end()) { break; } m_log->trace() << "Received GetFields for inactive object with id " << r_do_id << std::endl; // Read requested fields from datagram std::list<DCField*> db_fields; // Ram|required db fields in request std::list<DCField*> ram_fields; // Ram|required but not-db fields in request for(uint16_t i = 0; i < field_count; ++i) { uint16_t field_id = dgi.read_uint16(); DCField* field = g_dcf->get_field_by_index(field_id); if(!field) { Datagram dg(sender, r_do_id, STATESERVER_OBJECT_GET_FIELDS_RESP); dg.add_uint32(r_context); dg.add_uint8(false); send(dg); } else if(field->is_ram() || field->is_required()) { if(field->is_db()) { db_fields.push_back(field); } else { ram_fields.push_back(field); } } } if(db_fields.size()) { // Get context for db query uint32_t db_context = m_next_context++; // Prepare reponse datagram if(m_context_datagrams.find(db_context) == m_context_datagrams.end()) { m_context_datagrams[db_context].add_server_header(sender, r_do_id, STATESERVER_OBJECT_GET_FIELDS_RESP); } m_context_datagrams[db_context].add_uint32(r_context); m_context_datagrams[db_context].add_uint8(true); m_context_datagrams[db_context].add_uint16(ram_fields.size() + db_fields.size()); for(auto it = ram_fields.begin(); it != ram_fields.end(); ++it) { m_context_datagrams[db_context].add_uint16((*it)->get_number()); m_context_datagrams[db_context].add_data((*it)->get_default_value()); } // Send query to database Datagram dg(m_db_channel, r_do_id, DBSERVER_OBJECT_GET_FIELDS); dg.add_uint32(db_context); dg.add_uint32(r_do_id); dg.add_uint16(db_fields.size()); for(auto it = db_fields.begin(); it != db_fields.end(); ++it) { dg.add_uint16((*it)->get_number()); } send(dg); } else // If no database fields exist { Datagram dg = Datagram(sender, r_do_id, STATESERVER_OBJECT_GET_FIELDS_RESP); dg.add_uint32(r_context); dg.add_uint8(true); dg.add_uint16(ram_fields.size()); for(auto it = ram_fields.begin(); it != ram_fields.end(); ++it) { dg.add_uint16((*it)->get_number()); dg.add_data((*it)->get_default_value()); } send(dg); } break; } case DBSERVER_OBJECT_GET_FIELDS_RESP: { uint32_t db_context = dgi.read_uint32(); // Check context auto dg_keyval = m_context_datagrams.find(db_context); if(dg_keyval == m_context_datagrams.end()) { break; // Not meant for me, handled by LoadingObject } // Get the datagram from the db_context Datagram dg(dg_keyval->second); // Cleanup the context m_context_datagrams.erase(db_context); // Check to make sure the datagram is appropriate DatagramIterator check_dgi = DatagramIterator(dg); uint16_t resp_type = check_dgi.get_msg_type(); if(resp_type != STATESERVER_OBJECT_GET_FIELDS_RESP) { if(resp_type == STATESERVER_OBJECT_GET_FIELD_RESP) { m_log->warning() << "Received GetFieldResp, but expecting GetFieldsResp." << std::endl; } else if(resp_type == STATESERVER_OBJECT_GET_ALL_RESP) { m_log->warning() << "Received GetAllResp, but expecting GetFieldsResp." << std::endl; } break; } m_log->trace() << "Received GetFieldResp from database." << std::endl; // Add database field payload to response (don't know dclass, so must copy payload). if(dgi.read_uint8() == true) { dgi.read_uint16(); // Discard field count dg.add_data(dgi.read_remainder()); } send(dg); break; } case STATESERVER_OBJECT_GET_ALL: { uint32_t r_context = dgi.read_uint32(); uint32_t r_do_id = dgi.read_uint32(); // If object is active or loading, the Object or Loader will handle it if(m_objs.find(r_do_id) != m_objs.end() || m_loading.find(r_do_id) != m_loading.end()) { break; } m_log->trace() << "Received GetAll for inactive object with id " << r_do_id << std::endl; // Get context for db query, and remember caller with it uint32_t db_context = m_next_context++; if(m_context_datagrams.find(db_context) == m_context_datagrams.end()) { m_context_datagrams[db_context].add_server_header(sender, r_do_id, STATESERVER_OBJECT_GET_ALL_RESP); } m_context_datagrams[db_context].add_uint32(r_context); m_context_datagrams[db_context].add_uint32(r_do_id); m_context_datagrams[db_context].add_uint64(INVALID_CHANNEL); // Location // Cache the do_id --> context in case we get a dbss_activate m_inactive_loads[r_do_id].insert(r_context); // Send query to database Datagram dg(m_db_channel, r_do_id, DBSERVER_OBJECT_GET_ALL); dg.add_uint32(db_context); dg.add_uint32(r_do_id); send(dg); break; } case DBSERVER_OBJECT_GET_ALL_RESP: { uint32_t db_context = dgi.read_uint32(); // Check context auto dg_keyval = m_context_datagrams.find(db_context); if(dg_keyval == m_context_datagrams.end()) { break; // Not meant for me, handled by LoadingObject } // Get the datagram from the db_context Datagram dg(dg_keyval->second); // Cleanup the context m_context_datagrams.erase(db_context); // Check to make sure the datagram is appropriate DatagramIterator check_dgi = DatagramIterator(dg); uint16_t resp_type = check_dgi.get_msg_type(); if(resp_type != STATESERVER_OBJECT_GET_ALL_RESP) { if(resp_type == STATESERVER_OBJECT_GET_FIELD_RESP) { m_log->warning() << "Received GetFieldResp, but expecting GetAllResp." << std::endl; } else if(resp_type == STATESERVER_OBJECT_GET_FIELDS_RESP) { m_log->warning() << "Received GetFieldsResp, but expecting GetAllResp." << std::endl; } break; } // Get do_id from datagram check_dgi.seek_payload(); check_dgi.skip(8 + 4); // skip over sender and context to do_id; uint32_t do_id = check_dgi.read_uint32(); // Remove cached loading operation if(m_inactive_loads[do_id].size() > 1) { m_inactive_loads[do_id].erase(db_context); } else { m_inactive_loads.erase(do_id); } m_log->trace() << "Received GetAllResp from database." << std::endl; // If object not found, just cleanup the context map if(dgi.read_uint8() != true) { break; // Object not found } // Read object class uint16_t dc_id = dgi.read_uint16(); if(!dc_id) { m_log->error() << "Received object from database with unknown dclass" << " - id:" << dc_id << std::endl; break; } DCClass* r_dclass = g_dcf->get_class(dc_id); // Get fields from database std::unordered_map<DCField*, std::vector<uint8_t> > required_fields; std::map<DCField*, std::vector<uint8_t> > ram_fields; if(!unpack_db_fields(dgi, r_dclass, required_fields, ram_fields)) { m_log->error() << "Error while unpacking fields from database." << std::endl; break; } // Add class to response dg.add_uint16(r_dclass->get_number()); // Add required fields to datagram int dcc_field_count = r_dclass->get_num_inherited_fields(); for(int i = 0; i < dcc_field_count; ++i) { DCField *field = r_dclass->get_inherited_field(i); if(!field->as_molecular_field() && field->is_required()) { auto req_it = required_fields.find(field); if(req_it != required_fields.end()) { dg.add_data(req_it->second); } else { dg.add_data(field->get_default_value()); } } } // Add ram fields to datagram dg.add_uint16(ram_fields.size()); for(auto it = ram_fields.begin(); it != ram_fields.end(); ++it) { dg.add_uint16(it->first->get_number()); dg.add_data(it->second); } // Send response back to caller send(dg); break; } default: { if(msgtype < STATESERVER_MSGTYPE_MIN || msgtype > DBSERVER_MSGTYPE_MAX) { m_log->warning() << "Received unknown message of type " << msgtype << std::endl; } else { m_log->trace() << "Ignoring stateserver or database message" << " of type " << msgtype << std::endl; } } } }