/* **************************************************************************** * * mongoUpdateContextAvailabilitySubscription - */ HttpStatusCode mongoUpdateContextAvailabilitySubscription ( UpdateContextAvailabilitySubscriptionRequest* requestP, UpdateContextAvailabilitySubscriptionResponse* responseP, Format notifyFormat, const std::string& tenant ) { bool reqSemTaken; LM_T(LmtMongo, ("Update Context Subscription, notifyFormat: '%s'", formatToString(notifyFormat))); reqSemTake(__FUNCTION__, "ngsi9 update subscription request", SemWriteOp, &reqSemTaken); /* Look for document */ BSONObj sub; std::string err; OID id; if (!safeGetSubId(requestP->subscriptionId, &id, &(responseP->errorCode))) { reqSemGive(__FUNCTION__, "ngsi9 update subscription request (mongo assertion exception)", reqSemTaken); if (responseP->errorCode.code == SccContextElementNotFound) { std::string details = std::string("invalid OID format: '") + requestP->subscriptionId.get() + "'"; alarmMgr.badInput(clientIp, details); } else // SccReceiverInternalError { LM_E(("Runtime Error (exception getting OID: %s)", responseP->errorCode.details.c_str())); } return SccOk; } if (!collectionFindOne(getSubscribeContextAvailabilityCollectionName(tenant), BSON("_id" << id), &sub, &err)) { reqSemGive(__FUNCTION__, "ngsi9 update subscription request (mongo db exception)", reqSemTaken); responseP->errorCode.fill(SccReceiverInternalError, err); return SccOk; } if (sub.isEmpty()) { responseP->errorCode.fill(SccContextElementNotFound); reqSemGive(__FUNCTION__, "ngsi9 update subscription request (no subscriptions found)", reqSemTaken); 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 (mandatory) */ BSONArrayBuilder entities; for (unsigned int ix = 0; ix < requestP->entityIdVector.size(); ++ix) { EntityId* en = requestP->entityIdVector[ix]; if (en->type == "") { entities.append(BSON(CASUB_ENTITY_ID << en->id << CASUB_ENTITY_ISPATTERN << en->isPattern)); } else { entities.append(BSON(CASUB_ENTITY_ID << en->id << CASUB_ENTITY_TYPE << en->type << CASUB_ENTITY_ISPATTERN << en->isPattern)); } } newSub.append(CASUB_ENTITIES, entities.arr()); /* Attributes (always taken into account) */ BSONArrayBuilder attrs; for (unsigned int ix = 0; ix < requestP->attributeList.size(); ++ix) { attrs.append(requestP->attributeList[ix]); } newSub.append(CASUB_ATTRS, attrs.arr()); /* Duration (optional) */ if (requestP->duration.isEmpty()) { newSub.append(CASUB_EXPIRATION, getField(sub, CASUB_EXPIRATION).numberLong()); } else { long long expiration = getCurrentTime() + requestP->duration.parse(); newSub.append(CASUB_EXPIRATION, expiration); LM_T(LmtMongo, ("New subscription expiration: %l", expiration)); } /* Reference is not updatable, so it is appended directly */ newSub.append(CASUB_REFERENCE, getStringField(sub, CASUB_REFERENCE)); int count = sub.hasField(CASUB_COUNT) ? getIntField(sub, CASUB_COUNT) : 0; /* The hasField check is needed due to lastNotification/count could not be present in the original doc */ if (sub.hasField(CASUB_LASTNOTIFICATION)) { newSub.append(CASUB_LASTNOTIFICATION, getIntField(sub, CASUB_LASTNOTIFICATION)); } if (sub.hasField(CASUB_COUNT)) { newSub.append(CASUB_COUNT, count); } /* Adding format to use in notifications */ newSub.append(CASUB_FORMAT, std::string(formatToString(notifyFormat))); /* Update document in MongoDB */ if (!collectionUpdate(getSubscribeContextAvailabilityCollectionName(tenant), BSON("_id" << OID(requestP->subscriptionId.get())), newSub.obj(), false, &err)) { reqSemGive(__FUNCTION__, "ngsi9 update subscription request (mongo db exception)", reqSemTaken); responseP->errorCode.fill(SccReceiverInternalError, err); return SccOk; } /* Send notifications for matching context registrations */ processAvailabilitySubscription(requestP->entityIdVector, requestP->attributeList, requestP->subscriptionId.get(), getStringField(sub, CASUB_REFERENCE), notifyFormat, tenant); /* Duration is an optional parameter, it is only added in the case they * was used for update */ if (!requestP->duration.isEmpty()) { responseP->duration = requestP->duration; } responseP->subscriptionId = requestP->subscriptionId; reqSemGive(__FUNCTION__, "ngsi9 update subscription request", reqSemTaken); return SccOk; }
/* **************************************************************************** * * mongoUnsubscribeContextAvailability - */ HttpStatusCode mongoUnsubscribeContextAvailability ( UnsubscribeContextAvailabilityRequest* requestP, UnsubscribeContextAvailabilityResponse* responseP, const std::string& tenant ) { bool reqSemTaken; std::string err; reqSemTake(__FUNCTION__, "ngsi9 unsubscribe request", SemWriteOp, &reqSemTaken); LM_T(LmtMongo, ("Unsubscribe Context Availability")); /* No matter if success or failure, the subscriptionId in the response is always the one * in the request */ responseP->subscriptionId = requestP->subscriptionId; /* Look for document */ BSONObj sub; OID id; if (!safeGetSubId(requestP->subscriptionId, &id, &(responseP->statusCode))) { reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (safeGetSubId fail)", reqSemTaken); if (responseP->statusCode.code == SccContextElementNotFound) { // FIXME: doubt: invalid OID format? Or, subscription not found? std::string details = std::string("invalid OID format: '") + requestP->subscriptionId.get() + "'"; alarmMgr.badInput(clientIp, details); } else // SccReceiverInternalError { LM_E(("Runtime Error (exception getting OID: %s)", responseP->statusCode.details.c_str())); } return SccOk; } if (!collectionFindOne(getSubscribeContextAvailabilityCollectionName(tenant), BSON("_id" << id), &sub, &err)) { reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (mongo db exception)", reqSemTaken); responseP->statusCode.fill(SccReceiverInternalError, err); return SccOk; } alarmMgr.dbErrorReset(); if (sub.isEmpty()) { responseP->statusCode.fill(SccContextElementNotFound); reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (no subscriptions)", reqSemTaken); return SccOk; } /* Remove document in MongoDB */ // FIXME: I would prefer to do the find and remove in a single operation. Is the some similar // to findAndModify for this? if (!collectionRemove(getSubscribeContextAvailabilityCollectionName(tenant), BSON("_id" << OID(requestP->subscriptionId.get())), &err)) { reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (mongo db exception)", reqSemTaken); responseP->statusCode.fill(SccReceiverInternalError, err); return SccOk; } reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request", reqSemTaken); responseP->statusCode.fill(SccOk); return SccOk; }
/* **************************************************************************** * * mongoUnsubscribeContext - */ HttpStatusCode mongoUnsubscribeContext(UnsubscribeContextRequest* requestP, UnsubscribeContextResponse* responseP, const std::string& tenant) { bool reqSemTaken; std::string err; reqSemTake(__FUNCTION__, "ngsi10 unsubscribe request", SemWriteOp, &reqSemTaken); LM_T(LmtMongo, ("Unsubscribe Context")); /* No matter if success or failure, the subscriptionId in the response is always the one * in the request */ responseP->subscriptionId = requestP->subscriptionId; if (responseP->subscriptionId.get() == "") { reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (no subscriptions found)", reqSemTaken); responseP->statusCode.fill(SccContextElementNotFound); alarmMgr.badInput(clientIp, "no subscriptionId"); return SccOk; } /* Look for document */ BSONObj sub; OID id; if (!safeGetSubId(requestP->subscriptionId, &id, &(responseP->statusCode))) { reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (safeGetSubId fail)", reqSemTaken); if (responseP->statusCode.code == SccContextElementNotFound) { // FIXME: Doubt - invalid OID format? Or, just a subscription that was not found? std::string details = std::string("invalid OID format: '") + requestP->subscriptionId.get() + "'"; alarmMgr.badInput(clientIp, details); } else // SccReceiverInternalError { LM_E(("Runtime Error (exception getting OID: %s)", responseP->statusCode.details.c_str())); } return SccOk; } if (!collectionFindOne(getSubscribeContextCollectionName(tenant), BSON("_id" << id), &sub, &err)) { reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo db exception)", reqSemTaken); responseP->statusCode.fill(SccReceiverInternalError, err); return SccOk; } if (sub.isEmpty()) { reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (no subscriptions found)", reqSemTaken); responseP->statusCode.fill(SccContextElementNotFound, std::string("subscriptionId: /") + requestP->subscriptionId.get() + "/"); return SccOk; } /* Remove document in MongoDB */ // FIXME: I would prefer to do the find and remove in a single operation. Is there something similar // to findAndModify for this? if (!collectionRemove(getSubscribeContextCollectionName(tenant), BSON("_id" << OID(requestP->subscriptionId.get())), &err)) { reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo db exception)", reqSemTaken); responseP->statusCode.fill(SccReceiverInternalError, err); return SccOk; } /* Destroy any previous ONTIMEINTERVAL thread */ getNotifier()->destroyOntimeIntervalThreads(requestP->subscriptionId.get()); // // Removing subscription from mongo subscription cache // LM_T(LmtSubCache, ("removing subscription '%s' (tenant '%s') from mongo subscription cache", requestP->subscriptionId.get().c_str(), tenant.c_str())); cacheSemTake(__FUNCTION__, "Removing subscription from cache"); CachedSubscription* cSubP = subCacheItemLookup(tenant.c_str(), requestP->subscriptionId.get().c_str()); if (cSubP != NULL) { subCacheItemRemove(cSubP); } cacheSemGive(__FUNCTION__, "Removing subscription from cache"); reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request", reqSemTaken); responseP->statusCode.fill(SccOk); return SccOk; }
/* **************************************************************************** * * mongoUnsubscribeContext - */ HttpStatusCode mongoUnsubscribeContext(UnsubscribeContextRequest* requestP, UnsubscribeContextResponse* responseP, const std::string& tenant) { bool reqSemTaken; std::string err; reqSemTake(__FUNCTION__, "ngsi10 unsubscribe request", SemWriteOp, &reqSemTaken); LM_T(LmtMongo, ("Unsubscribe Context")); /* No matter if success or failure, the subscriptionId in the response is always the one * in the request */ responseP->subscriptionId = requestP->subscriptionId; if (responseP->subscriptionId.get() == "") { responseP->statusCode.fill(SccContextElementNotFound); LM_W(("Bad Input (no subscriptionId)")); return SccOk; } /* Look for document */ BSONObj sub; OID id; try { id = OID(requestP->subscriptionId.get()); } catch (const AssertionException &e) { reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo assertion exception)", reqSemTaken); // // This happens when OID format is wrong // FIXME: this checking should be done at parsing stage, without progressing to // mongoBackend. For the moment we can live this here, but we should remove in the future // (old issue #95) // responseP->statusCode.fill(SccContextElementNotFound); LM_W(("Bad Input (invalid OID format)")); return SccOk; } if (!collectionFindOne(getSubscribeContextCollectionName(tenant), BSON("_id" << id), &sub, &err)) { reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo db exception)", reqSemTaken); responseP->statusCode.fill(SccReceiverInternalError, err); return SccOk; } if (sub.isEmpty()) { reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (no subscriptions found)", reqSemTaken); responseP->statusCode.fill(SccContextElementNotFound, std::string("subscriptionId: /") + requestP->subscriptionId.get() + "/"); return SccOk; } /* Remove document in MongoDB */ // FIXME: I would prefer to do the find and remove in a single operation. Is there something similar // to findAndModify for this? if (!collectionRemove(getSubscribeContextCollectionName(tenant), BSON("_id" << OID(requestP->subscriptionId.get())), &err)) { reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo db exception)", reqSemTaken); responseP->statusCode.fill(SccReceiverInternalError, err); return SccOk; } /* Destroy any previous ONTIMEINTERVAL thread */ getNotifier()->destroyOntimeIntervalThreads(requestP->subscriptionId.get()); // FIXME P7: mongoSubCache stuff could be avoided if subscription is not patterned // // Removing subscription from mongo subscription cache // LM_T(LmtMongoSubCache, ("removing subscription '%s' (tenant '%s') from mongo subscription cache", requestP->subscriptionId.get().c_str(), tenant.c_str())); cacheSemTake(__FUNCTION__, "Removing subscription from cache"); CachedSubscription* cSubP = mongoSubCacheItemLookup(tenant.c_str(), requestP->subscriptionId.get().c_str()); if (cSubP != NULL) // Will only enter here if wildcard subscription { mongoSubCacheItemRemove(cSubP); } cacheSemGive(__FUNCTION__, "Removing subscription from cache"); reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request", reqSemTaken); responseP->statusCode.fill(SccOk); return SccOk; }