/* **************************************************************************** * * mongoGetContextElementResponses_fail - */ TEST(mongoOntimeintervalOperations, mongoGetContextElementResponses_fail) { HttpStatusCode ms; /* Forge the parameters */ EntityIdVector enV; EntityId en("E5", "T", "false"); enV.push_back(&en); AttributeList attrL; attrL.push_back("A1"); attrL.push_back("A2"); attrL.push_back("A3"); attrL.push_back("A4"); ContextElementResponseVector cerV; std::string err; /* Prepare database */ prepareDatabase(); /* Do operation */ ms = mongoGetContextElementResponses(enV, attrL, &cerV, &err); /* Check results */ EXPECT_EQ(SccOk, ms); ASSERT_EQ(0, cerV.size()); }
/* **************************************************************************** * * processGenericEntities - * * If the request included some "generic" entity, some additional CPr could be needed in the CER array. There are * three cases of "generic" entities: 1) not pattern + null type, 2) pattern + not null type, 3) pattern + null type * * The limitReached parameter is to prevent the addition of new entities, which is needed in the case of the pagination * limit has been reached with local entities. * */ void processGenericEntities(EntityIdVector& enV, ContextElementResponseVector& cerV, ContextRegistrationResponseVector& crrV, bool limitReached) { for (unsigned int ix = 0; ix < enV.size(); ++ix) { EntityId* enP = enV.get(ix); if (enP->type == "" || isTrue(enP->isPattern)) { addContextProviders(cerV, crrV, limitReached, enP); } } }
/* **************************************************************************** * * equalEntityIdVector - * */ static bool equalEntityIdVector(EntityIdVector enExpectedV, EntityIdVector enArgV) { /* Check vector size */ if (enExpectedV.size() != enArgV.size()) { LM_M(("different sizes: expected %d, actual %d", enExpectedV.size(), enArgV.size())); return false; } /* Check that every entity in 'enArgV' is in 'enExpectedV'. Order doesn't matter */ for (unsigned int ix = 0; ix < enArgV.size(); ++ix) { bool entityMatch = false; for (unsigned int jx = 0; jx < enExpectedV.size(); ++jx) { EntityId enArg = *enArgV.get(ix); EntityId enExpected = *enExpectedV.get(jx); LM_M(("%d == %d?", ix, jx)); if (equalEntity(enExpected, enArg)) { LM_M(("entity matches in EntityIdVector comparison, check next one...")); entityMatch = true; break; /* loop in jx */ } } if (!entityMatch) { LM_M(("after looking everyone, entity doesn't match in EntityIdVector")); return false; } } LM_M(("EntityIdVector comparison ok")); return true; }
/* **************************************************************************** * * mongoGetContextElementResponses_dbfail - * * FIXME: not sure if this test should exist... it should be include in unit testing * for entitiesQuery() */ TEST(mongoOntimeintervalOperations, mongoGetContextElementResponses_dbfail) { HttpStatusCode ms; /* Prepare mock */ const DBException e = DBException("boom!!", 33); DBClientConnectionMock* connectionMock = new DBClientConnectionMock(); ON_CALL(*connectionMock,_query("utest.entities",_,_,_,_,_,_)) .WillByDefault(Throw(e)); /* Forge the parameters */ EntityIdVector enV; EntityId en1("E1", "T", "false"); EntityId en2("E2", "T", "false"); enV.push_back(&en1); enV.push_back(&en2); AttributeList attrL; attrL.push_back("A1"); attrL.push_back("A2"); attrL.push_back("A3"); attrL.push_back("A4"); ContextElementResponseVector cerV; std::string err; /* Set MongoDB connection (prepare database first with the "actual" connection object) */ prepareDatabase(); DBClientBase* connectionDb = getMongoConnection(); setMongoConnectionForUnitTest(connectionMock); /* Do operation */ ms = mongoGetContextElementResponses(enV, attrL, &cerV, &err); /* Check results */ EXPECT_EQ(SccOk, ms); EXPECT_EQ("Database Error (collection: utest.entities - " "query(): { query: { $or: [ { _id.id: \"E1\", _id.type: \"T\" }, { _id.id: \"E2\", _id.type: \"T\" } ], _id.servicePath: { $in: [ /^/.*/, null ] }, " "attrNames: { $in: [ \"A1\", \"A2\", \"A3\", \"A4\" ] } }, orderby: { creDate: 1 } } - " "exception: boom!!)", err); /* Restore real DB connection */ setMongoConnectionForUnitTest(connectionDb); /* Release mocks */ delete connectionMock; }
Entity declare_element(BulkData & mesh, PartVector & parts, const EntityId elem_id, const EntityIdVector & node_ids) { MetaData & fem_meta = MetaData::get(mesh); stk::topology top = fem_meta.get_topology(*parts[0]); ThrowAssert(node_ids.size() >= top.num_nodes()); ThrowErrorMsgIf(top == stk::topology::INVALID_TOPOLOGY, "Part " << parts[0]->name() << " does not have a local topology"); PartVector empty; const EntityRank entity_rank = stk::topology::ELEMENT_RANK; Entity elem = mesh.declare_entity(entity_rank, elem_id, parts); const EntityRank node_rank = stk::topology::NODE_RANK; Permutation perm = stk::mesh::Permutation::INVALID_PERMUTATION; OrdinalVector ordinal_scratch; ordinal_scratch.reserve(64); PartVector part_scratch; part_scratch.reserve(64); for(unsigned i = 0; i < top.num_nodes(); ++i) { //declare node if it doesn't already exist Entity node = mesh.get_entity(node_rank, node_ids[i]); if(!mesh.is_valid(node)) { node = mesh.declare_entity(node_rank, node_ids[i], empty); } mesh.declare_relation(elem, node, i, perm, ordinal_scratch, part_scratch); } return elem; }
/* **************************************************************************** * * mongoGetContextElementResponses_pattern - */ TEST(mongoOntimeintervalOperations, mongoGetContextElementResponses_pattern) { HttpStatusCode ms; /* Forge the parameters */ EntityIdVector enV; EntityId en("E[1-2]", "T", "true"); enV.push_back(&en); AttributeList attrL; attrL.push_back("A1"); attrL.push_back("A2"); attrL.push_back("A3"); attrL.push_back("A4"); ContextElementResponseVector cerV; std::string err; /* Prepare database */ prepareDatabase(); /* Do operation */ ms = mongoGetContextElementResponses(enV, attrL, &cerV, &err); /* Check results */ EXPECT_EQ(SccOk, ms); ASSERT_EQ(2, cerV.size()); ContextElementResponse cer0 = *cerV[0]; ContextElementResponse cer1 = *cerV[1]; ContextAttribute ca0, ca1, ca2, ca3; /* Context Element Response #1 */ EXPECT_EQ(SccOk, cer0.statusCode.code); EXPECT_EQ("OK", cer0.statusCode.reasonPhrase); EXPECT_EQ(0, cer0.statusCode.details.length()); EXPECT_EQ("E1", cer0.contextElement.entityId.id); EXPECT_EQ("T", cer0.contextElement.entityId.type); EXPECT_EQ("false", cer0.contextElement.entityId.isPattern); ASSERT_EQ(3, cer0.contextElement.contextAttributeVector.size()); ca0 = *cer0.contextElement.contextAttributeVector[0]; ca1 = *cer0.contextElement.contextAttributeVector[1]; ca2 = *cer0.contextElement.contextAttributeVector[2]; EXPECT_EQ("A1", ca0.name); EXPECT_EQ("TA1", ca0.type); EXPECT_EQ("X", ca0.stringValue); EXPECT_EQ("A2", ca1.name); EXPECT_EQ("TA2", ca1.type); EXPECT_EQ("Z", ca1.stringValue); EXPECT_EQ("A3", ca2.name); EXPECT_EQ("TA3", ca2.type); EXPECT_EQ("W", ca2.stringValue); /* Context Element Response #2 */ EXPECT_EQ(SccOk, cer1.statusCode.code); EXPECT_EQ("OK", cer1.statusCode.reasonPhrase); EXPECT_EQ(0, cer1.statusCode.details.length()); EXPECT_EQ("E2", cer1.contextElement.entityId.id); EXPECT_EQ("T", cer1.contextElement.entityId.type); EXPECT_EQ("false", cer1.contextElement.entityId.isPattern); ASSERT_EQ(2, cer1.contextElement.contextAttributeVector.size()); ca0 = *cer1.contextElement.contextAttributeVector[0]; ca1 = *cer1.contextElement.contextAttributeVector[1]; EXPECT_EQ("A1", ca0.name); EXPECT_EQ("TA1", ca0.type); EXPECT_EQ("S", ca0.stringValue); EXPECT_EQ("A4", ca1.name); EXPECT_EQ("TA4", ca1.type); EXPECT_EQ("T", ca1.stringValue); EXPECT_EQ(0, err.length()); }
/* **************************************************************************** * * mongoUpdateContextSubscription - */ HttpStatusCode mongoUpdateContextSubscription(UpdateContextSubscriptionRequest* requestP, UpdateContextSubscriptionResponse* responseP, Format inFormat, const std::string& tenant) { reqSemTake(__FUNCTION__, "ngsi10 update subscription request"); LM_T(LmtMongo, ("Update Context Subscription")); DBClientBase* connection = getMongoConnection(); /* Look for document */ BSONObj sub; try { OID id = OID(requestP->subscriptionId.get()); mongoSemTake(__FUNCTION__, "findOne in SubscribeContextCollection"); sub = connection->findOne(getSubscribeContextCollectionName(tenant).c_str(), BSON("_id" << id)); mongoSemGive(__FUNCTION__, "findOne in SubscribeContextCollection"); LM_I(("Database Operation Successful (findOne _id: %s)", id.toString().c_str())); } catch (const AssertionException &e) { /* This happens when OID format is wrong */ // FIXME P4: this checking should be done at the parsing stage, without progressing to // mongoBackend. For the moment we can leave this here, but we should remove it in the future // (old issue #95) mongoSemGive(__FUNCTION__, "findOne in SubscribeContextCollection (mongo assertion exception)"); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (mongo assertion exception)"); responseP->subscribeError.errorCode.fill(SccContextElementNotFound); LM_W(("Bad Input (invalid OID format)")); return SccOk; } catch (const DBException &e) { mongoSemGive(__FUNCTION__, "findOne in SubscribeContextCollection (mongo db exception)"); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (mongo db exception)"); responseP->subscribeError.errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() + " - findOne() _id: " + requestP->subscriptionId.get() + " - exception: " + e.what()); LM_E(("Database Error (%s)", responseP->subscribeError.errorCode.details.c_str())); return SccOk; } catch (...) { mongoSemGive(__FUNCTION__, "findOne in SubscribeContextCollection (mongo generic exception)"); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (mongo generic exception)"); responseP->subscribeError.errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() + " - findOne() _id: " + requestP->subscriptionId.get() + " - exception: " + "generic"); LM_E(("Database Error (%s)", responseP->subscribeError.errorCode.details.c_str())); return SccOk; } if (sub.isEmpty()) { responseP->subscribeError.errorCode.fill(SccContextElementNotFound); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (no subscriptions found)"); return SccOk; } /* We start with an empty BSONObjBuilder and process requestP for all the fields that can * be updated. I don't like too much this strategy (I would have preferred to start with * a copy of the original document, then modify as neded, but this doesn't seem to be easy * using the API provide by the Mongo C++ driver) * * FIXME: a better implementation strategy could be doing an findAndModify() query to do the * update, so detecting if the document was not found, instead of using findOne() + update() * with $set operation. One operations to MongoDb. vs two operations. */ BSONObjBuilder newSub; /* Entities, attribute list and reference are not updatable, so they are appended directly */ newSub.appendArray(CSUB_ENTITIES, sub.getField(CSUB_ENTITIES).Obj()); newSub.appendArray(CSUB_ATTRS, sub.getField(CSUB_ATTRS).Obj()); newSub.append(CSUB_REFERENCE, STR_FIELD(sub, CSUB_REFERENCE)); /* Duration update */ if (requestP->duration.isEmpty()) { newSub.append(CSUB_EXPIRATION, sub.getField(CSUB_EXPIRATION).numberLong()); } else { long long expiration = getCurrentTime() + requestP->duration.parse(); newSub.append(CSUB_EXPIRATION, expiration); LM_T(LmtMongo, ("New subscription expiration: %l", expiration)); } /* Restriction update */ // FIXME: Restrictions not implemented yet /* Throttling update */ if (!requestP->throttling.isEmpty()) { /* Throttling equal to 0 removes throttling */ long long throttling = requestP->throttling.parse(); if (throttling != 0) { newSub.append(CSUB_THROTTLING, throttling); } } else { /* The hasField check is needed due to Throttling could not be present in the original doc */ if (sub.hasField(CSUB_THROTTLING)) { newSub.append(CSUB_THROTTLING, sub.getField(CSUB_THROTTLING).numberLong()); } } /* Notify conditions */ bool notificationDone = false; if (requestP->notifyConditionVector.size() == 0) { newSub.appendArray(CSUB_CONDITIONS, sub.getField(CSUB_CONDITIONS).embeddedObject()); } else { /* Destroy any previous ONTIMEINTERVAL thread */ getNotifier()->destroyOntimeIntervalThreads(requestP->subscriptionId.get()); /* Build conditions array (including side-effect notifications and threads creation) * In order to do so, we have to create and EntityIdVector and AttributeList from sub * document, given the processConditionVector() signature */ EntityIdVector enV = subToEntityIdVector(sub); AttributeList attrL = subToAttributeList(sub); BSONArray conds = processConditionVector(&requestP->notifyConditionVector, enV, attrL, requestP->subscriptionId.get(), C_STR_FIELD(sub, CSUB_REFERENCE), ¬ificationDone, inFormat, tenant); newSub.appendArray(CSUB_CONDITIONS, conds); /* Remove EntityIdVector and AttributeList dynamic memory */ enV.release(); attrL.release(); } int count = sub.hasField(CSUB_COUNT) ? sub.getIntField(CSUB_COUNT) : 0; /* Last notification */ if (notificationDone) { newSub.append(CSUB_LASTNOTIFICATION, getCurrentTime()); newSub.append(CSUB_COUNT, count + 1); } else { /* The hasField check is needed due to lastNotification/count could not be present in the original doc */ if (sub.hasField(CSUB_LASTNOTIFICATION)) { newSub.append(CSUB_LASTNOTIFICATION, sub.getIntField(CSUB_LASTNOTIFICATION)); } if (sub.hasField(CSUB_COUNT)) { newSub.append(CSUB_COUNT, count); } } /* Adding format to use in notifications */ newSub.append(CSUB_FORMAT, std::string(formatToString(inFormat))); /* Update document in MongoDB */ BSONObj update = newSub.obj(); try { LM_T(LmtMongo, ("update() in '%s' collection _id '%s': %s}", getSubscribeContextCollectionName(tenant).c_str(), requestP->subscriptionId.get().c_str(), update.toString().c_str())); mongoSemTake(__FUNCTION__, "update in SubscribeContextCollection"); connection->update(getSubscribeContextCollectionName(tenant).c_str(), BSON("_id" << OID(requestP->subscriptionId.get())), update); mongoSemGive(__FUNCTION__, "update in SubscribeContextCollection"); LM_I(("Database Operation Successful (update _id: %s, %s)", requestP->subscriptionId.get().c_str(), update.toString().c_str())); } catch (const DBException &e) { mongoSemGive(__FUNCTION__, "update in SubscribeContextCollection (mongo db exception)"); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (mongo db exception)"); responseP->subscribeError.errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() + " - update() _id: " + requestP->subscriptionId.get().c_str() + " - update() doc: " + update.toString() + " - exception: " + e.what()); LM_E(("Database Error (%s)", responseP->subscribeError.errorCode.details.c_str())); return SccOk; } catch (...) { mongoSemGive(__FUNCTION__, "update in SubscribeContextCollection (mongo generic exception)"); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (mongo generic exception)"); responseP->subscribeError.errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() + " - update() _id: " + requestP->subscriptionId.get().c_str() + " - update() doc: " + update.toString() + " - exception: " + "generic"); LM_E(("Database Error (%s)", responseP->subscribeError.errorCode.details.c_str())); return SccOk; } /* Duration and throttling are optional parameters, they are only added in the case they * was used for update */ if (!requestP->duration.isEmpty()) { responseP->subscribeResponse.duration = requestP->duration; } if (!requestP->throttling.isEmpty()) { responseP->subscribeResponse.throttling = requestP->throttling; } responseP->subscribeResponse.subscriptionId = requestP->subscriptionId; reqSemGive(__FUNCTION__, "ngsi10 update subscription request"); return SccOk; }
/* **************************************************************************** * * processRegisterContext - * * This function has a slightly different behaviour depending on whether the id * parameter is null (new registration case) or not null (update case), in * particular: * * - In the new registration case, the _id is generated and insert() is used to * put the document in the DB. * - In the update case, the _id is set according to the argument 'id' and update() is * used to put the document in the DB. * */ HttpStatusCode processRegisterContext ( RegisterContextRequest* requestP, RegisterContextResponse* responseP, OID* id, const std::string& tenant, const std::string& servicePath, const std::string& format, const std::string& fiwareCorrelator ) { std::string err; /* If expiration is not present, then use a default one */ if (requestP->duration.isEmpty()) { requestP->duration.set(DEFAULT_DURATION); } /* Calculate expiration (using the current time and the duration field in the request) */ long long expiration = getCurrentTime() + requestP->duration.parse(); LM_T(LmtMongo, ("Registration expiration: %lu", expiration)); /* Create the mongoDB registration document */ BSONObjBuilder reg; OID oid; if (id == NULL) { oid.init(); } else { oid = *id; } reg.append("_id", oid); reg.append(REG_EXPIRATION, expiration); reg.append(REG_SERVICE_PATH, servicePath == "" ? DEFAULT_SERVICE_PATH_UPDATES : servicePath); reg.append(REG_FORMAT, format); // // We accumulate the subscriptions in a map. The key of the map is the string representing // subscription id // std::map<string, TriggeredSubscription*> subsToNotify; // This vector is used to define which entities to include in notifications EntityIdVector triggerEntitiesV; BSONArrayBuilder contextRegistration; for (unsigned int ix = 0; ix < requestP->contextRegistrationVector.size(); ++ix) { ContextRegistration* cr = requestP->contextRegistrationVector[ix]; BSONArrayBuilder entities; for (unsigned int jx = 0; jx < cr->entityIdVector.size(); ++jx) { EntityId* en = cr->entityIdVector[jx]; triggerEntitiesV.push_back(en); if (en->type == "") { entities.append(BSON(REG_ENTITY_ID << en->id)); LM_T(LmtMongo, ("Entity registration: {id: %s}", en->id.c_str())); } else { entities.append(BSON(REG_ENTITY_ID << en->id << REG_ENTITY_TYPE << en->type)); LM_T(LmtMongo, ("Entity registration: {id: %s, type: %s}", en->id.c_str(), en->type.c_str())); } } BSONArrayBuilder attrs; for (unsigned int jx = 0; jx < cr->contextRegistrationAttributeVector.size(); ++jx) { ContextRegistrationAttribute* cra = cr->contextRegistrationAttributeVector[jx]; attrs.append(BSON(REG_ATTRS_NAME << cra->name << REG_ATTRS_TYPE << cra->type << "isDomain" << cra->isDomain)); LM_T(LmtMongo, ("Attribute registration: {name: %s, type: %s, isDomain: %s}", cra->name.c_str(), cra->type.c_str(), cra->isDomain.c_str())); for (unsigned int kx = 0; kx < requestP->contextRegistrationVector[ix]->contextRegistrationAttributeVector[jx]->metadataVector.size(); ++kx) { // FIXME: metadata not supported at the moment } } contextRegistration.append( BSON( REG_ENTITIES << entities.arr() << REG_ATTRS << attrs.arr() << REG_PROVIDING_APPLICATION << requestP->contextRegistrationVector[ix]->providingApplication.get())); LM_T(LmtMongo, ("providingApplication registration: %s", requestP->contextRegistrationVector[ix]->providingApplication.c_str())); std::string err; if (!addTriggeredSubscriptions(*cr, subsToNotify, err, tenant)) { responseP->errorCode.fill(SccReceiverInternalError, err); return SccOk; } } reg.append(REG_CONTEXT_REGISTRATION, contextRegistration.arr()); /* Note we are using upsert = "true". This means that if the document doesn't previously * exist in the collection, it is created. Thus, this way both uses of registerContext are OK * (either new registration or updating an existing one) */ if (!collectionUpdate(getRegistrationsCollectionName(tenant), BSON("_id" << oid), reg.obj(), true, &err)) { responseP->errorCode.fill(SccReceiverInternalError, err); releaseTriggeredSubscriptions(subsToNotify); return SccOk; } // // Send notifications for each one of the subscriptions accumulated by // previous addTriggeredSubscriptions() invocations // processSubscriptions(triggerEntitiesV, subsToNotify, err, tenant, fiwareCorrelator); // Fill the response element responseP->duration = requestP->duration; responseP->registrationId.set(oid.toString()); responseP->errorCode.fill(SccOk); return SccOk; }
/* **************************************************************************** * * associationsDiscoverContextAvailability - */ static HttpStatusCode associationsDiscoverContextAvailability ( DiscoverContextAvailabilityRequest* requestP, DiscoverContextAvailabilityResponse* responseP, const std::string& scope, const std::string& tenant, int offset, int limit, bool details, const std::vector<std::string>& servicePathV ) { if (scope == SCOPE_VALUE_ASSOC_ALL) { LM_W(("Bad Input (%s scope not supported)", SCOPE_VALUE_ASSOC_ALL)); responseP->errorCode.fill(SccNotImplemented, std::string("Not supported scope: '") + SCOPE_VALUE_ASSOC_ALL + "'"); return SccOk; } MetadataVector mdV; std::string err; if (!associationsQuery(&requestP->entityIdVector, &requestP->attributeList, scope, &mdV, &err, tenant, offset, limit, details, servicePathV)) { mdV.release(); responseP->errorCode.fill(SccReceiverInternalError, std::string("Database error: ") + err); return SccOk; } LM_T(LmtPagination, ("Offset: %d, Limit: %d, Details: %s", offset, limit, (details == true)? "true" : "false")); /* Query for associated entities */ for (unsigned int ix = 0; ix < mdV.size(); ++ix) { /* Each association involves a registrationsQuery() operation, accumulating the answer in * responseP->responseVector */ Metadata* md = mdV.get(ix); EntityIdVector enV; AttributeList attrL; EntityId en; if (scope == SCOPE_VALUE_ASSOC_SOURCE) { en = EntityId(md->association.entityAssociation.source.id, md->association.entityAssociation.source.type); } else { // SCOPE_VALUE_ASSOC_TARGET en = EntityId(md->association.entityAssociation.target.id, md->association.entityAssociation.target.type); } enV.push_back(&en); for (unsigned int jx = 0; jx < md->association.attributeAssociationList.size(); ++jx) { if (scope == SCOPE_VALUE_ASSOC_SOURCE) { attrL.push_back(md->association.attributeAssociationList.get(jx)->source); } else { attrL.push_back(md->association.attributeAssociationList.get(jx)->target); } } ContextRegistrationResponseVector crrV; if (!registrationsQuery(enV, attrL, &crrV, &err, tenant, servicePathV)) { responseP->errorCode.fill(SccReceiverInternalError, err); mdV.release(); return SccOk; } /* Accumulate in responseP */ for (unsigned int jx = 0; jx < crrV.size(); ++jx) { responseP->responseVector.push_back(crrV.get(jx)); } } if (responseP->responseVector.size() == 0) { mdV.release(); responseP->errorCode.fill(SccContextElementNotFound, "Could not query association with combination of entity/attribute"); LM_RE(SccOk, (responseP->errorCode.details.c_str())); } /* Set association metadata as final ContextRegistrationResponse */ ContextRegistrationResponse* crrMd = new ContextRegistrationResponse(); crrMd->contextRegistration.providingApplication.set("http://www.fi-ware.eu/NGSI/association"); crrMd->contextRegistration.registrationMetadataVector = mdV; responseP->responseVector.push_back(crrMd); return SccOk; }
/* **************************************************************************** * * processRegisterContext - * * This function has a slightly different behaviour depending on whether the id * parameter is null (new registration case) or not null (update case), in * particular: * * - In the new registration case, the _id is generated and insert() is used to * put the document in the DB. * - In the update case, the _id is set according to the argument 'id' and update() is * used to put the document in the DB. * */ HttpStatusCode processRegisterContext ( RegisterContextRequest* requestP, RegisterContextResponse* responseP, OID* id, const std::string& tenant, const std::string& servicePath, const std::string& format ) { DBClientBase* connection = NULL; /* If expiration is not present, then use a default one */ if (requestP->duration.isEmpty()) { requestP->duration.set(DEFAULT_DURATION); } /* Calculate expiration (using the current time and the duration field in the request) */ long long expiration = getCurrentTime() + requestP->duration.parse(); LM_T(LmtMongo, ("Registration expiration: %lu", expiration)); /* Create the mongoDB registration document */ BSONObjBuilder reg; OID oid; if (id == NULL) { oid.init(); } else { oid = *id; } reg.append("_id", oid); reg.append(REG_EXPIRATION, expiration); reg.append(REG_SERVICE_PATH, servicePath); reg.append(REG_FORMAT, format); /* We accumulate the subscriptions in a map. The key of the map is the string representing * subscription id */ std::map<string, TriggeredSubscription*> subsToNotify; /* This vector is used to define which entities to include in notifications */ EntityIdVector triggerEntitiesV; BSONArrayBuilder contextRegistration; for (unsigned int ix = 0; ix < requestP->contextRegistrationVector.size(); ++ix) { ContextRegistration* cr = requestP->contextRegistrationVector.get(ix); BSONArrayBuilder entities; for (unsigned int jx = 0; jx < cr->entityIdVector.size(); ++jx) { EntityId* en = cr->entityIdVector.get(jx); triggerEntitiesV.push_back(en); if (en->type == "") { entities.append(BSON(REG_ENTITY_ID << en->id)); LM_T(LmtMongo, ("Entity registration: {id: %s}", en->id.c_str())); } else { entities.append(BSON(REG_ENTITY_ID << en->id << REG_ENTITY_TYPE << en->type)); LM_T(LmtMongo, ("Entity registration: {id: %s, type: %s}", en->id.c_str(), en->type.c_str())); } } BSONArrayBuilder attrs; for (unsigned int jx = 0; jx < cr->contextRegistrationAttributeVector.size(); ++jx) { ContextRegistrationAttribute* cra = cr->contextRegistrationAttributeVector.get(jx); attrs.append(BSON(REG_ATTRS_NAME << cra->name << REG_ATTRS_TYPE << cra->type << "isDomain" << cra->isDomain)); LM_T(LmtMongo, ("Attribute registration: {name: %s, type: %s, isDomain: %s}", cra->name.c_str(), cra->type.c_str(), cra->isDomain.c_str())); for (unsigned int kx = 0; kx < requestP->contextRegistrationVector.get(ix)->contextRegistrationAttributeVector.get(jx)->metadataVector.size(); ++kx) { // FIXME: metadata not supported at the moment } } contextRegistration.append(BSON( REG_ENTITIES << entities.arr() << REG_ATTRS << attrs.arr() << REG_PROVIDING_APPLICATION << requestP->contextRegistrationVector.get(ix)->providingApplication.get()) ); LM_T(LmtMongo, ("providingApplication registration: %s", requestP->contextRegistrationVector.get(ix)->providingApplication.c_str())); std::string err; if (!processAssociations(cr->registrationMetadataVector, &err, tenant)) { responseP->errorCode.fill(SccReceiverInternalError); return SccOk; } if (!addTriggeredSubscriptions(*cr, subsToNotify, err, tenant)) { responseP->errorCode.fill(SccReceiverInternalError, err); return SccOk; } } reg.append(REG_CONTEXT_REGISTRATION, contextRegistration.arr()); BSONObj regDoc = reg.obj(); LM_T(LmtMongo, ("upsert update() in '%s' collection: '%s'", getRegistrationsCollectionName(tenant).c_str(), regDoc.toString().c_str())); try { connection = getMongoConnection(); /* Note the fourth parameter is set to "true". This means "upsert", so if the document doesn't previously * exist in the collection, it is created. Thus, this way is ok with both uses of * registerContext (either new registration or updating an existing one) */ connection->update(getRegistrationsCollectionName(tenant).c_str(), BSON("_id" << oid), regDoc, true); releaseMongoConnection(connection); LM_I(("Database Operation Successful (_id: %s)", oid.toString().c_str())); } catch (const DBException& e) { releaseMongoConnection(connection); responseP->errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getRegistrationsCollectionName(tenant).c_str() + " - upsert update(): " + regDoc.toString() + " - exception: " + e.what()); LM_E(("Database Error (%s)", responseP->errorCode.reasonPhrase.c_str())); releaseTriggeredSubscriptions(subsToNotify); return SccOk; } catch (...) { releaseMongoConnection(connection); responseP->errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getRegistrationsCollectionName(tenant).c_str() + " - upsert update(): " + regDoc.toString() + " - exception: " + "generic"); LM_E(("Database Error (%s)", responseP->errorCode.reasonPhrase.c_str())); releaseTriggeredSubscriptions(subsToNotify); return SccOk; } /* Send notifications for each one of the subscriptions accumulated by * previous addTriggeredSubscriptions() invocations */ std::string err; processSubscriptions(triggerEntitiesV, subsToNotify, err, tenant); /* Fill the response element */ responseP->duration = requestP->duration; responseP->registrationId.set(oid.toString()); responseP->errorCode.fill(SccOk); return SccOk; }