Skip to content

Instantly share code, notes, and snippets.

@fgalan
Created April 28, 2014 17:17
Show Gist options
  • Save fgalan/11378281 to your computer and use it in GitHub Desktop.
Save fgalan/11378281 to your computer and use it in GitHub Desktop.
mongoDiscoverContextAvailability.cpp @ IoT integration meeting Madrid April 2014
/*
*
* Copyright 2013 Telefonica Investigacion y Desarrollo, S.A.U
*
* This file is part of Orion Context Broker.
*
* Orion Context Broker is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Orion Context Broker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by this license please contact with
* fermin at tid dot es
*
* Author: Ken Zangelin
*/
#include <string>
#include "logMsg/logMsg.h"
#include "logMsg/traceLevels.h"
#include "common/statistics.h"
#include "common/sem.h"
#include "mongoBackend/mongoDiscoverContextAvailability.h"
#include "rest/HttpStatusCode.h"
#include "mongoBackend/MongoGlobal.h"
#include "ngsi/StatusCode.h"
#include "ngsi9/DiscoverContextAvailabilityRequest.h"
#include "ngsi9/DiscoverContextAvailabilityResponse.h"
#include "mongo/client/dbclient.h"
using namespace mongo;
/* ****************************************************************************
*
* associationsQuery -
*/
bool associationsQuery(EntityIdVector enV, AttributeList attrL, std::string scope, MetadataVector* mdV, std::string* err) {
DBClientConnection* connection = getMongoConnection();
/* Note that SCOPE_VALUE_ASSOC_SOURCE means that the argument is a target (so we use ASSOC_TARGET_ENT and
* ASSOC_ATTRS_TARGET in the query), while SCOPE_VALUE_ASSOC_TARGET means that the argument is a source (so we
* use ASSOC_SOURCE_ENT and ASSOC_ATTRS_source in the query) */
BSONObjBuilder queryB;
/* Build query (entity part) */
BSONArrayBuilder enArray;
for (unsigned int ix = 0; ix < enV.size() ; ++ix) {
enArray.append(BSON(ASSOC_ENT_ID << enV.get(ix)->id << ASSOC_ENT_TYPE << enV.get(ix)->type));
}
BSONObj queryEn;
if (scope == SCOPE_VALUE_ASSOC_SOURCE) {
queryB.append(ASSOC_TARGET_ENT, BSON("$in" << enArray.arr()));
}
else { // SCOPE_VALUE_ASSOC_TARGET
queryB.append(ASSOC_SOURCE_ENT, BSON("$in" << enArray.arr()));
}
/* Build query (attribute part) */
BSONArrayBuilder attrArray;
for (unsigned int ix = 0; ix < attrL.size() ; ++ix) {
attrArray.append(attrL.get(ix));
}
std::string attrField;
if (scope == SCOPE_VALUE_ASSOC_SOURCE) {
attrField = std::string(ASSOC_ATTRS) + "." + ASSOC_ATTRS_TARGET;
}
else { // SCOPE_VALUE_ASSOC_TARGET
attrField = std::string(ASSOC_ATTRS) + "." + ASSOC_ATTRS_SOURCE;
}
queryB.append(attrField, BSON("$in" << attrArray.arr()));
/* Do query in MongoDB */
BSONObj query = queryB.obj();
auto_ptr<DBClientCursor> cursor;
try {
LM_T(LmtMongo, ("query() in '%s' collection: '%s'", getAssociationsCollectionName(), query.toString().c_str()));
mongoSemTake(__FUNCTION__, "query in AssociationsCollection");
cursor = connection->query(getAssociationsCollectionName(), query);
/*
* We have observed that in some cases of DB errors (e.g. the database daemon is down) instead of
* raising an exception, the query() method sets the cursor to NULL. In this case, we raise the
* exception ourselves
*/
if (cursor.get() == NULL) {
throw DBException("Null cursor from mongo (details on this is found in the source code)", 0);
}
mongoSemGive(__FUNCTION__, "query in AssociationsCollection");
}
catch( const DBException &e ) {
mongoSemGive(__FUNCTION__, "query in AssociationsCollection (DBException)");
*err = std::string("collection: ") + getAssociationsCollectionName() +
" - query(): " + query.toString() +
" - exception: " + e.what();
return false;
}
catch(...) {
mongoSemGive(__FUNCTION__, "query in AssociationsCollection (Generic Exception)");
*err = std::string("collection: ") + getAssociationsCollectionName() +
" - query(): " + query.toString() +
" - exception: " + "generic";
return false;
}
/* Process query result */
while (cursor->more()) {
BSONObj r = cursor->next();
LM_T(LmtMongo, ("retrieved document: '%s'", r.toString().c_str()));
std::string name = STR_FIELD(r, "_id");
std::string srcEnId = STR_FIELD(r.getField(ASSOC_SOURCE_ENT).embeddedObject(), ASSOC_ENT_ID);
std::string srcEnType = STR_FIELD(r.getField(ASSOC_SOURCE_ENT).embeddedObject(), ASSOC_ENT_TYPE);
std::string tgtEnId = STR_FIELD(r.getField(ASSOC_TARGET_ENT).embeddedObject(), ASSOC_ENT_ID);
std::string tgtEnType = STR_FIELD(r.getField(ASSOC_TARGET_ENT).embeddedObject(), ASSOC_ENT_TYPE);
Metadata* md = new Metadata(name, "Association");
md->association.entityAssociation.source.id = srcEnId;
md->association.entityAssociation.source.type = srcEnType;
md->association.entityAssociation.source.isPattern = "false";
md->association.entityAssociation.target.id = tgtEnId;
md->association.entityAssociation.target.type = tgtEnType;
md->association.entityAssociation.target.isPattern = "false";
std::vector<BSONElement> attrs = r.getField(ASSOC_ATTRS).Array();
for (unsigned int ix = 0; ix < attrs.size(); ++ix) {
std::string srcAttr = STR_FIELD(attrs[ix].embeddedObject(), ASSOC_ATTRS_SOURCE);
std::string tgtAttr = STR_FIELD(attrs[ix].embeddedObject(), ASSOC_ATTRS_TARGET);
AttributeAssociation* attrAssoc = new AttributeAssociation();
attrAssoc->source = srcAttr;
attrAssoc->target = tgtAttr;
md->association.attributeAssociationList.push_back(attrAssoc);
}
mdV->push_back(md);
}
return true;
}
/* ****************************************************************************
*
* associationsDiscoverConvextAvailability -
*/
static HttpStatusCode associationsDiscoverConvextAvailability(DiscoverContextAvailabilityRequest* requestP, DiscoverContextAvailabilityResponse* responseP, std::string scope) {
if (scope == SCOPE_VALUE_ASSOC_ALL) {
LM_W(("%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)) {
responseP->errorCode.fill(SccReceiverInternalError, std::string("Database error: ") + err);
LM_RE(SccOk,(responseP->errorCode.details.c_str()));
}
/* 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)) {
responseP->errorCode.fill(SccReceiverInternalError, err);
LM_RE(SccOk,(responseP->errorCode.details.c_str()));
}
/* Accumulate in responseP */
for (unsigned int jx = 0; jx < crrV.size(); ++jx) {
responseP->responseVector.push_back(crrV.get(jx));
}
}
if (responseP->responseVector.size() != 0)
{
/* 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);
}
else
{
//
// FIXME P10: Two problems with associations discovered during Integration Meeting in Madrid 2014
//
// The two problems are (both discoveries with associations):
// 1. When nothing is found the response lacks the ErrorCode but it has that 'default' response
// 2. When the discovery has an empty attributeList, nothing is found
//
// It works just fine when something is found and the request contains a valid non-empty attributeList.
//
// Also, of course, the string "Include Associations" has to be "IncludeAssociations" -
// just change the define SCOPE_TYPE_ASSOC in Scope.h
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// I needed these two lines (errorCode.fill + LM_RE) to give a better reply for nothing found, but in the case
// of something found, I cannot have these lines present.
//
// We need a harness test to take care of the following situations:
//
// 1. Association found, but no entity responds to the association
// 2. Association found and all ok, but the discovery has an empty AttributeList
// 3. Association found and the discovery has a 'correct' AttributeList
// 4. Association not found
//
// Point 3. is already covered, and perhaps point 4 also, but the other two
// I don't think have any harness test.
//
// Actually, there are 12 combinations here (2*2*3):
// 1. Association FOUND / NOT FOUND
// 2. Responding entity FOUND /NOT FOUND
// 3. AttributeList NOT THERE / THERE WITH OK ATTR / THERE WITH 'BAD' ATTR
//
//
// Finally, here are the two lines that I needed for 'nothing found' but that breaks 'something found'
// responseP->errorCode.fill(SccContextElementNotFound, "Just Testing");
// LM_RE(SccOk, (responseP->errorCode.details.c_str()));
//
// Another thing we should fix:
// Uppercase 'A' in a few XML fields about associations:
// - AttributeAssociationList
// - AttributeAssociation
// These are probably/hopefully THE ONLY xml tags we have that start in uppercase ...
//
// I checked the tagnames in JSON also, but they aren't even there!
// WE DON'T SUPPORT associations in JSON !
}
return SccOk;
}
/* ****************************************************************************
*
* conventionalDiscoverContextAvailability -
*/
static HttpStatusCode conventionalDiscoverContextAvailability(DiscoverContextAvailabilityRequest* requestP, DiscoverContextAvailabilityResponse* responseP) {
std::string err;
if (!registrationsQuery(requestP->entityIdVector, requestP->attributeList, &responseP->responseVector, &err)) {
responseP->errorCode.fill(SccReceiverInternalError, err);
LM_RE(SccOk,(responseP->errorCode.details.c_str()));
}
if (responseP->responseVector.size() == 0) {
/* If the responseV is empty, we haven't found any entity and have to fill the status code part in the
* response */
responseP->errorCode.fill(SccContextElementNotFound);
return SccOk;
}
return SccOk;
}
/* ****************************************************************************
*
* mongoDiscoverContextAvailability -
*/
HttpStatusCode mongoDiscoverContextAvailability(DiscoverContextAvailabilityRequest* requestP, DiscoverContextAvailabilityResponse* responseP)
{
reqSemTake(__FUNCTION__, "mongo ngsi9 discovery request");
LM_T(LmtMongo, ("DiscoverContextAvailability Request"));
/* Depending on the scope used, we invoke one function or other. DiscoverContextAvailability may behave
* differently depending on the scope. Although OperationScope is a list in NGSI, we only support one
* scope at the same time */
int nScopes = requestP->restriction.scopeVector.size();
if (nScopes > 0) {
if (nScopes > 1) {
LM_W(("Using %d scopes: only the first one will be used", nScopes));
}
std::string scopeType = requestP->restriction.scopeVector.get(0)->type;
std::string scopeValue = requestP->restriction.scopeVector.get(0)->value;
if (scopeType == SCOPE_TYPE_ASSOC) {
HttpStatusCode ms = associationsDiscoverConvextAvailability(requestP, responseP, scopeValue);
reqSemGive(__FUNCTION__, "mongo ngsi9 discovery request (association)");
if (responseP->responseVector.size() != 0)
return ms;
else
LM_W(("Associations: Nothing found - doing conventional discoverContextAvailability"));
}
else {
LM_W(("Unsupported scope (%s, %s), doing conventional discoverContextAvailability", scopeType.c_str(), scopeValue.c_str()));
}
}
HttpStatusCode hsCode = conventionalDiscoverContextAvailability(requestP, responseP);
if (hsCode != SccOk)
++noOfDiscoveryErrors;
reqSemGive(__FUNCTION__, "mongo ngsi9 discovery request");
return hsCode;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment