/* ****************************************************************************
*
* 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);
    }
  }
}
Example #3
0
/* ****************************************************************************
*
* 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;
}
Example #5
0
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),
                                                &notificationDone,
                                                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;
}