// Main Method int main(int argc, char** argv) { // Set up a handler for any signals so that we always shutdown gracefully struct sigaction sigIntHandler; sigIntHandler.sa_handler = my_signal_handler; sigemptyset(&sigIntHandler.sa_mask); sigIntHandler.sa_flags = 0; sigaction(SIGINT, &sigIntHandler, NULL); // Setup the component factories cli_factory = new CommandLineInterpreterFactory; uuid_factory = new uuidComponentFactory; zmq_factory = new ZmqComponentFactory; logging_factory = new LoggingComponentFactory; mongo_factory = new MongoComponentFactory; ObjectListFactory ofactory; ObjectFactory objfactory; // Set up our command line interpreter cli = cli_factory->get_command_line_interface(argc, argv); // Allow for wait on startup, if configured if (cli->opt_exist("-wait")) { std::string wait_time_string = cli->get_opt("-wait"); int wait_time = std::stoi(wait_time_string, NULL); // Accept input on the command line in seconds, convert to microseconds usleep(wait_time * 1000000); } // Set up logging std::string initFileName; // See if we have a command line setting for the log file const char * env_logging_file = std::getenv("CRAZYIVAN_LOGGING_CONF"); if (env_logging_file) { std::string tempFileName(env_logging_file); initFileName = tempFileName; } else if (cli->opt_exist("-log-conf")) { initFileName = cli->get_opt("-log-conf"); } else { initFileName = "log4cpp.properties"; } // This reads the logging configuration file logging = logging_factory->get_logging_interface(initFileName); // Set up the logging submodules for each category start_logging_submodules(); // Set up the UUID Generator uid = uuid_factory->get_uuid_interface(); std::string service_id = "Clyman"; // Set up our configuration manager with the CLI config = new ConfigurationManager(cli, service_id); // Start up the Kafka Producer kafka = new KafkaClient(); // The configuration manager will look at any command line arguments, // configuration files, and Consul connections to try and determine the // correct configuration for the service // The configuration manager will look at any command line arguments, // configuration files, and Consul connections to try and determine the // correct configuration for the service bool config_success = false; bool config_tried = false; int config_attempts = 0; // If we fail configuration, we should sleep for 5 seconds and try again while (!config_success) { if (config_attempts > 50) { main_logging->error("Max Config Attempts failed, exiting"); shutdown(); exit(1); } if (config_tried) { main_logging->error("Configuration Failed, trying again in 5 seconds"); usleep(5000000); } else { config_tried = true; } try { config_success = config->configure(); } catch (std::exception& e) { main_logging->error("Exception encountered during Configuration"); } } // Set up the Mongo Connection std::string DBConnStr = config->get_mongoconnstr(); std::string DBName = config->get_dbname(); std::string DBHeaderCollection = config->get_dbheadercollection(); if (!(DBConnStr.empty() || DBName.empty() \ || DBHeaderCollection.empty())) { try { mongo = mongo_factory->get_mongo_interface(DBConnStr, \ DBName, DBHeaderCollection); main_logging->debug("Connected to Mongo"); } catch (std::exception& e) { main_logging->error("Exception encountered during Mongo Startup"); main_logging->error(e.what()); shutdown(); exit(1); } } else { main_logging->error("Insufficient Mongo Connection Information"); shutdown(); exit(1); } // Connect to the inbound ZMQ Admin std::string ib_zmq_connstr = config->get_ibconnstr(); if (!(ib_zmq_connstr.empty())) { zmqi = zmq_factory->get_zmq_inbound_interface(ib_zmq_connstr, REQ_RESP); main_logging->info("ZMQ Socket Open, opening request loop"); } else { main_logging->error("No IB ZMQ Connection String Supplied"); shutdown(); exit(1); } // Main Request Loop while (true) { std::string resp_str = ""; rapidjson::Document d; protoObj3::Obj3List new_proto; // Convert the OMQ message into a string to be passed on the event char * req_ptr = zmqi->crecv(); if (!req_ptr) continue; main_logging->debug("Conversion to C String performed with result: "); main_logging->debug(req_ptr); // Trim the string recieved std::string recvd_msg(req_ptr); std::string clean_string; std::string new_error_message = ""; // Parsing logic - JSON if (config->get_formattype() == JSON_FORMAT) { response_message = ofactory.build_json_object_list(); int final_closing_char = recvd_msg.find_last_of("}"); int first_opening_char = recvd_msg.find_first_of("{"); clean_string = \ recvd_msg.substr(first_opening_char, final_closing_char+1); main_logging->debug("Input String Cleaned"); main_logging->debug(clean_string); try { d.Parse<rapidjson::kParseStopWhenDoneFlag>(clean_string.c_str()); if (d.HasParseError()) { main_logging->error("Parsing Error: "); main_logging->error(GetParseError_En(d.GetParseError())); response_message->set_error_code(TRANSLATION_ERROR); new_error_message.assign(GetParseError_En(d.GetParseError())); } else {inbound_message = ofactory.build_object_list(d);} } // Catch a possible error and write to logs catch (std::exception& e) { main_logging->error("Exception occurred while parsing document:"); main_logging->error(e.what()); } // Parsing logic - Protocol Buffers } else if (config->get_formattype() == PROTO_FORMAT) { response_message = ofactory.build_proto_object_list(); clean_string = trim(recvd_msg); main_logging->debug("Input String Cleaned"); main_logging->debug(clean_string); try { new_proto.ParseFromString(clean_string); inbound_message = ofactory.build_object_list(new_proto); } catch (std::exception& e) { main_logging->error("Exception occurred while parsing bytes:"); main_logging->error(e.what()); response_message->set_error_code(TRANSLATION_ERROR); new_error_message.assign(e.what()); } } // Determine the Transaction ID UuidContainer id_container; id_container.id = ""; if (config->get_transactionidsactive() && inbound_message) { // Get an existing transaction ID std::string existing_trans_id = \ inbound_message->get_transaction_id(); // If no transaction ID is sent in, generate a new one if (existing_trans_id.empty()) { try { id_container = uid->generate(); if (!id_container.err.empty()) { main_logging->error(id_container.err); } inbound_message->set_transaction_id(id_container.id); } catch (std::exception& e) { main_logging->error("Exception encountered in UUID Generation"); main_logging->error(e.what()); shutdown(); exit(1); } } main_logging->debug("Transaction ID: "); main_logging->debug(inbound_message->get_transaction_id()); } bool shutdown_needed = false; // Core application logic if (inbound_message) { try { // Object Creation if (inbound_message->get_msg_type() == OBJ_CRT) { main_logging->info("Processing Object Creation Message"); for (int i = 0; i < inbound_message->num_objects(); i++) { // Create a new Obj3 to add to the return list ObjectInterface *resp_element = objfactory.build_object(); // Create the Obj3 document for Mongo AOSSL::MongoBufferInterface *bson = mongo_factory->get_mongo_buffer(); inbound_message->get_object(i)->to_bson(bson); MongoResponseInterface *resp = mongo->create_document(bson); delete bson; // Add the key into the new obj3 resp_element->set_key(resp->get_value()); // Add the new obj3 to the response list response_message->add_object(resp_element); delete resp; } // Object Update } else if (inbound_message->get_msg_type() == OBJ_UPD || \ inbound_message->get_msg_type() == OBJ_LOCK || \ inbound_message->get_msg_type() == OBJ_UNLOCK || inbound_message->get_msg_type() == OBJ_OVERWRITE) { main_logging->info("Processing Object Update Message"); for (int i = 0; i < inbound_message->num_objects(); i++) { // Build a query bson to pass to the update_by_query AOSSL::MongoBufferInterface *query_bson = mongo_factory->get_mongo_buffer(); AOSSL::MongoBufferInterface *bson = mongo_factory->get_mongo_buffer(); std::string msg_key = inbound_message->get_object(i)->get_key(); bool execute_query = true; if (msg_key.empty() && \ !(inbound_message->get_object(i)->get_name().empty() && \ inbound_message->get_object(i)->get_scene().empty())) { std::string name_key = "name"; std::string scene_key = "scene"; query_bson->add_string(name_key, inbound_message->get_object(i)->get_name()); query_bson->add_string(scene_key, inbound_message->get_object(i)->get_scene()); } else if (!(msg_key.empty())) { query_bson->add_oid(msg_key); } // Add an owner query if object locking is active if (config->get_locking_active()) { std::string owner_key = "owner"; // If we have a lock message, then look for an empty owner if (inbound_message->get_msg_type() == OBJ_LOCK) { std::string owner_search = ""; query_bson->add_string(owner_key, owner_search); // Otherwise, look for a matching owner to the input } else { std::string owner_search = inbound_message->get_object(i)->get_owner(); query_bson->add_string(owner_key, owner_search); } } if (inbound_message->get_msg_type() == OBJ_OVERWRITE) { // Build the BSON Update main_logging->debug("Overwriting BSON Object"); inbound_message->get_object(i)->to_bson_update(bson); } else { // Load the current doc from the database rapidjson::Document resp_doc; MongoResponseInterface *resp = mongo->load_document(msg_key); if (resp) { std::string mongo_resp_str = resp->get_value(); main_logging->debug("Document loaded from Mongo"); main_logging->debug(mongo_resp_str); resp_doc.Parse(mongo_resp_str.c_str()); ObjectInterface *resp_obj = objfactory.build_object(resp_doc); // Save the resulting object if (inbound_message->get_op_type() == APPEND) { // Apply the object message as changes to the DB Object resp_obj->merge(inbound_message->get_object(i)); // Update the object in case of unlock to have no owner before saving if (inbound_message->get_msg_type() == OBJ_UNLOCK) { std::string new_owner = ""; resp_obj->set_owner(new_owner); } resp_obj->to_bson_update(bson); } else { // Update the object in case of unlock to have no owner before saving if (inbound_message->get_msg_type() == OBJ_UNLOCK) { std::string new_owner = ""; inbound_message->get_object(0)->set_owner(new_owner); } inbound_message->get_object(0)->to_bson_update(true, false, bson); } response_message->add_object(resp_obj); delete resp; } else { main_logging->error("Document not found in Mongo"); response_message->set_error_code(NOT_FOUND); new_error_message = "Object not Found"; response_message->set_error_message(new_error_message); execute_query = false; } } // Actually execute the Mongo update AOSSL::MongoBufferInterface *response_buffer = NULL; bool send_update = false; if (execute_query) { response_buffer = mongo->update_single_by_query(query_bson, bson); // Look at the response buffer to ensure that a single document // was matched and updated std::string mdc_key = "modifiedCount"; std::string mtc_key = "matchedCount"; int num_modified = response_buffer->get_int(mdc_key); int num_matched = response_buffer->get_int(mtc_key); if (num_matched == 0) { main_logging->error("Document not found in Mongo"); response_message->set_error_code(NOT_FOUND); new_error_message = "Object not Found"; response_message->set_error_message(new_error_message); } else if (num_modified == 0) { main_logging->error("Document found in Mongo, but update failed"); response_message->set_error_code(LOCK_EXISTS_ERROR); new_error_message = "Object locked by another device"; response_message->set_error_message(new_error_message); } else { // Updates were successful in Mongo, so send via streams send_update = true; } } // Send an update on the Kafka 'dvs' topic if ((inbound_message->get_msg_type() == OBJ_OVERWRITE || \ inbound_message->get_msg_type() == OBJ_UPD) && send_update) { kafka->send(inbound_message->get_object(i)->to_transform_json(), config->get_kafkabroker()); } delete query_bson; delete bson; } // Object Retrieve } else if (inbound_message->get_msg_type() == OBJ_GET) { main_logging->info("Processing Object Get Message"); for (int i = 0; i < inbound_message->num_objects(); i++) { // Pull down and parse the value from Mongo rapidjson::Document resp_doc; MongoResponseInterface *resp = mongo->load_document(\ inbound_message->get_object(i)->get_key()); if (resp) { std::string mongo_resp_str = resp->get_value(); main_logging->debug("Document loaded from Mongo"); main_logging->debug(mongo_resp_str); resp_doc.Parse(mongo_resp_str.c_str()); // Create a new Obj3 and add to the return list ObjectInterface *resp_obj = objfactory.build_object(resp_doc); response_message->add_object(resp_obj); delete resp; } else { main_logging->error("Document not found in Mongo"); response_message->set_error_code(NOT_FOUND); new_error_message = "Object not Found"; response_message->set_error_message(new_error_message); } } // Object Query } else if (inbound_message->get_msg_type() == OBJ_QUERY) { main_logging->info("Processing Object Batch Query Message"); batch_query(inbound_message, response_message, mongo); // Object Delete } else if (inbound_message->get_msg_type() == OBJ_DEL) { main_logging->info("Processing Object Deletion Message"); for (int i = 0; i < inbound_message->num_objects(); i++) { ObjectInterface *resp_element = objfactory.build_object(); resp_element->set_key(\ inbound_message->get_object(i)->get_key()); mongo->delete_document(\ inbound_message->get_object(i)->get_key()); response_message->add_object(resp_element); } // Ping } else if (inbound_message->get_msg_type() == PING) { main_logging->info("Ping Message Recieved"); // Kill } else if (inbound_message->get_msg_type() == KILL) { main_logging->info("Shutting Down"); shutdown_needed = true; // Invalid Message Type } else { response_message->set_error_code(BAD_MSG_TYPE_ERROR); new_error_message = "Unknown Message Type"; response_message->set_error_message(new_error_message); } // Exception during processing } catch (std::exception& e) { main_logging->error("Exception encountered during Processing"); main_logging->error(e.what()); response_message->set_error_code(PROCESSING_ERROR); new_error_message.assign(e.what()); response_message->set_error_message(new_error_message); } response_message->set_msg_type(inbound_message->get_msg_type()); } // Convert the response object to a message main_logging->debug("Building Response"); std::string application_response; response_message->to_msg_string(application_response); // Send the response via ZMQ main_logging->info("Sending Response"); main_logging->info(application_response); zmqi->send(application_response); // Cleanup if (response_message) { delete response_message; response_message = NULL; } if (inbound_message) { delete inbound_message; inbound_message = NULL; } // If we recieved a shutdown message, then we cleanup and exit if (shutdown_needed) {shutdown(); exit(1);} } return 0; }