We use proprietary and third party's cookies to improve your experience and our services, identifying your Internet Browsing preferences on our website; develop analytic activities and display advertising based on your preferences. If you keep browsing, you accept its use. You can get more information on our Cookie Policy
Cookies Policy
Publish/Subscribe Broker - Orion Context Broker - User and Programmers Guide R3 - FIWARE Forge Wiki

Publish/Subscribe Broker - Orion Context Broker - User and Programmers Guide R3

From FIWARE Forge Wiki

Jump to: navigation, search

IMPORTANT NOTE: this page contains Orion Context Broker documentation until version 0.22.0 (May 2015). From 0.23.0 version on, the documentation is included in the Orion GitHub repository, along with the source code. You can get access to such documentation through Readthedocs as well (from 0.24.0 on).

Contents

Introduction

Welcome to the Orion Context Broker User and Programmers Guide!

The Orion Context Broker is an NGSI9/10 server implementation to manage context information and context information availability. Using the Orion Context Broker, you are able to register context elements and manage them through updates and queries. In addition, you can subscribe to context information so when some condition occurs (e.g. an interval of time has passed or the context elements have changed) you receive a notification. These usage scenarios and the Orion Context Broker features are described in this document.

This User and Programmers Guide covers Orion Context Broker versions since 0.5.0 (corresponding to FIWARE release 2.3.3). Please pay attention to the "Release Note" fragments along the document, as they contain important information regarding particular versions/releases, and special attention to the note on versions prior to 0.9.0 in the introduction to programmers guide

Any feedback on this document is highly welcome, including bug reports, typos or stuff you think should be included but isn't. Please send the feedback to the "Contact Person" email that appears in the Catalogue page for this GEi. Thanks in advance!

User Guide

Orion Context Broker runs as a backend service daemon. Thus, it doesn't have any Graphical User Interface (GUI). It is accessed through its REST API, described in the Programmers Guide section.

Programmers Guide

READ FIRST: important notes for users of old versions

Important for users of releases previous to 0.21.0:

  • The browsing type operations (i.e. "GET /v1/contextTypes" and "GET /v1/contextTypes/<type>/) has been simplified. Given that attribute type is no longer part of the attribute identification, it has been removed from the response and now only attribute name is returned. Although this implies a significative simplification in the response payload, it could break existing client implementations, as the XML/JSON structure changes.

Important for users of releases previous to 0.17.0:

  • The URI parameter used for selecting JSON-Object styles responses for attributes '?attributesFormat=object' has changed to the more correct name '?attributeFormat=object'. The old name is still supported, for backward compatibility. Just keep in mind that for releases prior to 0.17.0, 'attributesFormat' must be used.
  • For the release 0.17.0, the response type appendContextElementResponse was given a new field to describe the EntityId.

Older releases don't have this information in the response. In the examples throughout this document this part of the payload is included, just remember that before the release 0.17.0 it was not returned in the response. The affected convenience operations are:

    • POST /v1/contextEntities
    • POST /v1/contextEntities/{EntityId::id}
    • POST /v1/contextEntities/type/{EntityId::type}/id/{EntityId::id}
  • location metadata value "WSG84" was a typo, so it has been changed to the right value "WGS84" (see section on defining location attribute). All the examples in this document use the new value, although the old one is still supported in request to enable backward compatibility.

Important for users of releases previous to 0.16.0:

  • In order to simplify the API the URL prefixes "ngsi10/" and "ngsi9/" has changed to "v1/" and "v1/registry/" (people use to be confused about "9" and "10", thinking they refer to versions of the API instead of different functional parts of it). All the examples in the documentation has been adapted to use them. However, the "ngsi10/" and "ngsi9/ prefixes will be mantaneined also as "alias" for all the old operations.
  • Scope type "FIWARE_Location" (used for geo-located queries) has been changed to "FIWARE::Location" (to align with other scope types introduced in 0.16.0). However, the old "FIWARE_Location" is still supported, maintaning backward compatibility.

Important for users of releases previous to 0.10.0: this version solves some issues recently detected in the JSON encoding. In this sense, you will note some slight changes in some JSON payloads shown along this document compared to previous versions of this manual. Depending on how your context-based application generates and process the JSON, you may need to do some small changes. In particular, the way context broker behaves from 0.10.0 on follows this:

  • "value" is the only valid name for the JSON field used to specify an attribute value (previous versions of the documentation sometimes use "contextValue"). Not fully solved in 0.10.0, but solved in 0.11.0.
  • JSON vectors ("[ ... ]") are enforced for the fields whose value is a list of elements (e.g. "contextElements" in updateContextRequest). Thus, the use of a JSON object ("{ ... }") as value instead of a vector in the case of mono-element lists is not allowed anymore.

Important for users of releases previous to 0.9.0:

  • You have probably noticed that the structure of this manual has changed,as previous version didn't have separated and independent sections for NGSI9 and NGSI10. The change has been motivated due to now NGSI9 and NGSI10 are completely independent functionalities. For example, you don't need to do a registerContext request (NGSI9) to "create" and entity before doing an updateContext request (NGSI10) on it. We decided to make both independent after the feedback we get from developers during the Campus Party London and Santander 2013 hackathons, to make things easier in many context broker usage cases. Thus, the structure of the manual has changed to reflect that.
  • In release 0.9.0 we have paid a lot of attention to improve our alignment with FIWARE NGSI9/10 XSD in XML payloads in requests and responses (the XSD specification is included in this file). In this sense, you will note some slight changes in the XML payloads shown along this document compared to previous versions of this manual. Depending on how your context-based application generates and process the XML, you may need to do some small changes. In particular, the way context broker behaves from 0.9.0 on follows this:
    • <duration> precedes <registrationId> in RegisterContextResponse
    • <statusCode> has been renamed to <responseCode> in notifyContextResponse and notifyContextAvailabilityResponse
    • <subscriptionId> precedes <duration> in NGSI9 and NGS10 subscriptions
    • Empty <throttling> is not allowed
    • Action type in updateContextRequest has to use uppercase (UPDATE, APPEND or DELETE)

Introduction to Programmers Guide

This guide adopts a practical approach that we hope will help our readers to get familiar with the Orion Context Broker and have some fun in the process :).

The first two sections on context management using NGSI10 and context availability management using NGSI9 are the main ones. They describe the basic context broker functionality, both for context management (information about entities, such as the temperature of a car) and context availability management (information not about the entities themselves, but about the providers of that information). Some remarks to take into account to use this stuff:

  • Context management and context availability management are independent functionalities (corresponding to different parts of the NGSI interface, NGS10 and NGSI9 respectively), so you can use the broker for one purpose, the other purpose, or both of them.
  • Note that each main section is divided in two sub-sections: the first being about standard operations while the second is about convenience operations. In fact, each sub-section is an independent tutorial (for a total of 4 tutorials summing up both sections) that can be done in a step-by-step manner, just copy-pasting the commands from this document. Note that since release 0.9.0, Orion Context Broker supports JSON rendering (preferred by many developers, as it is easier to use than XML) so we provide two versions of each example: one in XML and one in JSON.
  • Before starting a tutorial (or if you get lost in the middle and need to start from scratch :), restart Orion Context Broker as described in starting the broker for the tutorials.
  • It is recommended to start with the tutorial on standard operations, and then do the tutorial on convenience operations (some explanations and concepts described in the former are needed for the latter).

The third section describes some advanced topics of the Orion Context Broker. This is optional material and you don't actually need it to build applications using the broker. Although you find links from tutorials to the advanced topics section, our recommendation is to first get familiar with the basic material before starting with the advanced topics.

The fourth section describes the FI-LAB context management platform. This information is particularly useful for FI-LAB users, if you plan to use Orion Context Broker independently of FI-LAB, you can skip this section.

Finally, you have a section with additional information and resources, including code snippets contributed by users.

It is recommended to get familiar with the theoretical concepts on which the NGSI model is based before starting. E.g. entities, attributes, etc. Have a look at the FIWARE documentation about this, e.g. this public presentation or this public video.

Before starting, let's introduce the example case that is used in the tutorials and how to run and interact with Orion Context Broker.


Example Case

Let's assume we have a building with several rooms and that we want to use Orion Context Broker to manage its context information. The rooms are Room1, Room2, Room3 and Room4 and each room has two sensors: temperature and (atmospheric) pressure (except Room4, that only has a pressure sensor). In addition, let's consider that we have two cars (Car1 and Car2) with sensors able to measure speed and location (in GPS sense).

Most of the time we will use Room1 and Room2 in the tutorials. Room3, Room4, Car1 and Car2 will be used only in the section regarding context availability subscriptions.

The Orion Context Broker interacts with context producer applications (which provide sensor information) and a context consumer application (which processes that information, e.g. to show it in a graphical user interface). We will play the role of both kinds of applications in the tutorials.

Starting the broker for the tutorials

Before starting, you need to install the broker as described in the Installation and Administration Guide.

The tutorials assume that you don't have any previous content in the Orion Context Broker database. In order to do so, follow the delete database procedure.

To start the broker (as root or using the sudo command):

 /etc/init.d/contextBroker start

To restart the broker (as root or using the sudo command):

 /etc/init.d/contextBroker restart

Starting accumulator server for the tutorials

Some parts of the tutorial (the ones related with subscriptions and notifications) require some process to play the role of the consumer application able to receive notifications. To that end, download the accumulator script, available at GitHub. It is a very simple "dummy" application that just listens to a given URL (let's use localhost:1028/accumulate) and prints whatever it gets in the terminal window where it is executed. Run it using the following command:

 # cd /dir/where/accumulator-server/is/downloaded
 # chmod a+x accumulator-server.py
 # ./accumulator-server.py 1028 /accumulate ::1 on

Notes:

  • The accumulator-server.py is also part of the contextBroker-test package (see in the administrator manual how to install). The script is located at /usr/share/contextBroker/tests/accumulator-server.py after installation. However, if you only need the accumulator-server.py it uses to be simpler just downloading it from GitHub, as suggested above.
  • If you are using pretty old Orion version (0.14.0 or before) you will probably see traces about "Broken Pipe" errors when using the accumulator-server.py. Don't worry: this is due to the way in which the underlying HTTP library in Python deals with HTTP connection ends in some cases and it is completely harmless.

Issuing commands to the broker

To issue requests to the broker, we use the curl command line tool. We have chosen curl because it is almost ubiquitous in any GNU/Linux system and simplifies including examples in this document that can easily be copied and pasted. Of course, it is not mandatory to use curl, you can use any REST client tool instead (e.g. RESTClient). Indeed, in a real case, you will probably interact with the Orion Context Broker using a programming language library implementing the REST client part of your application.

The basic patterns for all the curl examples in this document are the following (XML case):

  • For POST:
curl localhost:1026/<operation_url> -s -S [headers]' -d @- <<EOF
[payload]
EOF
  • For PUT:
(curl localhost:1026/<operation_url> -s -S [headers] -X PUT -d @- <<EOF
[payload]
EOF
  • For GET:
curl localhost:1026/<operation_url> -s -S [headers]' 
  • For DELETE:
curl localhost:1026/<operation_url> -s -S [headers] -X DELETE

Regarding [headers] you have to include the following ones:

  • Accept header to specify which payload format (either XML or JSON) you want to receive in the response (if you don't specify a Accept header, the default is XML):
curl ... --header 'Accept: application/json' ...
curl ... --header 'Accept: application/xml' ...
  • Only in the case of using payload in the request (i.e. POST or PUT), you have to use Context-Type header to specify the format (either XML or JSON).
curl ... --header 'Content-Type: application/json' ...
curl ... --header 'Content-Type: application/xml' ...

Release note 0.9.0 or before: versions previous to 0.9.0 doesn't support JSON at all. Version 0.9.0 support JSON for standard operations, but not for convenience operations.

Some additional remarks:

  • We are using multi-line shell commands to provide the input to curl, using EOF to mark the beginning and the end of the multi-line block (here-documents). An alternative to this is to put the XML in a file (e.g. payload.xml), then use "--data-binary @payload.xml". In some cases (GET and DELETE) we omit "-d @-" as they don't use payload.
  • In our examples we assume that the broker is listening on port 1026. Adjust this in the curl command line if you are using a different setting.
  • In order to pretty-print XML in responses, you can use xmllint (examples along with tutorial are using this style):
(curl ... | xmllint --format -) <<EOF
...
EOF
  • In order to pretty-print JSON in responses, you can use Python with msjon.tool (examples along with tutorial are using this style):
(curl ... | python -mjson.tool) <<EOF
...
EOF
  • Check that curl (and xmllint, if you plan to use it for pretty-printing) are installed in your system using:
 # which curl
 # which xmllint

Text colour convention

This document assumes the following convention for text colour in XML fragments:

  • Blue: request messages
  • Green: response messages
  • Purple: notification messages

Context management using NGSI10

Tutorial on NGSI10 standard operations

This section describes the different standard NGSI10 operations that the Orion Context Broker supports, showing examples of requests and responses. We use the term "standard" as they are directly derived from the OMA NGSI specification, to distinguish them from the other family of operations ("convenience") which has been defined by the FIWARE project to ease the usage of NGSI implementations (see the section on additional information later in this manual).

Don't forget to restart the broker before starting this tutorial as described previously in this document.

At the end of this section, you will have the basic knowledge to create applications (both context producers and consumers) using Orion Context Broker with NGSI10 standard operations:

  • updateContext
  • queryContext
  • subscribeContext
  • updateContextSubscription
  • unsubscribeContext

Entity Creation

Orion Context Broker will start in an empty state, so first of all we need to make it aware of the existence of certain entities. In particular, we are going to "create" Room1 and Room2 entities, each one with two attributes (temperature and pressure). We do this using the updateContext operation with APPEND action type (the other main action type, UPDATE, will be discussed in a next section).

First, we are going to create Room1. Let's assume that at entity creation time temperature and pressure of Room1 are 23 ºC and 720 mmHg respectively.

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>23</contextValue>
        </contextAttribute>
        <contextAttribute>
          <name>pressure</name>
          <type>integer</type>
          <contextValue>720</contextValue>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>APPEND</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "contextElements": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room1",
            "attributes": [
            {
                "name": "temperature",
                "type": "float",
                "value": "23"
            },
            {
                "name": "pressure",
                "type": "integer",
                "value": "720"
            }
            ]
        }
    ],
    "updateAction": "APPEND"
}
EOF

The updateContext request payload contains a list of contextElement elements. Each contextElement is associated to an entity (whose identification is provided in the entityId element, in this case we provide the identification for Room1) and contains a list of contextAttribute elements ('attributes' for short, in JSON). Each contextAttribute provides the value for a given attribute (identified by name and type) of the entity. Apart from the list of contextElement elements, the payload includes also an updateAction element. We use APPEND, which means that we want to add new information.

Release Note (any version): Orion Context Broker doesn't perform any checking on types (e.g. it doesn't check that when a context producer application updates the value of the temperature, this value is formatted as a float like "25.5" or "-40.23" and not something like "hot").

Upon receipt of this request, the broker will create the entity in its internal database, set the values for its attributes and will response with the following:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<updateContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room1</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue/>
          </contextAttribute>
          <contextAttribute>
            <name>pressure</name>
            <type>integer</type>
            <contextValue/>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</updateContextResponse>
{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": ""
                    },
                    {
                        "name": "pressure",
                        "type": "integer",
                        "value": ""
                    }
                ],
                "id": "Room1",
                "isPattern": "false",
                "type": "Room"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

As you can see, it follows the same structure as the request, just to acknowledge that the request was correctly processed for these context elements. You probably wonder why contextValue elements are empty in this case, but actually you don't need the values in the response because you were the one to provide them in the request.

Next, let's create Room2 in a similar way (in this case, setting temperature and pressure to 21 ºC and 711 mmHg respectively).

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room2</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>21</contextValue>
        </contextAttribute>
        <contextAttribute>
          <name>pressure</name>
          <type>integer</type>
          <contextValue>711</contextValue>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>APPEND</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool ) <<EOF
{
    "contextElements": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room2",
            "attributes": [
            {
                "name": "temperature",
                "type": "float",
                "value": "21"
            },
            {
                "name": "pressure",
                "type": "integer",
                "value": "711"
            }
            ]
        }
    ],
    "updateAction": "APPEND"
}
EOF

The response to this request is:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<updateContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room2</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue/>
          </contextAttribute>
          <contextAttribute>
            <name>pressure</name>
            <type>integer</type>
            <contextValue/>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</updateContextResponse>
{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": ""
                    },
                    {
                        "name": "pressure",
                        "type": "integer",
                        "value": ""
                    }
                ],
                "id": "Room2",
                "isPattern": "false",
                "type": "Room"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

Apart from simple values (i.e. strings) for attribute values, you can also use complex structures or custom metadata. These are advance topics, described in this section and this other, respectively.

Query Context operation

Now let's play the role of a consumer application, wanting to access the context information stored by Orion Context Broker to do something interesting with it (e.g. show a graph with the room temperature in a graphical user interface). The NGSI10 queryContext request is used in this case, e.g. to get context information for Room1:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <attributeList/>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room1"
        }
    ]
}
EOF

The response includes all the attributes belonging to Room1 and we can check that temperature and pressure have the values that we set at entity creation with updateContext (23ºC and 720 mmHg).

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<queryContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room1</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue>23</contextValue>
          </contextAttribute>
          <contextAttribute>
            <name>pressure</name>
            <type>integer</type>
            <contextValue>720</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</queryContextResponse>
{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": "23"
                    },
                    {
                        "name": "pressure",
                        "type": "integer",
                        "value": "720"
                    }
                ],
                "id": "Room1",
                "isPattern": "false",
                "type": "Room"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

If you use an empty attributeList element in the request ('attributes' for short, in JSON), the response will include all the attributes of the entity. If you include an actual list of attributes (e.g. temperature) only that are retrieved, as shown in the following request:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>temperature</attribute>
  </attributeList>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
    {
        "type": "Room",
        "isPattern": "false",
        "id": "Room1"
    }
    ],
    "attributes" : [
        "temperature"
    ]
}
EOF

which response is as follows:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<queryContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room1</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue>23</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</queryContextResponse>
{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": "23"
                    }
                ],
                "id": "Room1",
                "isPattern": "false",
                "type": "Room"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

Moreover, a powerful feature of Orion Context Broker is that you can use a regular expression for the entity ID. For example, you can query entities which ID starts with "Room" using the regex "Room.*". In this case, you have to set isPattern to "true" as shown below:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
    <entityId type="Room" isPattern="false">
      <id>Room2</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>temperature</attribute>
  </attributeList>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
    {
        "type": "Room",
        "isPattern": "false",
        "id": "Room1"
    },
    {
        "type": "Room",
        "isPattern": "false",
        "id": "Room2"
    }
    ],
    "attributes" : [
        "temperature"
    ]
}
EOF
XML JSON (since release 0.9.0)
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="true">
      <id>Room.*</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>temperature</attribute>
  </attributeList>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
    {
        "type": "Room",
        "isPattern": "true",
        "id": "Room.*"
    }
    ],
    "attributes" : [
    "temperature"
    ]
}
EOF

Both produce the same response:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<queryContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room1</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue>23</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room2</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue>21</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</queryContextResponse>
{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": "23"
                    }
                ],
                "id": "Room1",
                "isPattern": "false",
                "type": "Room"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        },
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": "21"
                    }
                ],
                "id": "Room2",
                "isPattern": "false",
                "type": "Room"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

Finally, note that you will get an error in case you try to query a non-existing entity or attribute, as shown in the following cases below:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room5</id>
    </entityId>
  </entityIdList>
  <attributeList/>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
    {
        "type": "Room",
        "isPattern": "false",
        "id": "Room5"
    }
    ]
}
EOF
XML JSON (since release 0.9.0)
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>humidity</attribute>
  </attributeList>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
    {
        "type": "Room",
        "isPattern": "false",
        "id": "Room1"
    }
    ],
    "attributes" : [
        "humidity"
    ]
}
EOF

Both requests will produce the same error response:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<queryContextResponse>
  <errorCode>
    <code>404</code>
    <reasonPhrase>No context elements found</reasonPhrase>
  </errorCode>
</queryContextResponse>
{
    "errorCode": {
        "code": "404",
        "reasonPhrase": "No context elements found"
    }
}

Additional comments:

  • You can also use geographical scopes in your queries. This is an advance topic, described in this section.
  • Note that by default only 20 entities are returned (which is fine for this tutorial, but probably not for a real utilization scenario). In order to change this behaviour, see the section on pagination in this manual.
  • Since 0.15.0: In the case of JSON responses, you can use the ?attributeFormat=object URI parameter to get attributes as a JSON object (i.e. key-values map) instead of a vector (default behaviour):
(curl 'localhost:1026/v1/queryContext?attributeFormat=object' -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
       {
           "type": "Room",
           "isPattern": "false",
           "id": "Room1"
       }
    ]
}
EOF
{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": {
                    "pressure": {
                        "type": "integer",
                        "value": "720"
                    },
                    "temperature": {
                        "type": "float",
                        "value": "23"
                    }
                },
                "id": "Room1",
                "isPattern": "false",
                "type": "Room"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

Update context elements

You can update the value of entities attributes using the updateContext operation with UPDATE action type. The basic rule to take into account with updateContext is that APPEND creates new context elements, while UPDATE updates already existing context elements (however, current Orion Context Broker version interprets APPEND as UPDATE if the entity already exists).

Now we will play the role of a context producer application, i.e. a source of context information. Let's assume that this application in a given moment wants to set the temperature and pressure of Room1 to 26.5 ºC and 763 mmHg respectively, so it issues the following request:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>26.5</contextValue>
        </contextAttribute>
        <contextAttribute>
          <name>pressure</name>
          <type>integer</type>
          <contextValue>763</contextValue>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>UPDATE</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "contextElements": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room1",
            "attributes": [
            {
                "name": "temperature",
                "type": "float",
                "value": "26.5"
            },
            {
                "name": "pressure",
                "type": "integer",
                "value": "763"
            }
            ]
        }
    ],
    "updateAction": "UPDATE"
}
EOF

As you can see, the structure of the request is exactly the same we used for updateContext with APPEND for creating entities, except we use UPDATE now as action type.

Upon receipt of this request, the broker will update the values for the entity attributes in its internal database and will response with the following:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<updateContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room1</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue/>
          </contextAttribute>
          <contextAttribute>
            <name>pressure</name>
            <type>integer</type>
            <contextValue/>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</updateContextResponse>
{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": ""
                    },
                    {
                        "name": "pressure",
                        "type": "integer",
                        "value": ""
                    }
                ],
                "id": "Room1",
                "isPattern": "false",
                "type": "Room"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

Again, the structure of the response is exactly the same one we used for updateContext with APPEND for creating entities.

The updateContext operation is quite flexible as it allows you to update as many entities and attributes as you want: it is just a matter of which contextElements you include in the list. You could even update the whole database of Orion Context Broker (maybe including thousands of entities/attributes) in just one updateContext operation (at least in theory).

To illustrate this flexibility, we will show how to update Room2 in two separated updateContext request (setting its temperature to 27.4 ºC and its pressure to 755 mmHg), each one targeting just one attribute. This also illustrates that you don't need to include all the attributes of an entity in the updateContext, just the ones you want to update (the other attributes maintain their current value).

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room2</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>27.4</contextValue>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>UPDATE</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "contextElements": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room2",
            "attributes": [
            {
                "name": "temperature",
                "type": "float",
                "value": "27.4"
            }
            ]
        }
    ],
    "updateAction": "UPDATE"
}
EOF
XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room2</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>pressure</name>
          <type>integer</type>
          <contextValue>755</contextValue>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>UPDATE</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "contextElements": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room2",
            "attributes": [
            {
                "name" : "pressure",
                "type" : "integer",
                "value" : "755"
            }
            ]
        }
    ],
    "updateAction": "UPDATE"
}
EOF

The responses for these requests are respectively:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<updateContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room2</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue/>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</updateContextResponse>
{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": ""
                    }
                ],
                "id": "Room2",
                "isPattern": "false",
                "type": "Room"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}
XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<updateContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room2</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>pressure</name>
            <type>integer</type>
            <contextValue/>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</updateContextResponse>
{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "pressure",
                        "type": "integer",
                        "value": ""
                    }
                ],
                "id": "Room2",
                "isPattern": "false",
                "type": "Room"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

Now, you can use queryContext operation as previously described to check that Room1 and Room2 attributes has been actually updated.

Apart from simple values (i.e. strings) for attribute values, you can also use complex structures. This is an advance topic, described in this section.

Context subscriptions

The NGSI10 operations you know up to now (updateContext and queryContext) are the basic building blocks for synchronous context producer and context consumer applications. However, Orion Context Broker has another powerful feature that you can take advantage of: the ability to subscribe to context information so when "something" happens (we will explain the different cases for that "something") your application will get an asynchronous notification. In that way, you don't need to continuously repeat queryContext requests (i.e. polling), the Orion Context Broker will let you know the information when it comes.

Before starting to play with feature, start the accumulator server to capture notifications.

Actually, there are two kinds of subscribeContext: ONTIMEINTERVAL and ONCHANGE subscriptions, described in the next two subsections.

Release Note (any version): NGSI standard describes a third subscription type, called ONVALUE, but the current version of the Orion Context Broker doesn't support it.

ONTIMEINTERVAL

The following is the request corresponding to an ONTIMEINTERVAL subscription:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/subscribeContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0"?>
<subscribeContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>temperature</attribute>
  </attributeList>
  <reference>http://localhost:1028/accumulate</reference>
  <duration>P1M</duration>
  <notifyConditions>
    <notifyCondition>
      <type>ONTIMEINTERVAL</type>
      <condValueList>
        <condValue>PT10S</condValue>
      </condValueList>
    </notifyCondition>
  </notifyConditions>
</subscribeContextRequest>
EOF
(curl localhost:1026/v1/subscribeContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room1"
        }
    ],
    "attributes": [
        "temperature"
    ],
    "reference": "http://localhost:1028/accumulate",
    "duration": "P1M",
    "notifyConditions": [
        {
            "type": "ONTIMEINTERVAL",
            "condValues": [
                "PT10S"
            ]
        }
    ]
}
EOF

Let's examine in detail the different elements included in the payload:

  • entityIdList and attributeList ('entities' and 'attributes' for short, in JSON) define which context elements will be included in the notification message. They work the same way as the XML elements with the same name in queryContext request. You can even include lists or patterns to specify entities. In this example, we are specifying that the notification has to include the temperature attribute for entity Room1.
  • The callback URL to send notifications is defined with the reference element. We are using the URL of the accumulator-server.py program started before. Only one reference can be included per subscribeContext request. However, you can have several subscriptions on the same context elements (i.e. same entityIdList and attributeList) without any problem. Default URL schema (in the case you don't specify any) is "http", e.g. using "localhost:1028" as reference will be actually interpreted as "http://localhost:1028".
  • Subscriptions have a duration, specified using the ISO 8601 standard format. Once that duration is expired, the subscription is simply ignored (however, it is still stored in the broker database and needs to be purged using the procedure described in the administration manual). You can extend the duration of a subscription by updating it, as described later in this document. We are using "P1M" which means "one month".
  • The notifyCondition element defines the "trigger" for the subscription. There is a type element (which value in this case is ONTIMERINTERVAL) and a condValueList element. The condValueList element structure depends on the type. In the case of ONTIMEINTERVAL, it includes exactly one condValue child element whose value is a time interval (using again, as usual in NGSI, the ISO 8601 format). A notification is sent with a frequency equal to that interval. In the example above we are using 10 seconds as interval.

The response corresponding to that request contains a subscription ID (a 24 hexadecimal number used for updating and cancelling the subscription - write it down because you will need it later in this tutorial) and a duration acknowledgement:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<subscribeContextResponse>
  <subscribeResponse>
    <subscriptionId>51c04a21d714fb3b37d7d5a7</subscriptionId>
    <duration>P1M</duration>
  </subscribeResponse>
</subscribeContextResponse>
{
    "subscribeResponse": {
        "duration": "P1M",
        "subscriptionId": "51c04a21d714fb3b37d7d5a7"
    }
}

If you look at the accumulator-script.py terminal window, you will see that a message resembling the following one is received each 10 seconds:

XML JSON (since release 0.9.0)
POST http://localhost:1028/accumulate
Content-Length: 739
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/xml

<notifyContextRequest>
  <subscriptionId>51c04a21d714fb3b37d7d5a7</subscriptionId>
  <originator>localhost</originator>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room1</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue>26.5</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</notifyContextRequest>
POST http://localhost:1028/accumulate
Content-Length: 492
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/json

{
  "subscriptionId" : "51c04a21d714fb3b37d7d5a7",
  "originator" : "localhost",
  "contextResponses" : [
    {
      "contextElement" : {
        "attributes" : [
          {
            "name" : "temperature",
            "type" : "float",
            "value" : "26.5"
          }
        ],
        "type" : "Room",
        "isPattern" : "false",
        "id" : "Room1"
      },
      "statusCode" : {
        "code" : "200",
        "reasonPhrase" : "OK"
      }
    }
  ]
}

Orion Context Broker notifies NGSI10 subscribeContext using the POST HTTP method (on the URL used as reference for the subscription) with a notifyContextRequest payload. Apart from the subscriptionId element (that matches the one in the response to subscribeContext request) and the originator element, there is a contextResponseList element which is the same that the one used in the queryContext responses.

Release Note (any version): currently, the originator is always "localhost". We will look into a more flexible way of using this in a later version.

You can do a small exercise: change the temperature value of Room1 (have a look at the update context elements section in this manual to see how to do it) and after that, check that in the next received notifyContextRequest for accumulator-server.py the contextValue element contains the new value. This exercise demanstrates that the Orion Context Broker always notifies the updated value in ONTIMEINTERVAL subscriptions.

Subscriptions can be updated using the NGSI10 updateContextSubcription. The request includes a subscriptionId that identifies the subscription to modify and the actual update payload. For example, if we want to change the notification interval to 5 seconds we will use the following (of course, replace the subscriptionId value after copy-paste with the one that you have got in the subscribeContext response in the previous step) command:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContextSubscription -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<updateContextSubscriptionRequest>
  <subscriptionId>51c04a21d714fb3b37d7d5a7</subscriptionId>
  <notifyConditions>
    <notifyCondition>
      <type>ONTIMEINTERVAL</type>
      <condValueList>
        <condValue>PT5S</condValue>
      </condValueList>
    </notifyCondition>
  </notifyConditions>
</updateContextSubscriptionRequest>
EOF
(curl localhost:1026/v1/updateContextSubscription -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "subscriptionId": "51c04a21d714fb3b37d7d5a7",
    "notifyConditions": [
        {
            "type": "ONTIMEINTERVAL",
            "condValues": [
                "PT5S"
            ]
        }
    ]
}
EOF

The response is very similar to the one for subscribeContext request:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<updateContextSubscriptionResponse>
  <subscribeResponse>
    <subscriptionId>51c04a21d714fb3b37d7d5a7</subscriptionId>
  </subscribeResponse>
</updateContextSubscriptionResponse>
{
  "subscribeResponse" : {
    "subscriptionId" : "51c04a21d714fb3b37d7d5a7",
  }
}

You can check in accumulator-server.py that the notification frequency has changed to 5 seconds.

Finally, you can cancel a subscription using the NGSI10 unsubscribeContext operation, that just uses de subscriptionId in the request payload (replace the subscriptionId value after copy-paste with the one that you get in the subscribeContext response in the previous step):

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/unsubscribeContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<unsubscribeContextRequest>
  <subscriptionId>51c04a21d714fb3b37d7d5a7</subscriptionId>
</unsubscribeContextRequest>
EOF
(curl localhost:1026/v1/unsubscribeContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "subscriptionId": "51c04a21d714fb3b37d7d5a7"
}
EOF

The response is just an acknowledgement of that the cancellation was successful.

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<unsubscribeContextResponse>
  <subscriptionId>51c04a21d714fb3b37d7d5a7</subscriptionId>
  <statusCode>
    <code>200</code>
    <reasonPhrase>OK</reasonPhrase>
  </statusCode>
</unsubscribeContextResponse>
{
    "statusCode": {
        "code": "200",
        "reasonPhrase": "OK"
    },
    "subscriptionId": "51c04a21d714fb3b37d7d5a7"
}

You can have a look at accumulator-server.py to check that the notification flow has stopped.

Release Note (minor or equal to 0.6.0 FIWARE 3.1.1): due to a bug in NGSI processing, use the following payload in the requests (using the "intermediate" <unsubscribeContext> XML element). Only XML case (JSON rendering was not included in version 0.6.0).

(curl localhost:1026/v1/unsubscribeContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<unsubscribeContextRequest>
  <unsubscribeContext>
    <subscriptionId>51c04a21d714fb3b37d7d5a7</subscriptionId>
  </unsubscribeContext>
</unsubscribeContextRequest>
EOF
ONCHANGE

We assume that the accumulator-server.py program is still running. Otherwise, start it as described here.

ONCHANGE subscriptions are used when you want to be notified not when a given time interval has passed but when some attribute changes. Let's consider the following example:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/subscribeContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0"?>
<subscribeContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>temperature</attribute>
  </attributeList>
  <reference>http://localhost:1028/accumulate</reference>
  <duration>P1M</duration>
  <notifyConditions>
    <notifyCondition>
      <type>ONCHANGE</type>
      <condValueList>
        <condValue>pressure</condValue>
      </condValueList>
    </notifyCondition>
  </notifyConditions>
  <throttling>PT5S</throttling>
</subscribeContextRequest>
EOF
(curl localhost:1026/v1/subscribeContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room1"
        }
    ],
    "attributes": [
        "temperature"
    ],
    "reference": "http://localhost:1028/accumulate",
    "duration": "P1M",
    "notifyConditions": [
        {
            "type": "ONCHANGE",
            "condValues": [
                "pressure"
            ]
        }
    ],
    "throttling": "PT5S"
}
EOF

Having a look at the payload we can check that it is very similar to the one used in ONTIMEINTERVAL, with two exceptions:

  • The notifyCondition element uses the type ONCHANGE (obviously :) but, in this case the condValueList contains an actual list of condValue elements, each one with an attribute name. They define the "triggering attributes", i.e. attributes that upon creation/change due to entity creation or update trigger the notification. The rule is that if at least one of the attributes in the list changes (e.g. some kind of "OR" condition), then a notification is sent. But note that that notification includes the attributes in the attributeList part, which doesn't necessarily include any attribute in the condValue. For example, in this case, when Room1 pressure changes the Room1 temperature value is notified, but not pressure itself. If you want also pressure to be notified, the request would need to include <attribute>pressure</attribute> within the attributeList (or to use an empty attributeList, which you already know means "all the attributes in the entity"). Now, this example here, to be notified of the value of temperature each time the value of pressure changes may not be too useful. The example is chosen this way only to show the enormous flexibility of subscriptions.
  • The throttling element is used to specify a minimum inter-notification arrival time. So, setting throttling to 5 seconds as in the example above makes that a notification will not be sent if a previous notification was sent less than 5 seconds ago, no matter how many actual changes take place in that period. This is to not stress the notification receptor in case of having context producers that update attribute values too frequently. Actually, throttling is not an "exclusive" field for ONCHANGE subscriptions: from a theoretical point of view it can be used in ONTIMEINTERVAL subscriptions but, given that in that case you can precisely control the notification frequency it doesn't have any practical sense.

As in ONTIMEINTERVAL subscriptions, the response consists of a subscription ID, a duration acknowledgement and (given that we used throttling in the request) a throttling acknowledgement:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<subscribeContextResponse>
  <subscribeResponse>
    <subscriptionId>51c0ac9ed714fb3b37d7d5a8</subscriptionId>
    <duration>P1M</duration>
    <throttling>PT5S</throttling>
  </subscribeResponse>
</subscribeContextResponse>
{
    "subscribeResponse": {
        "duration": "P1M",
        "subscriptionId": "51c0ac9ed714fb3b37d7d5a8",
        "throttling": "PT5S"
    }
}

Let's have a look now at accumulator-server.py. We will see one (and just one by the moment, no matter how much you wait) notifyContextRequest, similar to this one:

XML JSON (since release 0.9.0)
POST http://localhost:1028/accumulate
Content-Length: 739
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/xml

<notifyContextRequest>
  <subscriptionId>51c0ac9ed714fb3b37d7d5a8</subscriptionId>
  <originator>localhost</originator>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room1</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue>26.5</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</notifyContextRequest>
POST http://localhost:1028/accumulate
Content-Length: 492
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/json

{
  "subscriptionId" : "51c0ac9ed714fb3b37d7d5a8",
  "originator" : "localhost",
  "contextResponses" : [
    {
      "contextElement" : {
        "attributes" : [
          {
            "name" : "temperature",
            "type" : "float",
            "value" : "26.5"
          }
        ],
        "type" : "Room",
        "isPattern" : "false",
        "id" : "Room1"
      },
      "statusCode" : {
        "code" : "200",
        "reasonPhrase" : "OK"
      }
    }
  ]
}

You may wonder why accumulator-server.py is getting this message if you don't actually do any update. This is because the Orion Context Broker considers the transition from "non existing subscription" to "subscribed" as a change.

Release Note (any version): NGSI specification is not clear on if an initial notifyContextRequest has to be sent in this case or not. On one hand, some developers have told us that it might be useful to know the initial values before starting to receive notifications due to actual changes. On the other hand, an application can get the initial status using queryContext. Thus, this behavior could be changed in a later version. What's your opinion? :)

Now, do the following exercise, based on what you know from update context: Do the following 4 updates, in sequence and letting pass more than 5 seconds between one and the next (to avoid losing notifications due to throttling):

  • update Room1 temperature to 27: nothing happens, as temperature is not the triggering attribute
  • update Room1 pressure to 765: you will get a notification with the current value of Room1 temperature (27)
  • update Room1 pressure to 765: nothing happens, as the broker is clever enough to know that the previous value to the updateContext request was also 765 so no actual update have occurred and consequently no notification is sent.
  • update Room2 pressure to 740: nothing happens, as the subscription is for Room1, not Room2.

Next, try to check how throttling is enforced. Update Room1 pressure fast, without letting pass 5 seconds and you will see that the second notification doesn't arrive to accumulator-server.py.

You can update and cancel ONCHANGE subscriptions in the same way as ONTIMEINTERVAL subscriptions. You can do that as a final exercise in this section of the tutorial, e.g try to set a new throttling value, check that it works as expected and cancel after that.

Summary of NGSI10 standard operations URLs

Each standard operation has a unique URL. All of them use the POST method. The summary is below:

  • <host:port>/v1/updateContext
  • <host:port>/v1/queryContext
  • <host:port>/v1/subscribeContext
  • <host:port>/v1/updateContextSubscription
  • <host:port>/v1/unsubscribeContext

Tutorial on NGSI10 convenience operations

This section describes the different convenience operations described as part of the FIWARE NGSI REST API NGSI10 that Orion Context Broker supports, showing examples of requests and responses. Convenience operations are a set of operations that have been defined by FIWARE project to ease the usage of NGSI implementations as a complement to the standard operations defined in the OMA NGSI specification (see the section on additional information later in this manual).

Don't forget to restart the broker before starting this tutorial as described previously in this document.

At the end of this section, you will have learnt to use convenience operations as a handy alternative to some standard operations described in the previous section. It is highly recommended to do that tutorial before, to get familiar with update and query context, etc. and to be able to compare between the two approaches.

Convenience Entity Creation

Orion Context Broker will start in an empty state, so first of all we need to make it aware of the existence of certain entities. Thus, let's first create Room1 entity with temperature and pressure attributes (with its initial values)

XML JSON (since release 0.10.0)
(curl localhost:1026/v1/contextEntities/Room1 -s -S --header 'Content-Type: application/xml' -X POST -d @- | xmllint --format - ) << EOF
<?xml version="1.0" encoding="UTF-8"?>
<appendContextElementRequest>
  <contextAttributeList>
    <contextAttribute>
      <name>temperature</name>
      <type>float</type>
      <contextValue>23</contextValue>
    </contextAttribute>
    <contextAttribute>
      <name>pressure</name>
      <type>integer</type>
      <contextValue>720</contextValue>
    </contextAttribute>
  </contextAttributeList>
</appendContextElementRequest>
EOF
(curl localhost:1026/v1/contextEntities/Room1 -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -X POST -d @- | python -mjson.tool) <<EOF
{
  "attributes" : [
    {
      "name" : "temperature",
      "type" : "float",
      "value" : "23"
    },
    {
      "name" : "pressure",
      "type" : "integer",
      "value" : "720"
    }
  ]
}
EOF


the response is:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<appendContextElementResponse>
  <entityId type="" isPattern="false">
    <id>Room1</id>
  </entityId>
  <contextResponseList>
    <contextAttributeResponse>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue/>
        </contextAttribute>
        <contextAttribute>
          <name>pressure</name>
          <type>integer</type>
          <contextValue/>
        </contextAttribute>
      </contextAttributeList>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextAttributeResponse>
  </contextResponseList>
</appendContextElementResponse>
{
  "contextResponses": [
    {
      "attributes": [
        {
          "name": "temperature",
          "type": "float",
          "value": ""
        },
        {
          "name": "pressure",
          "type": "integer",
          "value": ""
        }
      ],
      "statusCode": {
        "code": "200",
        "reasonPhrase": "OK"
      }
    }
  ], 
  "id": "Room1", 
  "isPattern": "false", 
  "type": ""
}

Now, let's do the same with Room2:

XML JSON (since release 0.10.0)
(curl localhost:1026/v1/contextEntities/Room2 -s -S --header 'Content-Type: application/xml' -X POST -d @- | xmllint --format - ) << EOF
<?xml version="1.0" encoding="UTF-8"?>
<appendContextElementRequest>
  <contextAttributeList>
    <contextAttribute>
      <name>temperature</name>
      <type>float</type>
      <contextValue>21</contextValue>
    </contextAttribute>
    <contextAttribute>
      <name>pressure</name>
      <type>integer</type>
      <contextValue>711</contextValue>
    </contextAttribute>
  </contextAttributeList>
</appendContextElementRequest>
EOF
(curl localhost:1026/v1/contextEntities/Room2 -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -X POST -d @- | python -mjson.tool) <<EOF
{
  "attributes" : [
    {
      "name" : "temperature",
      "type" : "float",
      "value" : "21"
    },
    {
      "name" : "pressure",
      "type" : "integer",
      "value" : "711"
    }
  ]
}

EOF

which response is:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<appendContextElementResponse>
  <entityId type="" isPattern="false">
    <id>Room2</id>
  </entityId>
  <contextResponseList>
    <contextAttributeResponse>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue/>
        </contextAttribute>
        <contextAttribute>
          <name>pressure</name>
          <type>integer</type>
          <contextValue/>
        </contextAttribute>
      </contextAttributeList>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextAttributeResponse>
  </contextResponseList>
</appendContextElementResponse>
{
  "contextResponses": [
    {
      "attributes": [
        {
          "name": "temperature",
          "type": "float",
          "value": ""
        },
        {
          "name": "pressure",
          "type": "integer",
          "value": ""
        }
      ],
      "statusCode": {
        "code": "200",
        "reasonPhrase": "OK"
      }
    }
  ],
  "id": "Room2", 
  "isPattern": "false", 
  "type": ""
}

You can also create an attribute (and the containing entity along the way) in the following way (additional attributes could be added after that, as described in this section):

XML JSON (since release 0.10.0)
(curl localhost:1026/v1/contextEntities/Room3/attributes/temperature -s -S --header 'Content-Type: application/xml' -X POST -d @- | xmllint --format - ) << EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextAttributeRequest>
   <contextValue>21</contextValue>
</updateContextAttributeRequest>
EOF
(curl localhost:1026/v1/contextEntities/Room3/attributes/temperature -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -X POST -d @- | python -mjson.tool) <<EOF
{
   "value" : "21"
}
EOF

Compared to entity creation based on standard operation we observe the following differences:

  • We are using the POST verb on the /v1/contextEntities/{EntityID} resource to create new entities
  • We cannot create more than one entity at a time using convenience operation requests.
  • The payload of requests and responses in convenience operations are very similar to the ones used in standard operations, since contextAttributeList and contextResponseList elements are the same.
  • Before 0.16.0 entity type couldn't be defined. Thus, we cannot specify whether "Room1" is of type "Room" or "Space". In some sense, the created entity has a "null" type. This lack of typing has some important implications. However, since 0.16.0 you can replace "Room1" by "/type/Room/id/Room1" in the URl to define the type (in general: "/type/<type>/id/<id>").

Since 0.17.0: as alterative, you can use "POST /v1/contextEntitites" to create entities. In this case, the entity information (ID and type) is included in the payload and the URL is independent of that fields, as shown below:

(curl localhost:1026/v1/contextEntities -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -X POST -d @- | python -mjson.tool) <<EOF
{
  "id": "Room1",
  "type": "Room",
  "attributes" : [
    {
      "name" : "temperature",
      "type" : "float",
      "value" : "23"
    },
    {
      "name" : "pressure",
      "type" : "integer",
      "value" : "720"
    }
  ]
}
EOF

Apart from simple values (i.e. strings) for attribute values, you can also use complex structures or custom metadata. These are advance topics, described in this section and this other, respectively.

Convenience Query Context

Finally, let's describe convenience operations for querying context information. We can query all the attribute values of a given entity, e.g. Room1 attributes:

XML JSON (since release 0.10.0)
curl localhost:1026/v1/contextEntities/Room1 -s -S | xmllint --format -
curl localhost:1026/v1/contextEntities/Room1 -s -S --header 'Accept: application/json' | python -mjson.tool

which response is:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<contextElementResponse>
  <contextElement>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
    <contextAttributeList>
      <contextAttribute>
        <name>temperature</name>
        <type>float</type>
        <contextValue>23</contextValue>
      </contextAttribute>
      <contextAttribute>
        <name>pressure</name>
        <type>integer</type>
        <contextValue>720</contextValue>
      </contextAttribute>
    </contextAttributeList>
  </contextElement>
  <statusCode>
    <code>200</code>
    <reasonPhrase>OK</reasonPhrase>
  </statusCode>
</contextElementResponse>
{
  "contextElement": {
    "attributes": [
    {
      "name": "temperature",
      "type": "float",
      "value": "23"
    },
    {
      "name": "pressure",
      "type": "integer",
      "value": "720"
    }
    ],
    "id": "Room1",
    "isPattern": "false",
    "type": "Room"
  },
  "statusCode": {
    "code": "200",
    "reasonPhrase": "OK"
  }
}

We can also query a single attribute of a given entity, e.g. Room2 temperature:

XML JSON (since release 0.10.0)
curl localhost:1026/v1/contextEntities/Room2/attributes/temperature -s -S | xmllint --format -
curl localhost:1026/v1/contextEntities/Room2/attributes/temperature -s -S --header 'Accept: application/json' | python -mjson.tool

which response is:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<contextAttributeResponse>
  <contextAttributeList>
    <contextAttribute>
      <name>temperature</name>
      <type>float</type>
      <contextValue>21</contextValue>
    </contextAttribute>
  </contextAttributeList>
  <statusCode>
    <code>200</code>
    <reasonPhrase>OK</reasonPhrase>
  </statusCode>
</contextAttributeResponse>
{
  "attributes": [
  {
    "name": "temperature",
    "type": "float",
    "value": "21"
  }
  ],
  "statusCode": {
    "code": "200",
    "reasonPhrase": "OK"
  }
}

Comparing to standard queryContext operation we observe the following differences:

  • Convenience operations use the GET method without payload in the request (simpler than standard operation)
  • The response contextElementResponse element used in the response of the convenience operation to query all the attributes of an entity has the same structure as the one that appears inside the responses for standard queryContext. However, the contextAttributeResponse element in the response of the convenience operation used as response to the query of a single attribute of an entity is new.
  • Before 0.16.0 entity type couldn't be defined. Thus, we cannot specify whether "Room1" is of type "Room" or "Space". In some sense, we are querying for "null" entity type. This lack of typing has some important implications. However, since 0.16.0 you can replace "Room1" by "/type/Room/id/Room1" in the URl to define the type (in general: "/type/<type>/id/<id>").

Since release 0.9.0: you can also query by all the entities belonging to the same type, either all the attributes or a particular one, as shown below. First, create an couple of entities of type Car using standard updateContext APPEND operations (given that, as described in previous section, you cannot create entities with types using convenience operations):

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Car" isPattern="false">
        <id>Car1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>speed</name>
          <type>integer</type>
          <contextValue>75</contextValue>
        </contextAttribute>
        <contextAttribute>
          <name>fuel</name>
          <type>float</type>
          <contextValue>12.5</contextValue>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>APPEND</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
    {
      "type": "Car",
      "isPattern": "false",
      "id": "Car1",
      "attributes": [
      {
        "name": "speed",
        "type": "integer",
        "value": "75"
      },
      {
        "name": "fuel",
        "type": "float",
        "value": "12.5"
      }
      ]
    }
  ],
  "updateAction": "APPEND"
}
EOF
XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Car" isPattern="false">
        <id>Car2</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>speed</name>
          <type>integer</type>
          <contextValue>90</contextValue>
        </contextAttribute>
        <contextAttribute>
          <name>fuel</name>
          <type>float</type>
          <contextValue>25.7</contextValue>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>APPEND</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
    {
      "type": "Car",
      "isPattern": "false",
      "id": "Car2",
      "attributes": [
      {
        "name": "speed",
        "type": "integer",
        "value": "90"
      },
      {
        "name": "fuel",
        "type": "float",
        "value": "25.7"
      }
      ]
    }
  ],
  "updateAction": "APPEND"
}
EOF

Request to get all the attributes:

XML JSON (since release 0.10.0)
curl localhost:1026/v1/contextEntityTypes/Car -s -S | xmllint --format -
curl localhost:1026/v1/contextEntityTypes/Car -s -S --header 'Accept: application/json' | python -mjson.tool

Response:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<?xml version="1.0"?>
<queryContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Car" isPattern="false">
          <id>Car1</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>speed</name>
            <type>integer</type>
            <contextValue>75</contextValue>
          </contextAttribute>
          <contextAttribute>
            <name>fuel</name>
            <type>float</type>
            <contextValue>12.5</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
    <contextElementResponse>
      <contextElement>
        <entityId type="Car" isPattern="false">
          <id>Car2</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>speed</name>
            <type>integer</type>
            <contextValue>90</contextValue>
          </contextAttribute>
          <contextAttribute>
            <name>fuel</name>
            <type>float</type>
            <contextValue>25.7</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</queryContextResponse>
{
  "contextResponses": [
  {
    "contextElement": {
      "attributes": [
      {
        "name": "speed",
        "type": "integer",
        "value": "75"
      },
      {
        "name": "fuel",
        "type": "float",
        "value": "12.5"
      }
      ],
      "id": "Car1",
      "isPattern": "false",
      "type": "Car"
    },
    "statusCode": {
      "code": "200",
      "reasonPhrase": "OK"
    }
  },
  {
    "contextElement": {
      "attributes": [
      {
        "name": "speed",
        "type": "integer",
        "value": "90"
      },
      {
        "name": "fuel",
        "type": "float",
        "value": "25.7"
      }
      ],
      "id": "Car2",
      "isPattern": "false",
      "type": "Car"
    },
    "statusCode": {
      "code": "200",
      "reasonPhrase": "OK"
    }
  }
  ]
}

Request to get only one attribute (e.g. speed):

XML JSON (since release 0.10.0)
curl localhost:1026/v1/contextEntityTypes/Car/attributes/speed -s -S | xmllint --format -
curl localhost:1026/v1/contextEntityTypes/Car/attributes/speed -s -S --header 'Accept: application/json' | python -mjson.tool

Response:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<queryContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Car" isPattern="false">
          <id>Car1</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>speed</name>
            <type>integer</type>
            <contextValue>75</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
    <contextElementResponse>
      <contextElement>
        <entityId type="Car" isPattern="false">
          <id>Car2</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>speed</name>
            <type>integer</type>
            <contextValue>90</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</queryContextResponse>
{
  "contextResponses": [
  {
    "contextElement": {
      "attributes": [
      {
        "name": "speed",
        "type": "integer",
        "value": "75"
      }
      ],
      "id": "Car1",
      "isPattern": "false",
      "type": "Car"
    },
    "statusCode": {
      "code": "200",
      "reasonPhrase": "OK"
    }
  },
  {
    "contextElement": {
      "attributes": [
      {
        "name": "speed",
        "type": "integer",
        "value": "90"
      }
      ],
      "id": "Car2",
      "isPattern": "false",
      "type": "Car"
    },
    "statusCode": {
      "code": "200",
      "reasonPhrase": "OK"
    }
  }
  ]
}

Additional comments:

  • You can also use geographical scopes in your queries. This is an advanced topic, described in this section.
  • Since 0.15.0: In the case of JSON responses, you can use the ?attributeFormat=object URI parameter to get attributes as a JSON object (i.e. key-values map) instead of a vector (default behaviour):
curl localhost:1026/v1/contextEntities/Room1?attributeFormat=object -s -S --header 'Accept: application/json' | python -mjson.tool
{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": {
                    "pressure": {
                        "type": "integer",
                        "value": "720"
                    },
                    "temperature": {
                        "type": "float",
                        "value": "23"
                    }
                },
                "id": "Room1",
                "isPattern": "false",
                "type": "Room"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

Getting all entities

Since 0.16.0:

You can get all the entities using the following convenience operation:

curl localhost:1026/v1/contextEntities -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool

In our case, it will return both Room1 and Room2:

{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": "23"
                    },
                    {
                        "name": "pressure",
                        "type": "integer",
                        "value": "720"
                    }
                ],
                "id": "Room1",
                "isPattern": "false",
                "type": ""
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        },
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": "21"
                    },
                    {
                        "name": "pressure",
                        "type": "integer",
                        "value": "711"
                    }
                ],
                "id": "Room2",
                "isPattern": "false",
                "type": ""
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

Additional comments:

  • Getting all the entities stored in Orion isn't a really good idea (except if you have a limited number of entities). Have a look at the section on filters.
  • Note that by default, only 20 entities are returned (which is fine for this tutorial, but probably not for a real utilization scenario). In order to change this behaviour, see the section on pagination in this manual.
  • In the case of JSON responses, you can use the ?attributeFormat=object URI parameter to get attributes as a JSON object (i.e. key-values map) instead of a vector (default behaviour), as described in the previous section.

Convenience Update Context

Let's set the Room1 temperature and pressure values:

XML JSON (since release 0.10.0)
(curl localhost:1026/v1/contextEntities/Room1/attributes -s -S --header 'Content-Type: application/xml' -X PUT -d @- | xmllint --format - ) << EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextElementRequest>
  <contextAttributeList>
    <contextAttribute>
      <name>temperature</name>
      <type>float</type>
      <contextValue>26.5</contextValue>
    </contextAttribute>
    <contextAttribute>
      <name>pressure</name>
      <type>integer</name>
      <contextValue>763</contextValue>
    </contextAttribute>
  </contextAttributeList>
</updateContextElementRequest>
EOF
(curl localhost:1026/v1/contextEntities/Room1/attributes -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -X PUT -d @- | python -mjson.tool) << EOF
{
  "attributes" : [
  {
    "name" : "temperature",
    "type" : "float",
    "value" : "26.5"
  },
  {
    "name" : "pressure",
    "type" : "integer",
    "value" : "763"
  }
  ]
}
EOF

the response is:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<updateContextElementResponse>
  <contextResponseList>
    <contextAttributeResponse>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue/>
        </contextAttribute>
        <contextAttribute>
          <name>pressure</name>
          <type>integer</type>
          <contextValue/>
        </contextAttribute>
      </contextAttributeList>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextAttributeResponse>
  </contextResponseList>
</updateContextElementResponse>
{
  "contextResponses": [
  {
    "attributes": [
    {
      "name": "temperature",
      "type": "float",
      "value": ""
    },
    {
      "name": "pressure",
      "type": "integer",
      "value": ""
    }
    ],
    "statusCode": {
      "code": "200",
      "reasonPhrase": "OK"
    }
  }
  ]
}

Now, let's do the same with Room2:

XML JSON (since release 0.10.0)
(curl localhost:1026/v1/contextEntities/Room2/attributes -s -S --header 'Content-Type: application/xml' -X PUT -d @- | xmllint --format - ) << EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextElementRequest>
  <contextAttributeList>
    <contextAttribute>
      <name>temperature</name>
      <type>float</type>
      <contextValue>27.4</contextValue>
    </contextAttribute>
    <contextAttribute>
      <name>pressure</name>
      <type>integer</type>
      <contextValue>755</contextValue>
    </contextAttribute>
  </contextAttributeList>
</updateContextElementRequest>
EOF
(curl localhost:1026/v1/contextEntities/Room2/attributes -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -X PUT -d @- | python -mjson.tool) << EOF
{
  "attributes" : [
  {
    "name" : "temperature",
    "type" : "float",
    "value" : "27.4"
  },
  {
    "name" : "pressure",
    "type" : "integer",
    "value" : "755"
  }
  ]
}
EOF

which response is:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<updateContextElementResponse>
  <contextResponseList>
    <contextAttributeResponse>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue/>
        </contextAttribute>
        <contextAttribute>
          <name>pressure</name>
          <type>integer</type>
          <contextValue/>
        </contextAttribute>
      </contextAttributeList>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextAttributeResponse>
  </contextResponseList>
</updateContextElementResponse>
{
  "contextResponses": [
  {
    "attributes": [
    {
      "name": "temperature",
      "type": "float",
      "value": ""
    },
    {
      "name": "pressure",
      "type": "integer",
      "value": ""
    }
    ],
    "statusCode": {
      "code": "200",
      "reasonPhrase": "OK"
    }
  }
  ]
}

You can update a single attribute of a given entity (previous to release 0.11.0 you can only do it that attribute uses metadata ID) in the following way:

XML JSON (since release 0.10.0)
(curl localhost:1026/v1/contextEntities/Room2/attributes/temperature -s -S --header 'Content-Type: application/xml' -X PUT -d @- | xmllint --format - ) << EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextAttributeRequest>
   <contextValue>26.3</contextValue>
</updateContextAttributeRequest>
EOF
(curl localhost:1026/v1/contextEntities/Room2/attributes/temperature -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -X PUT -d @- | python -mjson.tool) <<EOF
{
   "value" : "26.3"
}
EOF

Comparing to standard updateContext operation we observe the following differences:

  • We cannot update more than one entity at a time using convenience operation requests.
  • The payload of request and response in convenience operations are very similar to the ones used in standard operations, the contextAttributeList and contextResponseList elements are the same.
  • Before 0.16.0 entity type couldn't be defined. Thus, we cannot specify whether "Room1" is of type "Room" or "Space". In some sense, the updated entity has a "null" type. This lack of typing has some important implications. However, since 0.16.0 you can replace "Room1" by "/type/Room/id/Room1" in the URl to define the type (in general: "/type/<type>/id/<id>").


Apart from simple values (i.e. strings) for attribute values, you can also use complex structures or custom metadata. These are advance topics, described in this section and this other, respectively.

Convenience operations for context subscriptions

Since release 0.9.0: new functionality.

You can use the following convenience operations to manage context subscriptions:

  • POST /v1/contextSubscriptions, to create the subscription, using the same payload as standard susbcribeContext operation.
  • PUT /v1/contextSubscriptions/{subscriptionID}, to update the subscription identified by {subscriptionID}, using the same payload as standard updateContextSubscription operation. The ID in the payload must match the ID in the URL.
  • DELETE /v1/contextSubscriptions/{subscriptionID}, to cancel the subscription identified by {subscriptionID}. In this case, payload is not used

Summary of NGSI10 convenience operations URLs

Convenience operations use a URL to identify the resource and a HTTP verb to identify the operation on that resource following the usual REST convention: GET is used to retrieve information, POST is used to create new information, PUT is used to update information and DELETE is used to destroy information.

You find a summary in the following document.

Context availability management using NGSI9

Tutorial on NGSI9 standard operations

This section describes the different standard NGSI9 operations that the Orion Context Broker supports, showing examples of requests and responses. We use the term "standard" as they are directly derived from the OMA NGSI specification, to distinguish them from the other family of operations ("convenience") which has been defined by the FIWARE project to ease the usage of NGSI implementations (see the section on additional information later in this manual).

Don't forget to restart the broker before starting this tutorial as described previously in this document.

At the end of this section, you will have the basic knowledge to create applications (both context producers and consumers) using Orion Context Broker with NGSI9 standard operations:

  • registerContext
  • discoverContextAvailability
  • subscribeContextAvailability
  • updateContextAvailabilitySubscription
  • unsubscribeContextAvailability

Register Context operation

First of all you have to register Room1 and Room2. In order to do so, we use the following NGSI9 registerContext operation:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<registerContextRequest>
  <contextRegistrationList>
    <contextRegistration>
      <entityIdList>
        <entityId type="Room" isPattern="false">
          <id>Room1</id>
        </entityId>
        <entityId type="Room" isPattern="false">
          <id>Room2</id>
        </entityId>
      </entityIdList>
      <contextRegistrationAttributeList>
        <contextRegistrationAttribute>
          <name>temperature</name>
          <type>float</type>
          <isDomain>false</isDomain>
        </contextRegistrationAttribute>
        <contextRegistrationAttribute>
          <name>pressure</name>
          <type>integer</type>
          <isDomain>false</isDomain>
        </contextRegistrationAttribute>
      </contextRegistrationAttributeList>
      <providingApplication>http://mysensors.com/Rooms</providingApplication>
  </contextRegistration>
  </contextRegistrationList>
  <duration>P1M</duration>
</registerContextRequest>
EOF
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "contextRegistrations": [
        {
            "entities": [
                {
                    "type": "Room",
                    "isPattern": "false",
                    "id": "Room1"
                },
                {
                    "type": "Room",
                    "isPattern": "false",
                    "id": "Room2"
                }
            ],
            "attributes": [
                {
                    "name": "temperature",
                    "type": "float",
                    "isDomain": "false"
                },
                {
                    "name": "pressure",
                    "type": "integer",
                    "isDomain": "false"
                }
            ],
            "providingApplication": "http://mysensors.com/Rooms"
        }
    ],
    "duration": "P1M"
}
EOF

The payload includes a list of contextRegistration elements, each one with the following information:

  • A list of entities to be registered. In our case, they are the Room1 and Room2 entities. For each entity we specify a type (in this case, we are using "Room" as type) and an ID (which are "Room1" and "Room2" respectively). The isPattern field is not actually used in registerContext, so it always has a value of "false".
  • A list of attributes to register for the entities. In our case, they are the temperature and pressure attributes. For each one, we define a name, a type and whether it is a domain attribute or not.
    • Release Note (any version): Orion Context Broker doesn't perform any checking on types (e.g. it doesn't check that when a context producer application updates the value of the temperature, this value is formatted as a float like "25.5" or "-40.23" and not something like "hot"). In addition, domain attributes are not supported, so isDomain must always be set to "false".
  • The URL of the providing application. By "providing application" (or Context Provider) we mean the URL that represents the actual context information for the entities and attributes being registered. In our example we are assuming that all the sensors are provided by http://mysensors.com/Rooms (of course, this is a fake URL :). More information on providing application later in this manual.

Note that in this case we are registering both rooms using just one contextRegistration element, but we could also have used two contextRegistrations, each one for a different Room. This would typically be the case in which both rooms have different providing applications (e.g. http://mysensors.com/Rooms1 and http://mysensors.com/Rooms2). Moreover, we would use four different contextRegistrations in case each sensor were associated to different providing applications (e.g. http://mysensors.com/Rooms1/temperature, http://mysensors.com/Rooms1/pressure, http://mysensors.com/Rooms2/temperature and http://mysensors.com/Rooms2/pressure).

Finally, note that the payload includes a duration element. The duration element sets the duration of the registration so after that time has passed it can be considered as expired (however, duration can be extended). We use the ISO 8601 standard for duration format. We are using "P1M" which means "one month" (a very large amount, probably enough time to complete this tutorial :).

We will get the following response (XML case):

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<registerContextResponse>
  <duration>P1M</duration>
  <registrationId>52a744b011f5816465943d58</registrationId>
</registerContextResponse>
{
  "duration" : "P1M",
  "registrationId" : "52a744b011f5816465943d58"
}

The registrationId (whose value will be different when you run the request, as it is generated using the timestamp of the current time :) is a 24 hexadecimal digit which provides an unique reference to the registration. It is used for updating the registration as explained later in this manual.

Discover Context Availability operation

So now the broker has registration information about Room1 and Room2. How can we access that information? Using the NGSI9 discoverContextAvailability operation. For example, we can discover registrations for Room1 using:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<discoverContextAvailabilityRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <attributeList/>
</discoverContextAvailabilityRequest>
EOF
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room1"
        }
    ]
}
EOF

This would produce the following response:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Room" isPattern="false">
            <id>Room1</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>temperature</name>
            <type>float</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
          <contextRegistrationAttribute>
            <name>pressure</name>
            <type>integer</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Rooms</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>
{
    "contextRegistrationResponses": [
        {
            "contextRegistration": {
                "attributes": [
                    {
                        "isDomain": "false",
                        "name": "temperature",
                        "type": "float"
                    },
                    {
                        "isDomain": "false",
                        "name": "pressure",
                        "type": "integer"
                    }
                ],
                "entities": [
                    {
                        "id": "Room1",
                        "isPattern": "false",
                        "type": "Room"
                    }
                ],
                "providingApplication": "http://mysensors.com/Rooms"
            }
        }
    ]
}

Note that we used an empty attributeList in the request ('attributes' for short, in JSON). Doing so, the discover searches for Room1, no matter which attributes have been registered. If we want to be more precise, we can include the name of an attribute to search for:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
  <discoverContextAvailabilityRequest>
    <entityIdList>
      <entityId type="Room" isPattern="false">
        <id>Room1</id>
      </entityId>
    </entityIdList>
    <attributeList>
      <attribute>temperature</attribute>
    </attributeList>
  </discoverContextAvailabilityRequest>
EOF
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room1"
        }
    ],
    "attributes": [
        "temperature"
    ]
}
EOF

which produces the following response:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Room" isPattern="false">
            <id>Room1</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>temperature</name>
            <type>float</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Rooms</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>
{
    "contextRegistrationResponses": [
        {
            "contextRegistration": {
                "attributes": [
                    {
                        "isDomain": "false",
                        "name": "temperature",
                        "type": "float"
                    }
                ],
                "entities": [
                    {
                        "id": "Room1",
                        "isPattern": "false",
                        "type": "Room"
                    }
                ],
                "providingApplication": "http://mysensors.com/Rooms"
            }
        }
    ]
}

If the broker doesn't have any registration information, it will return a response telling so. Thus, the following request:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
  <discoverContextAvailabilityRequest>
    <entityIdList>
      <entityId type="Room" isPattern="false">
        <id>Room1</id>
      </entityId>
    </entityIdList>
    <attributeList>
      <attribute>humidity</attribute>
    </attributeList>
  </discoverContextAvailabilityRequest>
EOF
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
    {
        "type": "Room",
        "isPattern": "false",
        "id": "Room1"
    }
    ],
    "attributes": [
        "humidity"
    ]
}
EOF

would produce the following response:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <errorCode>
    <code>404</code>
    <reasonPhrase>No context element registrations found</reasonPhrase>
  </errorCode>
</discoverContextAvailabilityResponse>
{
    "errorCode": {
        "code": "404",
        "reasonPhrase": "No context element registrations found"
    }
}

You can also search for a list of entities, e.g. to discover temperature in both Room1 and Room2:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
  <discoverContextAvailabilityRequest>
    <entityIdList>
      <entityId type="Room" isPattern="false">
        <id>Room1</id>
      </entityId>
      <entityId type="Room" isPattern="false">
        <id>Room2</id>
      </entityId>
    </entityIdList>
    <attributeList>
      <attribute>temperature</attribute>
    </attributeList>
  </discoverContextAvailabilityRequest>
EOF
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
    {
        "type": "Room",
        "isPattern": "false",
        "id": "Room1"
    },
    {
        "type": "Room",
        "isPattern": "false",
        "id": "Room2"
    }
    ],
    "attributes": [
    "temperature"
    ]
}
EOF

which will produce the following response:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Room" isPattern="false">
            <id>Room1</id>
          </entityId>
          <entityId type="Room" isPattern="false">
            <id>Room2</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>temperature</name>
            <type>float</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Rooms</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>
{
    "contextRegistrationResponses": [
        {
            "contextRegistration": {
                "attributes": [
                    {
                        "isDomain": "false",
                        "name": "temperature",
                        "type": "float"
                    }
                ],
                "entities": [
                    {
                        "id": "Room1",
                        "isPattern": "false",
                        "type": "Room"
                    },
                    {
                        "id": "Room2",
                        "isPattern": "false",
                        "type": "Room"
                    }
                ],
                "providingApplication": "http://mysensors.com/Rooms"
            }
        }
    ]
}

Finally, a powerful feature of Orion Context Broker is that you can use a regular expression for the entity ID. For example, you can discover entities whose ID starts with "Room" using the regex "Room.*". In this case, you have to set isPattern to "true" as shown below:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
  <discoverContextAvailabilityRequest>
    <entityIdList>
      <entityId type="Room" isPattern="true">
        <id>Room.*</id>
      </entityId>
    </entityIdList>
    <attributeList>
      <attribute>temperature</attribute>
    </attributeList>
  </discoverContextAvailabilityRequest>
EOF
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
    {
        "type": "Room",
        "isPattern": "true",
        "id": "Room.*"
    }
    ],
    "attributes": [
    "temperature"
    ]
}
EOF

This will produce the exact same response as the previous example.

Note that by default only 20 registrations are returned (which is fine for this tutorial, but probably not for a real utilization scenario). In order to change this behaviour, see the section on pagination in this manual.

Context availability subscriptions

The NGSI9 operations you know up to now (registerContext and discoverContextAvailability) are the basic building blocks for synchronous context producer and context consumer applications. However, Orion Context Broker has another powerful feature that you can take advantage of: the ability to context information availability so when "something" happens (we will explain the different cases for that "something") your application will get an asynchronous notification. In that way, you don't need to continuously repeat discoverContextAvailability requests (i.e. polling), the Orion Context Broker will let you know the information when it comes.

We assume that the accumulator-server.py program is still running. Otherwise, start it as described in the previous section.

Context availability subscriptions are used when we want to be notified not about context information (i.e. the values of attributes of some entities) but about the availability of the context sources themselves. We will clarify what this means with an example.

Let's consider that your context consumer application wants to be notified each time the Orion Context Broker gets aware of a new Room registration, e.g. because a new Room icon has to be drawn in the graphical user interface that the application is offering to final users. Thus, each time a new entity of type "Room" is registered in the broker (using registerContext operation), the broker must be able to send notifications.

In order to configure this behavior, we use the following NGSI9 subscribeContextAvailability request:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/subscribeContextAvailability -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0"?>
<subscribeContextAvailabilityRequest>
  <entityIdList>
    <entityId type="Room" isPattern="true">
      <id>.*</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>temperature</attribute>
  </attributeList>
  <reference>http://localhost:1028/accumulate</reference>
  <duration>P1M</duration>
</subscribeContextAvailabilityRequest>
EOF
(curl localhost:1026/v1/registry/subscribeContextAvailability -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
    {
        "type": "Room",
        "isPattern": "true",
        "id": ".*"
    }
    ],
    "attributes": [
    "temperature"
    ],
    "reference": "http://localhost:1028/accumulate",
    "duration": "P1M"
}
EOF

The payload has the following elements:

  • entityIdList and attributeList ('entities' and 'attributes' for short, in JSON) define which context availability information we are interested in. They are used to select the context registrations to include in the notifications. They work in the same way as the XML elements with the same name in discoverContextAvailability request. In this case, we are stating that we are interested in context availability about "temperature" attribute in any entity of type "Room" ("any" is represented by the ".*" pattern, which is a regular expression that matches any string).
  • The callback URL to send notifications is defined with the reference element. We are using the URL of the accumulator-server.py program started before. Only one reference can be included per subscribeContextAvailability request. However, you can have several subscriptions on the same context availability elements (i.e. same entityIdList and attributeList) without any problem. Default URL schema (in the case you don't specify any) is "http", e.g. using "localhost:1028" as reference will be actually interpreted as "http://localhost:1028".
  • Subscriptions have a duration (specified in the duration elements in the same format as registerContext request). Once that duration expires, the subscription is ignored (however, it is still stored in the broker database and needs to be purged using the procedure described in the administration manual). You can extend the duration of a subscription by updating it, as described later in this document. We are using "P1M" which means "one month".

As you can see, the structure of subscriptionContextAvailability is similar to the structure of NGSI10 subscribeContext, although in this case we don't use notifyConditions nor throttling.

The response to the subscribeContextAvailability request is a subscription ID (a 24 hexadecimal number used for updating and cancelling the subscription - write it down because you will need it in later steps of this tutorial) and a duration acknowledgement. Again, pretty similar to a subscribeContext.

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<subscribeContextAvailabilityResponse>
  <subscriptionId>52a745e011f5816465943d59</subscriptionId>
  <duration>P1M</duration>
</subscribeContextAvailabilityResponse>
{
    "duration": "P1M",
    "subscriptionId": "52a745e011f5816465943d59"
}

Looking at accumulator-server.py, we will see the following initial notification:

XML JSON (since release 0.9.0)
POST http://localhost:1028/accumulate
Content-Length: 940
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/xml

<notifyContextAvailabilityRequest>
  <subscriptionId>52a745e011f5816465943d59</subscriptionId>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Room" isPattern="false">
            <id>Room1</id>
          </entityId>
          <entityId type="Room" isPattern="false">
            <id>Room2</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>temperature</name>
            <type>float</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Rooms</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</notifyContextAvailabilityRequest>
POST http://localhost:1028/accumulate
Content-Length: 638
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/json

{
  "subscriptionId" : "52a745e011f5816465943d59",
  "contextRegistrationResponses" : [
    {
      "contextRegistration" : {
        "entities" : [
          {
            "type" : "Room",
            "isPattern" : "false",
            "id" : "Room1"
          },
          {
            "type" : "Room",
            "isPattern" : "false",
            "id" : "Room2"
          }
        ],
        "attributes" : [
          {
            "name" : "temperature",
            "type" : "float",
            "isDomain" : "false"
          }
        ],
        "providingApplication" : "http://mysensors.com/Rooms"
      }
    }
  ]
}

Orion Context Broker notifies NGSI9 subscribeContextAvailability using the POST HTTP method (on the URL used as reference for the subscription) with a notifyContextAvailabilityRequest payload. Apart from the subscriptionId element (that matches the one in the response to subscribeContextAvailability request) and the originator element, the contextResponseList element is the same than the one used in the discoverContextAvailability responses.

Release Note (any version): currently, the originator is always "localhost". We will look into a more flexible way of using this in a later version.

The initial notification includes all the currently registered entities that match the entityIdList/attributeList used in subscribeContextAvailability request. That is, the registration corresponding to Room1 and Room2 temperature. Note that, although Room1 and Room2 registered two attributes (temperature and pressure) only temperature is shown, as the attributeList in subscribeContextAvailability only includes temperature.

Release Note (any version): The NGSI specification is not clear on if an initial notifyContextAvailabilityRequest has to be sent in this case or not. On one hand, some developers have told us that it might be useful to know the initial registrations before starting to receive notifications due to new registrations. On the other hand, an application can get the initial status using discoverContextAvailability. Thus, this behavior could be changed in a later version. What is your opinion? :)

Let's see what happens when we register a new room (Room3) with temperature and pressure:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
    <registerContextRequest>
      <contextRegistrationList>
        <contextRegistration>
          <entityIdList>
            <entityId type="Room" isPattern="false">
              <id>Room3</id>
            </entityId>
          </entityIdList>
          <contextRegistrationAttributeList>
            <contextRegistrationAttribute>
              <name>temperature</name>
              <type>float</type>
              <isDomain>false</isDomain>
            </contextRegistrationAttribute>
            <contextRegistrationAttribute>
              <name>pressure</name>
              <type>integer</type>
              <isDomain>false</isDomain>
            </contextRegistrationAttribute>
          </contextRegistrationAttributeList>
          <providingApplication>http://mysensors.com/Rooms</providingApplication>
        </contextRegistration>
      </contextRegistrationList>
      <duration>P1M</duration>
    </registerContextRequest>
EOF
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "contextRegistrations": [
    {
        "entities": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room3"
        }
        ],
        "attributes": [
        {
            "name": "temperature",
            "type": "float",
            "isDomain": "false"
        },
        {
            "name": "pressure",
            "type": "integer",
            "isDomain": "false"
        }
        ],
        "providingApplication": "http://mysensors.com/Rooms"
    }
    ],
    "duration": "P1M"
}
EOF

As expected, the accumulator-server.py will be notified of the new registration. Again, although Room3 registration includes temperature and pressure, only the first attribute is included in the notification.

XML JSON (since release 0.9.0)
POST http://localhost:1028/accumulate
Content-Length: 840
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/xml

<notifyContextAvailabilityRequest>
  <subscriptionId>52a745e011f5816465943d59</subscriptionId>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Room" isPattern="false">
            <id>Room3</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>temperature</name>
            <type>float</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Rooms</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</notifyContextAvailabilityRequest>
POST http://localhost:1028/accumulate
Content-Length: 522
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/json

{
  "subscriptionId" : "52a745e011f5816465943d59",
  "contextRegistrationResponses" : [
    {
      "contextRegistration" : {
        "entities" : [
          {
            "type" : "Room",
            "isPattern" : "false",
            "id" : "Room3"
          }
        ],
        "attributes" : [
          {
            "name" : "temperature",
            "type" : "float",
            "isDomain" : "false"
          }
        ],
        "providingApplication" : "http://mysensors.com/Rooms"
      }
    }
  ]
}

We can also check that context registrations not matching the subscription doesn't trigger any notifications. For example, let's register a room (Room4) with only attribute pressure (remember that the subscription only includes temperature in attributeList).

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
    <registerContextRequest>
      <contextRegistrationList>
        <contextRegistration>
          <entityIdList>
            <entityId type="Room" isPattern="false">
              <id>Room4</id>
            </entityId>
          </entityIdList>
          <contextRegistrationAttributeList>
            <contextRegistrationAttribute>
              <name>pressure</name>
              <type>integer</type>
              <isDomain>false</isDomain>
            </contextRegistrationAttribute>
          </contextRegistrationAttributeList>
          <providingApplication>http://mysensors.com/Rooms</providingApplication>
        </contextRegistration>
      </contextRegistrationList>
      <duration>P1M</duration>
    </registerContextRequest>
EOF
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "contextRegistrations": [
    {
        "entities": [
        {
            "type": "Room",
            "isPattern": "false",
            "id": "Room4"
        }
        ],
        "attributes": [
        {
            "name": "pressure",
            "type": "integer",
            "isDomain": "false"
        }
        ],
        "providingApplication": "http://mysensors.com/Rooms"
    }
    ],
    "duration": "P1M"
}
EOF

You can now check that no new notification arrives to accumulator-server.py.

As with context subscriptions, context availability subscriptions can be updated (using the NGSI9 updateContextAvailabilitySubscription). The request includes the subscriptionId that identifies the subscription to modify, and the actual update payload. For example, let's change subscription entities to something completely different: cars instead of rooms and all the attributes are removed (i.e. an empty attributeList element). As always you have to replace the subscriptionId value after copy-pasting with the value you got from the subscribeContextAvailability response in the previous step).

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/updateContextAvailabilitySubscription -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<updateContextAvailabilitySubscriptionRequest>
  <entityIdList>
    <entityId type="Car" isPattern="true">
      <id>.*</id>
    </entityId>
  </entityIdList>
  <attributeList/>
  <duration>P1M</duration>
  <subscriptionId>52a745e011f5816465943d59</subscriptionId>
</updateContextAvailabilitySubscriptionRequest>
EOF
(curl localhost:1026/v1/registry/updateContextAvailabilitySubscription -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
    {
        "type": "Car",
        "isPattern": "true",
        "id": ".*"
    }
    ],
    "duration": "P1M",
    "subscriptionId": "52a745e011f5816465943d59"
}
EOF

The response is very similar to the one for subscribeContextAvailability request:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<updateContextAvailabilitySubscriptionResponse>
  <subscriptionId>52a745e011f5816465943d59</subscriptionId>
  <duration>P1M</duration>
</updateContextAvailabilitySubscriptionResponse>
{
    "duration": "P1M",
    "subscriptionId": "52a745e011f5816465943d59"
}

Given that there are currently no car entities registered, you will not receive any initial notification. So. let's register two cars: Car1 with an attribute named speed and Car2 with an attribute named location (don't worry about the ISO6709 reference in the XML, it's just standard for geo-location and nothing significant for this tutorial).

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
    <registerContextRequest>
      <contextRegistrationList>
        <contextRegistration>
          <entityIdList>
            <entityId type="Car" isPattern="false">
              <id>Car1</id>
            </entityId>
          </entityIdList>
          <contextRegistrationAttributeList>
            <contextRegistrationAttribute>
              <name>speed</name>
              <type>integer</type>
              <isDomain>false</isDomain>
            </contextRegistrationAttribute>
          </contextRegistrationAttributeList>
          <providingApplication>http://mysensors.com/Cars</providingApplication>
        </contextRegistration>
      </contextRegistrationList>
      <duration>P1M</duration>
    </registerContextRequest>
EOF
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "contextRegistrations": [
    {
        "entities": [
        {
            "type": "Car",
            "isPattern": "false",
            "id": "Car1"
        }
        ],
        "attributes": [
        {
            "name": "speed",
            "type": "integer",
            "isDomain": "false"
        }
        ],
        "providingApplication": "http://mysensors.com/Cars"
    }
    ],
    "duration": "P1M"
}
EOF
XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
    <registerContextRequest>
      <contextRegistrationList>
        <contextRegistration>
          <entityIdList>
            <entityId type="Car" isPattern="false">
              <id>Car2</id>
            </entityId>
          </entityIdList>
          <contextRegistrationAttributeList>
            <contextRegistrationAttribute>
              <name>location</name>
              <type>ISO6709</type>
              <isDomain>false</isDomain>
            </contextRegistrationAttribute>
          </contextRegistrationAttributeList>
          <providingApplication>http://mysensors.com/Cars</providingApplication>
        </contextRegistration>
      </contextRegistrationList>
      <duration>P1M</duration>
    </registerContextRequest>
EOF
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "contextRegistrations": [
    {
        "entities": [
        {
            "type": "Car",
            "isPattern": "false",
            "id": "Car2"
        }
        ],
        "attributes": [
        {
            "name": "location",
            "type": "ISO6709",
            "isDomain": "false"
        }
        ],
        "providingApplication": "http://mysensors.com/Cars"
    }
    ],
    "duration": "P1M"
}
EOF

As both registrations match the entityIdList and attributeList used in the updateContextAvailabilitySubscription, we will get a notification for each car registration, as can be seen in accumulator-server.py:

XML JSON (since release 0.9.0)
POST http://localhost:1028/accumulate
Content-Length: 825
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/xml

<notifyContextAvailabilityRequest>
  <subscriptionId>52a745e011f5816465943d59</subscriptionId>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Car" isPattern="false">
            <id>Car1</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>speed</name>
            <type>integer</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Cars</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</notifyContextAvailabilityRequest>
POST http://localhost:1028/accumulate
Content-Length: 529
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/json

{
  "subscriptionId" : "52a745e011f5816465943d59",
  "contextRegistrationResponses" : [
    {
      "contextRegistration" : {
        "entities" : [
          {
            "type" : "Car",
            "isPattern" : "false",
            "id" : "Car1"
          }
        ],
        "attributes" : [
          {
            "name" : "speed",
            "type" : "integer",
            "isDomain" : "false"
          }
        ],
        "providingApplication" : "http://mysensors.com/Cars"
      }
    }
  ]
XML JSON (since release 0.9.0)
POST http://localhost:1028/accumulate
Content-Length: 831
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/xml

<notifyContextAvailabilityRequest>
  <subscriptionId>52a745e011f5816465943d59</subscriptionId>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Car" isPattern="false">
            <id>Car2</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>location</name>
            <type>ISO6709</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Cars</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</notifyContextAvailabilityRequest>
POST http://localhost:1028/accumulate
Content-Length: 535
User-Agent: orion/0.9.0
Host: localhost:1028
Accept: application/xml, application/json
Content-Type: application/json

{
  "subscriptionId" : "52a745e011f5816465943d59",
  "contextRegistrationResponses" : [
    {
      "contextRegistration" : {
        "entities" : [
          {
            "type" : "Car",
            "isPattern" : "false",
            "id" : "Car2"
          }
        ],
        "attributes" : [
          {
            "name" : "location",
            "type" : "ISO6709",
            "isDomain" : "false"
          }
        ],
        "providingApplication" : "http://mysensors.com/Cars"
      }
    }
  ]
}

Finally, you can cancel a subscription using the NGSI9 unsubscribeContextAvailability operation, just using the subscriptionId in the request payload (replace the subscriptionId value after copy-pasting with the one you received in the subscribeContextAvailability response in the previous step).

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/unsubscribeContextAvailability -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<unsubscribeContextAvailabilityRequest>
  <subscriptionId>52a745e011f5816465943d59</subscriptionId>
</unsubscribeContextAvailabilityRequest>
EOF
(curl localhost:1026/v1/registry/unsubscribeContextAvailability -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "subscriptionId": "52a745e011f5816465943d59"
}
EOF

The response is just an acknowledgement that the cancellation was successful.

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<unsubscribeContextAvailabilityResponse>
  <subscriptionId>52a745e011f5816465943d59</subscriptionId>
  <statusCode>
    <code>200</code>
    <reasonPhrase>OK</reasonPhrase>
  </statusCode>
</unsubscribeContextAvailabilityResponse>
{
    "statusCode": {
        "code": "200",
        "reasonPhrase": "OK"
    },
    "subscriptionId": "52a745e011f5816465943d59"
}

After cancelling, you can try to register a new car (e.g. Car3) to check that no new notification is sent to accumulator-server.py.

Summary of NGSI9 standard operations URLs

Each standard operation has a unique URL. All of them use the POST method. The summary is below:

  • <host:port>/v1/registry/registerContext
  • <host:port>/v1/registry/discoverContextAvailability
  • <host:port>/v1/registry/subscribeContextAvailability
  • <host:port>/v1/registry/updateContextAvailabilitySubscription
  • <host:port>/v1/registry/unsubscribeContextAvailability

Tutorial on NGSI9 convenience operations

The following section describes the different convenience operations described as part of the FIWARE NGSI REST API NGSI9 that Orion Context Broker supports, showing examples of requests and responses. Convenience operations are a set of operations that have been defined by FIWARE project to ease the usage of NGSI implementations as a complement to the standard operations defined in the OMA NGSI specification (see the section on additional information later in this manual).

Don't forget to restart the broker before starting this tutorial as described previously in this document.

At the end of this section, you will have learnt to use convenience operations as a handy alternative to some standard operations described in the previous section. It is highly recommended to do that tutorial before, to get familiar with register, discover, etc. to be able to compare between the two approaches.


Convenience Register Context

First of all, we register Room1 and Room2 with attributes temperature and pressure, using the following commands:

XML JSON (since release 0.10.0)
(curl localhost:1026/v1/registry/contextEntities/Room1/attributes/temperature -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) << EOF
<?xml version="1.0"?>
<registerProviderRequest>
  <duration>P1M</duration>
  <providingApplication>http://mysensors.com/Rooms</providingApplication>
</registerProviderRequest>
EOF
(curl localhost:1026/v1/registry/contextEntities/Room1/attributes/temperature -s -S --header 'Content-Type: application/json' --header 'Accept: application/json'  -d @- | python -mjson.tool) << EOF
{
  "duration" : "P1M",
  "providingApplication" : "http://mysensors.com/Rooms"
}
EOF
XML JSON (since release 0.10.0)
(curl localhost:1026/v1/registry/contextEntities/Room1/attributes/pressure -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) << EOF
<?xml version="1.0"?>
<registerProviderRequest>
  <duration>P1M</duration>
  <providingApplication>http://mysensors.com/Rooms</providingApplication>
</registerProviderRequest>
EOF
(curl localhost:1026/v1/registry/contextEntities/Room1/attributes/pressure -s -S --header 'Content-Type: application/json' --header 'Accept: application/json'  -d @- | python -mjson.tool) << EOF
{
  "duration" : "P1M",
  "providingApplication" : "http://mysensors.com/Rooms"
}
EOF
XML JSON (since release 0.10.0)
(curl localhost:1026/v1/registry/contextEntities/Room2/attributes/temperature -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) << EOF
<?xml version="1.0"?>
<registerProviderRequest>
  <duration>P1M</duration>
  <providingApplication>http://mysensors.com/Rooms</providingApplication>
</registerProviderRequest>
EOF
(curl localhost:1026/v1/registry/contextEntities/Room2/attributes/temperature -s -S --header 'Content-Type: application/json' --header 'Accept: application/json'  -d @- | python -mjson.tool) << EOF
{
  "duration" : "P1M",
  "providingApplication" : "http://mysensors.com/Rooms"
}
EOF
XML JSON (since release 0.10.0)
(curl localhost:1026/v1/registry/contextEntities/Room2/attributes/pressure -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) << EOF
<?xml version="1.0"?>
<registerProviderRequest>
  <duration>P1M</duration>
  <providingApplication>http://mysensors.com/Rooms</providingApplication>
</registerProviderRequest>
EOF
(curl localhost:1026/v1/registry/contextEntities/Room2/attributes/pressure -s -S --header 'Content-Type: application/json' --header 'Accept: application/json'  -d @- | python -mjson.tool) << EOF
{
  "duration" : "P1M",
  "providingApplication" : "http://mysensors.com/Rooms"
}
EOF

So, what's the difference compared to standard registerContext operation?

  • We needed four requests, instead of just one request in the standard operation case.
  • We are using more operations, but the payload used in each operation is much simpler. This payload is a simplified version of the payload in registerContext, including only duration and providing application.
  • Before 0.16.0 entity type couldn't be registered using convenience operations. Thus, we cannot specify if "Room1" is in type "Room" or "Space". This lack of typing has some important implications. However, since 0.16.0 you can replace "Room1" by "/type/Room/id/Room1" in the URl to define the type (in general: "/type/<type>/id/<id>").
  • From the Orion Context Broker perspective, there are 4 independent registrations (i.e. 4 different registration IDs) to all effects (e.g. updating, extending duration).
  • It is possible to use /v1/registry/contextEntities/Room1 (without the attribute part). In that case, you are registering an entity without attributes (whatever it means in the context of your application :). Note you cannot specify attributes in the registerProviderRequest element.

The response to each of these requests is the same as the response to a standard registerContext (one response for each of the four requests, with a different ID):

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<registerContextResponse>
  <registrationId>51c1f5c31612797e4fe6b6b6</registrationId>
  <duration>P1M</duration>
</registerContextResponse>
{
  "duration": "P1M",
  "registrationId": "51c1f5c31612797e4fe6b6b6"
}

Convenience Discover Context Availability

Using convenience operations you can discover registration information for a single entity or for an entity-attribute pair. For example, to discover registrations for Room1 (no matter the attributes):

XML JSON (since release 0.10.0)
curl localhost:1026/v1/registry/contextEntities/Room1 -s -S --header 'Content-Type: application/xml' | xmllint --format -
curl localhost:1026/v1/registry/contextEntities/Room1 -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool

which produces the following response:

XML JSON (since release 0.10.0)
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="" isPattern="false">
            <id>Room1</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>temperature</name>
            <type/>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Rooms</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="" isPattern="false">
            <id>Room1</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>pressure</name>
            <type/>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Rooms</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>
{
  "contextRegistrationResponses": [
  {
    "contextRegistration": {
      "attributes": [
      {
        "isDomain": "false",
        "name": "temperature",
        "type": ""
      }
      ],
      "entities": [
      {
        "id": "Room1",
        "isPattern": "false",
        "type": ""
      }
      ],
      "providingApplication": "http://mysensors.com/Rooms"
    }
  },
  {
    "contextRegistration": {
      "attributes": [
      {
        "isDomain": "false",
        "name": "pressure",
        "type": ""
      }
      ],
      "entities": [
      {
        "id": "Room1",
        "isPattern": "false",
        "type": ""
      }
      ],
      "providingApplication": "http://mysensors.com/Rooms"
    }
  }
  ]
}

Now, let's discover registrations for Room2-temperature:

XML JSON (since release 0.10.0)
curl localhost:1026/v1/registry/contextEntities/Room2/attributes/temperature -s -S --header 'Content-Type: application/xml' | xmllint --format -
curl localhost:1026/v1/registry/contextEntities/Room2/attributes/temperature -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool

The response is as follows:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="" isPattern="false">
            <id>Room2</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>temperature</name>
            <type/>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Rooms</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>
{
  "contextRegistrationResponses": [
  {
    "contextRegistration": {
      "attributes": [
      {
        "isDomain": "false",
        "name": "temperature",
        "type": ""
      }
      ],
      "entities": [
      {
        "id": "Room2",
        "isPattern": "false",
        "type": ""
      }
      ],
      "providingApplication": "http://mysensors.com/Rooms"
    }
  }
  ]
}

Discovery of not registered elements (e.g. Room5 or the humidity of Room1) will produce an error. E.g. the following requests:

XML JSON (since release 0.10.0)
curl localhost:1026/v1/registry/contextEntities/Room3 -s -S --header 'Content-Type: application/xml' | xmllint --format -
curl localhost:1026/v1/registry/contextEntities/Room3 -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool
XML JSON (since release 0.10.0)
curl localhost:1026/v1/registry/contextEntities/Room2/attributes/humidity -s -S --header 'Content-Type: application/xml' | xmllint --format -
curl localhost:1026/v1/registry/contextEntities/Room2/attributes/humidity -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool

will produce the following error response:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <errorCode>
    <code>404</code>
    <reasonPhrase>No context element found</reasonPhrase>
  </errorCode>
</discoverContextAvailabilityResponse>
{
  "errorCode": {
    "code": "404",
    "reasonPhrase": "No context element found"
  }
}

Compared to standard discoverContextAvailability operation:

  • Convenience operations use the GET method without needing any payload in the request (simpler than the standard operation)
  • The structure of the response XML (i.e. discoverContextAvailabilityResponse) is the same in both cases. However, there are two differences in the content. First, each attribute always comes in a different contextRegistrationResponse element (as each attribute corresponds to a different registration, as explained before). Secondly, as registrations done using convenience operations aren't typed, the type fields are empty for entities and attributes.
  • Before 0.16.0 entity type couldn't be specified. Thus, we cannot specify if "Room1" is in type "Room" or "Space". This lack of typing has some important implications. However, since 0.16.0 you can replace "Room1" by "/type/Room/id/Room1" in the URl to define the type (in general: "/type/<type>/id/<id>").
  • It is not possible to use convenience operations to discover lists of entities, entity patterns, nor lists of attributes.

Since release 0.9.0: you can also discover by all the entities belonging to the same type, either all the attributes or a particular one, as shown below. First, register an couple of entities of type Car using standard registerContext operations (given that, as described in previous section, you cannot register entities with types using convenience operations):

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
    <registerContextRequest>
      <contextRegistrationList>
        <contextRegistration>
          <entityIdList>
            <entityId type="Car" isPattern="false">
              <id>Car1</id>
            </entityId>
          </entityIdList>
          <contextRegistrationAttributeList>
            <contextRegistrationAttribute>
              <name>speed</name>
              <type>integer</type>
              <isDomain>false</isDomain>
            </contextRegistrationAttribute>
          </contextRegistrationAttributeList>
          <providingApplication>http://mysensors.com/Cars</providingApplication>
        </contextRegistration>
      </contextRegistrationList>
      <duration>P1M</duration>
    </registerContextRequest>
EOF
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextRegistrations": [
  {
    "entities": [
    {
      "type": "Car",
      "isPattern": "false",
      "id": "Car1"
    }
    ],
    "attributes": [
    {
      "name": "speed",
      "type": "integer",
      "isDomain": "false"
    }
    ],
    "providingApplication": "http://mysensors.com/Cars"
  }
  ],
  "duration": "P1M"
}
EOF
XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
    <registerContextRequest>
      <contextRegistrationList>
        <contextRegistration>
          <entityIdList>
            <entityId type="Car" isPattern="false">
              <id>Car2</id>
            </entityId>
          </entityIdList>
          <contextRegistrationAttributeList>
            <contextRegistrationAttribute>
              <name>fuel</name>
              <type>float</type>
              <isDomain>false</isDomain>
            </contextRegistrationAttribute>
          </contextRegistrationAttributeList>
          <providingApplication>http://mysensors.com/Cars</providingApplication>
        </contextRegistration>
      </contextRegistrationList>
      <duration>P1M</duration>
    </registerContextRequest>
EOF
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextRegistrations": [
  {
    "entities": [
    {
      "type": "Car",
      "isPattern": "false",
      "id": "Car2"
    }
    ],
    "attributes": [
    {
      "name": "fuel",
      "type": "float",
      "isDomain": "false"
    }
    ],
    "providingApplication": "http://mysensors.com/Cars"
  }
  ],
  "duration": "P1M"
}
EOF

Request without specifying attributes:

XML JSON (since release 0.10.0)
curl localhost:1026/v1/registry/contextEntityTypes/Car -s -S --header 'Content-Type: application/xml' | xmllint --format -
curl localhost:1026/v1/registry/contextEntityTypes/Car -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool

Response:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Car" isPattern="false">
            <id>Car1</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>speed</name>
            <type>integer</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Cars</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Car" isPattern="false">
            <id>Car2</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>fuel</name>
            <type>liter</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Cars</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>
{
  "contextRegistrationResponses": [
  {
    "contextRegistration": {
      "attributes": [
      {
        "isDomain": "false",
        "name": "speed",
        "type": "integer"
      }
      ],
      "entities": [
      {
        "id": "Car1",
        "isPattern": "false",
        "type": "Car"
      }
      ],
      "providingApplication": "http://mysensors.com/Cars"
    }
  },
  {
    "contextRegistration": {
      "attributes": [
      {
        "isDomain": "false",
        "name": "fuel",
        "type": "float"
      }
      ],
      "entities": [
      {
        "id": "Car2",
        "isPattern": "false",
        "type": "Car"
      }
      ],
      "providingApplication": "http://mysensors.com/Cars"
    }
  }
  ]
}

Request specifying one attribute (e.g. speed):

XML JSON (since release 0.10.0)
curl localhost:1026/v1/registry/contextEntityTypes/Car/attributes/speed -s -S --header 'Content-Type: application/xml' | xmllint --format -
curl localhost:1026/v1/registry/contextEntityTypes/Car/attributes/speed -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool

Response:

XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Car" isPattern="false">
            <id>Car1</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>speed</name>
            <type>integer</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Cars</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>
{
  "contextRegistrationResponses": [
  {
    "contextRegistration": {
      "attributes": [
      {
        "isDomain": "false",
        "name": "speed",
        "type": "integer"
      }
      ],
      "entities": [
      {
        "id": "Car1",
        "isPattern": "false",
        "type": "Car"
      }
      ],
      "providingApplication": "http://mysensors.com/Cars"
    }
  }
  ]
}

Note that by default only 20 registrations are returned (which is fine for this tutorial, but probably not for a real utilization scenario). In order to change this behaviour, see the section on pagination in this manual.

Convenience operations for context availability subscriptions

Since release 0.9.0: new functionality.

You can use the following convenience operations to manage context availability subscriptions:

  • POST /v1/registry/contextAvailabilitySubscriptions, to create the subscription, using the same payload as standard susbcribeAvailabilityContext operation.
  • PUT /v1/registry/contextAvailabilitySubscriptions/{subscriptionID}, to update the subscription identified by {subscriptionID}, using the same payload as standard updateContextAvailabilitySubscription operation. The ID in the payload must match the ID in the URL.
  • DELETE /v1/registry/contextAvailabilitySubscriptions/{subscriptionID}, to cancel the subscription identified by {subscriptionID}. In this case, payload is not used.

Summary of NGSI9 convenience operations URLs

Convenience operations use a URL to identify the resource and a HTTP verb to identify the operation on that resource following the usual REST convention: GET is used to retrieve information, POST is used to create new information, PUT is used to update information and DELETE is used to destroy information.

You find a summary in the following document. .

Advanced topics

Pagination

Since 0.14.0

In order to help clients organize query and discovery requests with a large number of responses (for example, think of how costly could be returning a query matching 1,000,000 results in a single HTTP response to a queryContext request), queryContext (and related convenience operations) and discoverContextAvailability (and related convenience operations) allow pagination. The mechanism is based on three URI parameters:

  • limit, in order to specify the maximum number of entities or context registrations (for queryContext and discoverContextAvailability respectively) (default is 20, maximun allowed is 1000).
  • offset, in order to skip a given number of elements at the beginning (default is 0)
  • details (allowed values are “on” and “off”, default is “off”), in order to get a global errorCode for the response including a count of total elements (in the case of using “on”). Note that using details set to “on” slightly breaks NGSI standard, which states that global errorCode must be used only in the case of general error with the request. However, we think it is very useful for a client to know in advance how many results in total the query has (and if you want to keep strict with NGSI, you can simply ignore the details parameter :)

Result are returned ordered by increasing entity/registration creation time. This is to ensure that if a new entity/registration is created while the client is going through all the results the new results are added at the end (thus avoiding duplication results).

Let’s illustrate with an example: a given client cannot process more than 100 results in a single response and the queryContext includes a total of 322 results. The client could do the following (only URL is included, for the sake of completeness).

POST <orion_host>:1026/v1/queryContext?limit=100&details=on
...
(The first 100 elements are returned, along with the following errorCode in the response, 
which allows the client to know how many entities are in sum and, therefore, the number of 
subsequence queries to do)

  <errorCode>
    <code>200</code>
    <reasonPhrase>OK</reasonPhrase>
    <details>Count: 322</details>
  </errorCode>

POST <orion_host>:1026/v1/queryContext?offset=100&limit=100
...
(Entities from 101 to 200)

POST <orion_host>:1026/v1/queryContext?offset=200&limit=100
...
(Entities from 201 to 300)

POST <orion_host>:1026/v1/queryContext?offset=300&limit=100
...
(Entities from 301 to 222)

Note that if the request uses an “out of bound” offset you will get a 404 NGSI error, as shown below:

POST <orion_host>:1026/v1/queryContext?offset=1000&limit=100
...
<queryContextResponse>
  <errorCode>
    <code>404</code>
    <reasonPhrase>No context element found</reasonPhrase>
    <details>Number of matching entities: 5. Offset is 1000</details>
  </errorCode>
</queryContextResponse>

Multi service tenancy

New since 0.13.0 as experimental (consolided in 0.18.1)

The Orion Context Broker implements a simple multitenant/multiservice model based and logical database separation, to ease service/tenant based authorization policies provided by other FIWARE components or third party software, e.g. the ones in the FIWARE security framework (PEP proxy, IDM and Access Control). This functionality is activated when the "-multiservice" command line option is used. When "-multiservice" is used, Orion uses the "Fiware-Service" HTTP header in the request to identify the service/tenant. If the header is not present in the HTTP request, the default service/tenant is used.

Multitenant/multiservice ensures that the entities/attributes/subscriptions of one service/tenant are "invisible" to other services/tentants. For example, queryContext on tenantA space will never return entities/attributes from tenantB space. This isolation is based on database separation, which details are described in the Installation and Administration manual.

In addition, note that when "-multiservice" is used Orion includes the "Fiware-Service" header in the notifyContextRequest and notifyContextAvailability request messages associated to subscriptions in the given tenant/service (except for the default service/tenant, in which case the header is not present), e.g.:

POST http://127.0.0.1:9977/notify
Content-Length: 725
User-Agent: orion/0.13.0
Host: 127.0.0.1:9977
Accept: application/xml, application/json
Fiware-Service: t_02
Content-Type: application/xml

<notifyContextRequest>
...
<notifyContextRequest>

Regarding service/tenant name syntax, it must be a string of alphanumeric characters (and the "_" symbol). Maximum length is 50 characters (it was 20 before 0.17.0), which should be enough for most use cases. Orion Context Broker interprets the tentant name in lowercase, thus, although you can use tenants such as in updateContext "MyService" it is not advisable, as the notifications related with that tenant will be sent with "myservice" and, in that sense, it is not coherent the tentant you used in updateContext compared with the one that Orion sends in notifyContextRequest.

Entity service paths

New since 0.14.0 as experimental (consolided in 0.18.1)

Orion Context Broker supports hierarchical scopes, so entities can be assigned to a scope at creation time with updateContext (or related convenience operation). Then, queryContext (and related convenience operations) (since 0.18.1 also subscribeContext and related convenience operations) can be also scoped to locate entities in the corresponding scopes.

For example, consider an Orion-based application using the following scopes (shown in the figure):

  • Madrid, as first level scope
  • Gardens and Districts, as second-level scope (children of Madrid)
  • ParqueNorte, ParqueOeste and ParqueSur (children of Gardens) and Fuencarral and Latina (children of Districts)
  • Parterre1 and Parterre2 (children of ParqueNorte)

The scope to use is specified using the “Fiware-ServicePath” HTTP in update/query request. For example, to create the entity “Tree1” of type "Tree" in “Parterre1” the following Fiware-ServicePath will be used:

Fiware-ServicePath: /Madrid/Gardens/ParqueNorte/Parterre1

In order to search for “Tree1” in that scope, the same Fiware-ServicePath will be used.

Scopes are hierarchical and hierarchical search can be done. In order to do that the '#' special keyword is used. Thus, a queryContext with pattern entity id “.*” of type “Tree” in /Madrid/Gardens/ParqueNorte/# will return all the trees in ParqueNorte, Parterre1 and Parterre2 (the '#' syntax was introduced in release 0.17.0, previous versions worked always in recursive way by default).

Finally, you can query for disjoint scopes, using a comma-separated list in the Fiware-ServicePath header. For example, to get all trees in both ParqueNorte and ParqueOeste (but not ParqueSur) the following Fiware-ServicePath would be used in queryContext request:

Fiware-ServicePath: /Madrid/Gardens/ParqueNorte, /Madrid/Gardens/ParqueOeste

Some additional remarks:

  • Limitations:
    • Scope must start with "/" (only 'absolute' scopes are allowed)
    • 10 maximum scope levels in a path
    • 50 maximum characters in each level (before release 0.17.0 it was 10), only alphanum and underscore allowed
    • 10 maximum dijoint scopes paths in a comman separated list in query Fiware-ServicePath header (no more than 1 scope path in update Fiware-ServicePath header)
  • Fiware-ServicePath is an optional header. It is assumed that all the entities created without Fiware-ServicePath (or that don't include service path information in the database) belongs to a root scope "/" implicitely. All the queries without using Fiware-ServicePath are on "/#" implicitely. This behavior ensures backward compatibility to pre-0.14.0 versions.
  • It is possible to have an entity with the same ID and type in different Scopes. E.g. we can create entity ID "Tree1" of type "Tree" in /Madrid/Gardens/ParqueNorte/Parterre1 and another entity with ID "Tree1" of type "Tree" in Madrid/Gardens/ParqueOeste without getting any error. However, queryContext can be weird in this scenario (e.g. a queryContext in Fiware-ServicePath /Madrid/Gardens will returns two entities with the same ID and type in the queryContextResponse, making hard to distinguish to which scope belongs each one)
  • Entities belongs to one (and only one) scope.
  • New in 0.18.1: Fiware-ServicePath header is included in NGSI10 notifyContext requests sent by Orion.
  • The scopes entities can be combined orthogonally with the multi-service/multi-tenant functionality. In that case, each “scope tree” lives in a different service/tenant and they can use even the same names with complete database-based isolation. See figure below.
  • Current version doesn’t allow to change the scope to which an entity belongs through the API (a workaround is to modify the _id.servicePath field in the entities collection directly).

Forbidden characters

Since 0.17.0

In order to avoid script injections attack in some circustances (e.g. cross domain to co-located web servers in the same hot that CB) the following characters are forbidden in any request:

  • <
  • >
  • "
  • '
  • =
  •  ;
  • (
  • )

Any attemp of using them will result in a NGSI 400 Bad Request response like this:

{
    "orionError": {
        "code": "400",
        "details": "Illegal value for JSON field",
        "reasonPhrase": "Bad Request"
    }
}

If your aplication needs to use these characteres, you should encode it using a scheme not including forbidden characters before sending the request to Orion (e.g. URL encoding).

Filters

Since release 0.16.0, Orion Context Broker implements several filters that can be used to filter the results in NGSI10 query operations. These filters are typically used with queryContext with patterns or the convenience operation to get all entities.

As a general rule, filters used in standard operation use a scope element:

XML JSON
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
    <entityId type="myEntType" isPattern="true">
      <id>.*</id>
    </entityId>
  </entityIdList>
  <attributeList>
  </attributeList>
  <restriction>
    <scope>
      <operationScope>
        <scopeType>FIWARE::Filter::foobar</scopeType>
        <scopeValue>...</scopeValue>
      </operationScope>
    </scope>
  </restriction>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
  {
    "type": "myEntityType",
    "isPattern": "true",
    "id": ".*"
  }
  ],
  "restriction": {
    "scopes": [
      {
        "type" : "FIWARE::Filter::foobar",
        "value" : ...
      }
    ]
  }
}
EOF

while filters in convenience operations are included as parameters in the URL:

curl localhost:1026/v1/contextEntities?filter=value -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool

Filters are cumulative. In other words, you can use several scopes in the same restriction (in the case of standard operations) or several URL argument separated by '&' in order to specify several filters. The result is a logic "and" between all of them.

Existence type filter

Since 0.17.0: The scope correspoding to this type is "FIWARE::Filter::Existence".

XML JSON
...
  <restriction>
    <scope>
      <operationScope>
        <scopeType>FIWARE::Filter::Existence</scopeType>
        <scopeValue>entity::type</scopeValue>
      </operationScope>
    </scope>
  </restriction>
...
...
  "restriction": {
    "scopes": [
      {
        "type" : "FIWARE::Filter::Existence",
        "value" : "entity::type"
      }
    ]
  }
...

Since 0.16.0: The URL parameter corresponding to this filter is 'exist'.

curl localhost:1026/v1/contextEntities?exist=entity::type ...

In the current version, the only parameter than can be checked for existence is the entity type, corresponding to "entity::type".

No-Existence type filter

Since 0.17.0: The scope corresponding to this type is "FIWARE::Filter::Not::Existence".

XML JSON
...
  <restriction>
    <scope>
      <operationScope>
        <scopeType>FIWARE::Filter::Not::Existence</scopeType>
        <scopeValue>entity::type</scopeValue>
      </operationScope>
    </scope>
  </restriction>
...
...
  "restriction": {
    "scopes": [
      {
        "type" : "FIWARE::Filter::Not::Existence",
        "value" : "entity::type"
      }
    ]
  }
...

Since 0.16.0: The URL parameter corresponding to this filter is '!exist'.

curl localhost:1026/v1/contextEntities?!exist=entity::type ...

In the current version, the only parameter than can be checked for no-existence is the entity type, corresponding to "entity::type". Note that this is the only way of selecting an "entity without type" (given that queries without type resolve to "any type", as explained in the following section).

Entity type filter

There is no scope corresponding to this filter, given that you can use the usual entity type:

XML JSON
...
    <entityId type="Room" isPattern="...">
      <id>...</id>
    </entityId>
...
...
        {
            "type": "Room",
            "isPattern": "...",
            "id": "..."
        }
...

Since 0.16.0: The URL parameter corresponding to this filter is 'entity::type'.

curl localhost:1026/v1/contextEntities?entity::type=Room ...

Geo-location filter

The scope corresponding to this type is "FIWARE::Location". It is described in detail in the following section.

In the current version of Orion, there is no equivalent convenience operation filter.

Browsing all types and detailed information on a type

Since 0.15.0

The following operation can be used to get a list of all entity types existing at Orion Context Broker in a given moment:

XML JSON
curl localhost:1026/v1/contextTypes -s -S --header 'Content-Type: application/xml'  | xmllint --format -
curl localhost:1026/v1/contextTypes -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool
XML JSON
<entityTypesResponse>
  <typeEntities>
    <entityType>
      <name>Car</name>
      <contextAttributeList>
        <name>speed</name>
        <name>fuel</name>
        <name>temperature</name>
      </contextAttributeList>
    </entityType>
    <entityType>
      <name>Room</name>
      <contextAttributeList>
        <name>pressure</name>
        <name>hummidity</name>
        <name>temperature</name>
      </contextAttributeList>
    </entityType>
  </typeEntities>
  <statusCode>
    <code>200</code>
    <reasonPhrase>OK</reasonPhrase>
  </statusCode>
</entityTypesResponse>
{
    "statusCode": {
        "code": "200",
        "reasonPhrase": "OK"
    },
    "types": [
        {
            "attributes": [
                "speed",
                "fuel",
                "temperature"
            ],
            "name": "Car"
        },
        {
            "attributes": [
                "pressure",
                "hummidity",
                "temperature"
            ],
            "name": "Room"
        }
    ]
}

As you can see, attribute information for each type is provided. Some important remarks:

  • Given that NGSI doesn't force all the entities of a given type to have the same set of attributes (i.e. entities of the same type could have a different attributes set) the attributes set per type returned by this operation is the union set of the attribut sets of each entity belonging to that type.
  • If you are not interested in attributes information, you can use the ?collapse=true parameter in order to get only a list of types.

In addition, you can use the following operation to get detailed information of a given type (by the time being, that information consits of a list of all its attributes):

XML JSON
curl localhost:1026/v1/contextTypes/Room -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -
curl localhost:1026/v1/contextTypes/Room -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool
XML JSON
entityTypeAttributesResponse>
  <entityType>
    <name>Room</name>
    <contextAttributeList>
      <name>hummidity</name>
      <name>pressure</name>
      <name>temperature</name>
    </contextAttributeList>
  </entityType>
  <statusCode>
    <code>200</code>
    <reasonPhrase>OK</reasonPhrase>
  </statusCode>
</entityTypeAttributesResponse>
{
    "attributes": [
        "hummidity",
        "pressure",
        "temperature"
    ],
    "name": "Room",
    "statusCode": {
        "code": "200",
        "reasonPhrase": "OK"
    }
}

Note that pagination mechanism also works in the operations described above.

Custom attribute metadata

Since 0.13.0: new in this release.

Apart from metadata elements to which Orion pays special attention (e.g. ID, location, etc.), users can attach their own metadata to entity attributes. These metadata elements are processed by Orion in a transparent way: it simply stores them in the database at updateContext (and notifyContext time in federeated scenarios) and retrieve it in queryContext or notifyContext.

For example, to create an entity Room1 with attribute "temperature", and associate metadata "accuracy" to "temperature":

XML JSON
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>26.5</contextValue>
          <metadata>
            <contextMetadata>
              <name>accuracy</name>
              <type>float</type>
              <value>0.8</value>
            </contextMetadata>
          </metadata>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>APPEND</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
  {
    "type": "Room",
    "isPattern": "false",
    "id": "Room1",
    "attributes": [
    {
      "name": "temperature",
      "type": "float",
      "value": "26.5",
      "metadatas": [
      {
        "name": "accuracy",
        "type": "float",
        "value": "0.8"
      }
      ]
    }
    ]
  }
  ],
  "updateAction": "APPEND"
}
EOF

Metadata can be updated regardless of the attribute value being updated or not. For example, next updateContext shows how "accuracy" is updated to 0.9 although the value of the temperature iself is still 26.5:

XML JSON
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>26.5</contextValue>
          <metadata>
            <contextMetadata>
              <name>accuracy</name>
              <type>float</type>
              <value>0.9</value>
            </contextMetadata>
          </metadata>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>UPDATE</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
  {
    "type": "Room",
    "isPattern": "false",
    "id": "Room1",
    "attributes": [
    {
      "name": "temperature",
      "type": "float",
      "value": "26.5",
      "metadatas": [
      {
        "name": "accuracy",
        "type": "float",
        "value": "0.9"
      }
      ]
    }
    ]
  }
  ],
  "updateAction": "UPDATE"
}
EOF

Metadata can be added after attribute creation. For example, if we want to add metadata "average" to "temperature" (in addition to the existing "precision"):

XML JSON
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>26.5</contextValue>
          <metadata>
            <contextMetadata>
              <name>average</name>
              <type>float</type>
              <value>22.4</value>
            </contextMetadata>
          </metadata>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>UPDATE</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
  {
    "type": "Room",
    "isPattern": "false",
    "id": "Room1",
    "attributes": [
    {
      "name": "temperature",
      "type": "float",
      "value": "26.5",
      "metadatas": [
      {
        "name": "average",
        "type": "float",
        "value": "22.4"
      }
      ]
    }
    ]
  }
  ],
  "updateAction": "UPDATE"
}
EOF

We can check that temperature includes both attributes

XML JSON
curl localhost:1026/v1/contextEntities/Room1 -s -S | xmllint --format -
curl localhost:1026/v1/contextEntities/Room1 -s -S --header 'Accept: application/json' | python -mjson.tool
XML JSON
<?xml version="1.0"?>
<contextElementResponse>
  <contextElement>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
    <contextAttributeList>
      <contextAttribute>
        <name>temperature</name>
        <type>float</type>
        <contextValue>26.5</contextValue>
        <metadata>
          <contextMetadata>
            <name>average</name>
            <type>float</type>
            <value>22.4</value>
          </contextMetadata>
          <contextMetadata>
            <name>accuracy</name>
            <type>float</type>
            <value>0.9</value>
          </contextMetadata>
        </metadata>
      </contextAttribute>
    </contextAttributeList>
  </contextElement>
  <statusCode>
    <code>200</code>
    <reasonPhrase>OK</reasonPhrase>
  </statusCode>
</contextElementResponse>
{
  "contextElements": [
  {
    "type": "Room",
    "isPattern": "false",
    "id": "Room1",
    "attributes": [
    {
      "name": "temperature",
      "type": "float",
      "value": "26.5",
      "metadatas": [
      {
        "name": "average",
        "type": "float",
        "value": "22.4"
      },
      {
        "name": "accuracy",
        "type": "float",
        "value": "0.9"
      }
      ]
    }
    ]
  }
  ],
  "statusCode": {
    "code": "200",
    "reasonPhrase": "OK"
  }
}

Note that, from the point of view of ONCHANGE subscription, changing the metadata of a given attribute or adding a new metadata element is considered a change even if attribute value itself hasn't changed. Metadata elements cannot be deleted once introduced: in order to delete metadata elements you have to remove the entity attribute (using updateContext DELETE), then re-create it (using updateContext APPEND).

You can use any name for your custom metadata except for the ones used for some metadata names that are interpreted by Orion:

  • ID
  • location
  • creData (reserved for future use)
  • modDate (reserved for future use)

Default duration

Release Note (0.6.0 FIWARE 3.1.1): new in this release.

If you don't specify a duration in registerContext, subscribeContext or subscribeContextAvailability a default of 24 hours is used. You will get a confirmation of the duration in these cases in the response, e.g. for a registerContext:

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<registerContextResponse>
  <registrationId>51bf1e0ada053170df590f20</registrationId>
  <duration>PT24H</duration>
</registerContextResponse>
{
    "duration": "PT24H",
    "registrationId": "52f38a64261c371af12b8565"
}

Mixing standard and convenience operations

Although the tutorials in this manual introduce standard and convenience operations independently for the sake of clarity, you can mix their use without any problem. Note that the set of URLs used by standard operations (NGSI10 and NGSI9) and the set of URLs used by convenience operations (NGSI10 and NGSI9) are orthogonal.

However, take into account some convenience operations don't allow to specify any type for entities nor for attributes, so the rules described in using empty types section apply.

We propose the following exercise: register entities/attributes using standard operations as described in the standards operations tutorial, then discover these attributes using convenience operations.

Registering Context Providers and request forwarding

The register context operation (both in standard and convenience cases) uses a field named "providing application" which is a URL that identifies the source of the context information for the entities/attributes included in that registration. We call that source the "Context Provider" (or CPr, for short).

XML JSON
...
<providingApplication>http://mysensors.com/Rooms</providingApplication>
...
...
"providingApplication" : "http://mysensors.com/Rooms"
...

If Orion receives a query or update operation (either in the standard or in the convenience family) and it cannot find the targeted context element locally (i.e. in its internal database) but a Context Provider is registered for that context element, then Orion will forward the query/update request to the Context Provider. In this case, Orion acts as a pure "NGSI proxy" (i.e. doesn't cache the result of the query internally) and, from the poinf of view of the client issuing the original request, the process is mostly transparent. The Context Provider is meant to implement the NGSI10 API (at least partially) to support the query/update operation.

Let's illustrate this with an example.

  • First (message number 1), the application (maybe on behalf of a Context Provider) registers the Context Provider at Orion for the Street4 temperature. Let's assume that the Context Provider exposes its API on http://sensor48.mycity.com/ngsi10
XML JSON
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<registerContextRequest>
  <contextRegistrationList>
    <contextRegistration>
      <entityIdList>
        <entityId type="Street" isPattern="false">
          <id>Street4</id>
        </entityId>
      </entityIdList>
      <contextRegistrationAttributeList>
        <contextRegistrationAttribute>
          <name>temperature</name>
          <type>float</type>
          <isDomain>false</isDomain>
        </contextRegistrationAttribute>
      </contextRegistrationAttributeList>
      <providingApplication>http://sensor48.mycity.com/v1</providingApplication>
  </contextRegistration>
  </contextRegistrationList>
  <duration>P1M</duration>
</registerContextRequest>
EOF
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "contextRegistrations": [
        {
            "entities": [
                {
                    "type": "Stret",
                    "isPattern": "false",
                    "id": "Street4"
                }
            ],
            "attributes": [
                {
                    "name": "temperature",
                    "type": "float",
                    "isDomain": "false"
                }
            ],
            "providingApplication": "http://sensor48.mycity.com/v1"
        }
    ],
    "duration": "P1M"
}
EOF
  • Next, consider that a client queries the Street4 temperature (message number 2).
XML JSON
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
    <entityId type="Street" isPattern="false">
      <id>Street4</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>temperature</attribute>
  </attributeList>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "entities": [
        {
            "type": "Street",
            "isPattern": "false",
            "id": "Street4"
        }
    ],
    "attributes" : [
        "temperature"
    ]
}
EOF

  • Orion doesn't know the Street 4 temperature, but it knows (due to the registration in the previous step) that the Context Provider at http://sensor48.mycity.com/v1 knows that, so it forwards the query (message number 3) to the URL http://sensor48.mycity.com/v1/queryContext (i.e. the URL used in the Providing Application field at registration time, plus the "/queryContext" operation).
XML JSON
<queryContextRequest>
  <entityIdList>
    <entityId type="Street" isPattern="false">
      <id>Street4</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>temperature</attribute>
  </attributeList>
</queryContextRequest>
EOF
{
    "entities": [
        {
            "type": "Street",
            "isPattern": "false",
            "id": "Street4"
        }
    ],
    "attributes" : [
        "temperature"
    ]
}
XML JSON To be supported in v0.21. See https://github.com/telefonicaid/fiware-orion/issues/722
<?xml version="1.0"?>
<queryContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Street" isPattern="false">
          <id>Street4</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue>16</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</queryContextResponse>
{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": "16"
                    }
                ],
                "id": "Street4",
                "isPattern": "false",
                "type": "Street"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}
  • Orion fordwars the response to the client (message number 5). Note that the response is not exactly the same, as it includes a reference to the Context Provider that has resolved it (that's why it is said that "the process is mostly transparent" instead of "the process is completely transparent"). The client can use (or ignore) that information. Orion doesn't store the Street4 temperature.
XML JSON (since release 0.9.0)


<?xml version="1.0"?>
<queryContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Street" isPattern="false">
          <id>Street4</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue>16</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <details>Redirected to context provider http://sensor48.mycity.com/ngsi10</details>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</queryContextResponse>

{
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "temperature",
                        "type": "float",
                        "value": "16"
                    }
                ],
                "id": "Street4",
                "isPattern": "false",
                "type": "Street"
            },
            "statusCode": {
                "code": "200",
                "details": "Redirected to context provider http://sensor48.mycity.com/ngsi10"
                "reasonPhrase": "OK"
            }
        }
    ]
}

The Context Providers and request forwarding functionality was developed in release 0.15.0. Previous version of Orion Context Broker just stores this field in the database. Thus, applications can access the Providing Application using the discover context availability operation and do whatever they want with it. This is typically the case when the Orion Context Broker is used just as a repository for NGSI9 registrations, but the actual management of context information is done by other components of the architecture. Although current versions support Context Providers and request forwarding functionaly, nothing precludes you from using Orion also in that way.

Some additional comments:

  • Since 0.21.0: the format used to send the forward (either JSON or XML) is the one used to make the registration of the CPr (this can be overriden with the "notifyFormat" parameter, as with subscriptions). Note that it could be different from the one used by the client which request is being forwarded.
  • Since 0.21.0: the "-httpTimeout" CLI parameter is used to set the CPr timeout. If a request forwarded to a CPr is taking more that that timeout, then Orion closes the connection and assumes that the CPr is not responding.
  • Since 0.21.0: in the case a given request involves more than one Context Provider (e.g. an updateContext including 3 context elements, each one being an entity managed by a different Context Provider), Orion will forward the corresponding "piece" of the request to each Context Provider, gathering all the results before responding to the client. Current implementation process multiple forwards in sequence, i.e. waiting the response from a given CPr (or timeout expiration) before sending the forward request to the following.

Updating registrations

The response to a register context request (both in standard and convenience) includes a registration ID (a 24 hexadecimal digit number):

XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<registerContextResponse>
  <registrationId>51bf1e0ada053170df590f20</registrationId>
  <duration>PT24H</duration>
</registerContextResponse>
{
    "duration": "PT24H",
    "registrationId": "51bf1e0ada053170df590f20"
}

This ID can be used to update the registration. There is no special operation to update a registration (in this sense, it is different from context subscriptions and context availability subscriptions, which have updateContextSubscription and updateContextAvailabilitySubscription operations). The update is done issuing a new registerContextRequest, with the registrationId set:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
    <registerContextRequest>
      <contextRegistrationList>
        <contextRegistration>
          <entityIdList>
            <entityId type="Room" isPattern="false">
              <id>Room8</id>
            </entityId>
          </entityIdList>
          <contextRegistrationAttributeList>
            <contextRegistrationAttribute>
              <name>humidity</name>
              <type>percentage</type>
              <isDomain>false</isDomain>
            </contextRegistrationAttribute>
          </contextRegistrationAttributeList>
          <providingApplication>http://mysensors.com/Rooms</providingApplication>
        </contextRegistration>
      </contextRegistrationList>
      <duration>P1M</duration>
      <registrationId>51bf1e0ada053170df590f20</registrationId>
    </registerContextRequest>
EOF
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
    "contextRegistrations": [
        {
            "entities": [
                {
                    "type": "Room",
                    "isPattern": "false",
                    "id": "Room8"
                }
            ],
            "attributes": [
                {
                    "name": "humidity",
                    "type": "percentage",
                    "isDomain": "false"
                }
            ],
            "providingApplication": "http://mysensors.com/Rooms"
        }
    ],
    "duration": "P1M",
    "registrationId": "51bf1e0ada053170df590f20"
}
EOF

This "update registration" replaces the existing registration associated to that ID with the new content, including expiration recalculation.

Surprisingly, there is no way in NGSI to cancel a registration. A workaround is to update it with a non-existing entity and duration 0, but in order to do an actual delete you will need to remove the registration from the database (check the administration manual about managing database).

Updating subscriptions

You have previously seen in this document that context subscriptions and context availability subscriptions can be updated. However, differently from registerContext, not everything can be updated. Let's look at this closely, depending on the type of subscription.

What can be updated in a context subscription?

The payload for updateContextSubscription is similar to the one for a subscribeContext request. However, not all the fields can be included, as not everything can be updated. In particular, the following fields cannot be updated:

  • subscriptionId (although you must include it in updateContextSubscription to refer to the subscription)
  • entityIdList
  • attributeList
  • reference

However, the following fields can be modified:

  • notifyConditions
  • throttling
  • duration
  • restriction

What can be updated in a context availability subscription?

The payload used by an updateContextAvailabilitySubscription is pretty similar to the one in the subscribeContextAvailability request. However, not all the fields can be included, as not everything is updatable. In particular, the following is not updatable:

  • subscriptionId (although you must include it in updateContextAvailabilitySubscription to refer to the subscription)
  • reference

Thus, the following is updatable:

  • entityIdList (in fact, it is mandatory in every updateContextAvailabilitySubscription).
  • attributeList
  • duration

Adding and removing attributes with APPEND and DELETE in updateContext

We have seen how to use updateContext with APPEND action type to create new entities. In addition, APPEND can be used to add a new attribute after entity creation. Let's illustrate this with an example.

We start creating a simple entity 'E1' with one attribute named 'A':

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="T" isPattern="false">
        <id>E1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>A</name>
          <type>TA</type>
          <contextValue>1</contextValue>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>APPEND</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
    {
      "type": "T",
      "isPattern": "false",
      "id": "E1",
      "attributes": [
      {
        "name": "A",
        "type": "TA",
        "value": "1"
      }
      ]
    }
  ],
  "updateAction": "APPEND"
}
EOF

Now, in order to append a new attribute (let's name it 'B') we use updateContext APPEND with an entityId matching 'E1':

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="T" isPattern="false">
        <id>E1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>B</name>
          <type>TB</type>
          <contextValue>2</contextValue>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>APPEND</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
    {
      "type": "T",
      "isPattern": "false",
      "id": "E1",
      "attributes": [
      {
        "name": "B",
        "type": "TB",
        "value": "2"
      }
      ]
    }
  ],
  "updateAction": "APPEND"
}
EOF

Now we can check with a query to that entity that both attributes A and B are there:

XML JSON (since release 0.10.0)
curl localhost:1026/v1/contextEntities/E1 -s -S --header 'Content-Type: application/xml' | xmllint --format -
curl localhost:1026/v1/contextEntities/E1 -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool


XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<contextElementResponse>
  <contextElement>
    <entityId type="" isPattern="false">
      <id>E1</id>
    </entityId>
    <contextAttributeList>
      <contextAttribute>
        <name>A</name>
        <type>TA</type>
        <contextValue>1</contextValue>
      </contextAttribute>
      <contextAttribute>
        <name>B</name>
        <type>TB</type>
        <contextValue>2</contextValue>
      </contextAttribute>
    </contextAttributeList>
  </contextElement>
  <statusCode>
    <code>200</code>
    <reasonPhrase>OK</reasonPhrase>
  </statusCode>
</contextElementResponse>
{
    "contextElement": {
        "attributes": [
            {
                "name": "B",
                "type": "TB",
                "value": "2"
            },
            {
                "name": "A",
                "type": "TA",
                "value": "1"
            }
        ],
        "id": "E1",
        "isPattern": "false",
        "type": ""
    },
    "statusCode": {
        "code": "200",
        "reasonPhrase": "OK"
    }
}

We can also remove attributes in a similar way, using the DELETE action type. For example, to remove attribute 'A' we will use (note the empty contextValue element):

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="T" isPattern="false">
        <id>E1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>A</name>
          <type>TA</type>
          <contextValue/>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>DELETE</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
    {
      "type": "T",
      "isPattern": "false",
      "id": "E1",
      "attributes": [
      {
        "name": "A",
        "type": "TA",
        "value": ""
      }
      ]
    }
  ],
  "updateAction": "DELETE"
}
EOF

Now, a query to the entity shows attribute B:

XML JSON (since release 0.10.0)
curl localhost:1026/v1/contextEntities/E1 -s -S --header 'Content-Type: application/xml' | xmllint --format -
curl localhost:1026/v1/contextEntities/E1 -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool
XML JSON (since release 0.9.0)
<?xml version="1.0"?>
<contextElementResponse>
  <contextElement>
    <entityId type="" isPattern="false">
      <id>E1</id>
    </entityId>
    <contextAttributeList>
      <contextAttribute>
        <name>B</name>
        <type>TB</type>
        <contextValue>2</contextValue>
      </contextAttribute>
    </contextAttributeList>
  </contextElement>
  <statusCode>
    <code>200</code>
    <reasonPhrase>OK</reasonPhrase>
  </statusCode>
</contextElementResponse>
{
    "contextElement": {
        "attributes": [
            {
                "name": "B",
                "type": "TB",
                "value": "2"
            }
        ],
        "id": "E1",
        "isPattern": "false",
        "type": ""
    },
    "statusCode": {
        "code": "200",
        "reasonPhrase": "OK"
    }
}

You can also use convenience operations with POST and DELETE verbs to add and delete attributes. Try the following:

Add a new attribute 'C' and 'D':

XML JSON (since release 0.10.0)
(curl localhost:1026/v1/contextEntities/E1 -s -S --header 'Content-Type: application/xml' -X POST -d @- | xmllint --format - ) << EOF
<?xml version="1.0" encoding="UTF-8"?>
<appendContextElementRequest>
  <contextAttributeList>
    <contextAttribute>
      <name>C</name>
      <type>TC</type>
      <contextValue>3</contextValue>
    </contextAttribute>
    <contextAttribute>
      <name>D</name>
      <type>TD</type>
      <contextValue>4</contextValue>
    </contextAttribute>
  </contextAttributeList>
</appendContextElementRequest>
EOF
(curl localhost:1026/v1/contextEntities/E1 -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "attributes" : [
    {
      "name" : "C",
      "type" : "TC",
      "value" : "3"
    },
    {
      "name" : "D",
      "type" : "TD",
      "value" : "4"
    }
  ]
}
EOF

Remove attribute 'B':

XML JSON (since release 0.10.0)
curl localhost:1026/v1/contextEntities/E1/attributes/B -s -S --header 'Content-Type: application/xml' -X DELETE | xmllint --format -
curl localhost:1026/v1/contextEntities/E1/attribute/B -s -S --header 'Content-Type: application/json' -X DELETE --header 'Accept: application/json' | python -mjson.tool

Query entity (should see 'C' and 'D', but not 'B'):

XML JSON (since release 0.10.0)
curl localhost:1026/v1/contextEntities/E1 -s -S --header 'Content-Type: application/xml' | xmllint --format -
curl localhost:1026/v1/contextEntities/E1 -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' | python -mjson.tool
XML JSON (since release 0.10.0)
<?xml version="1.0"?>
<contextElementResponse>
  <contextElement>
    <entityId type="" isPattern="false">
      <id>E1</id>
    </entityId>
    <contextAttributeList>
      <contextAttribute>
        <name>C</name>
        <type>TC</type>
        <contextValue>3</contextValue>
      </contextAttribute>
      <contextAttribute>
        <name>D</name>
        <type>TD</type>
        <contextValue>4</contextValue>
      </contextAttribute>
    </contextAttributeList>
  </contextElement>
  <statusCode>
    <code>200</code>
    <reasonPhrase>OK</reasonPhrase>
  </statusCode>
</contextElementResponse>
{
    "contextElement": {
        "attributes": [
            {
                "name": "C",
                "type": "TC",
                "value": "3"
            },
            {
                "name": "D",
                "type": "TD",
                "value": "4"
            }
        ],
        "id": "E1",
        "isPattern": "false",
        "type": ""
    },
    "statusCode": {
        "code": "200",
        "reasonPhrase": "OK"
    }
}

Deleting entities

New since version 0.11.0: for older versions you need to use a database procedure to delete entities.

Apart from deleting individual attributes from a given entity (see previous section on that topic), you can also delete an entire entity, including all its attributes with their corresponding metadata. In order to do so, the updateContext operation is used, with DELETE as actionType and with an empty attributeList, as in the following example:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="T" isPattern="false">
        <id>E1</id>
      </entityId>
      <contextAttributeList/>
    </contextElement>
  </contextElementList>
  <updateAction>DELETE</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
   {
      "type": "T",
      "isPattern": "false",
      "id": "E1"
    }
  ],
  "updateAction": "DELETE"
}
EOF

You can also use the following equivalent convenience operation:

XML JSON (since release 0.9.0)
curl localhost:1026/v1/contextEntities/E1 -s -S --header 'Content-Type: application/xml' -X DELETE
curl localhost:1026/v1/contextEntities/E1 -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -X DELETE

Metadata ID for attributes

Since release 0.9.0: new in this release.

Sometimes, you could want to model attributes belonging to an entity which share the same name and type. For example, let's consider an entity "Room1" which has two temperature sensors: one in the ground and other in the wall. We can model this as two instances of the attribute "temperature", one with ID "ground" and the other with the ID "wall". We use the metadata ID for this purpose. Let's illustrate with an example.

First, we create the Room1 entity:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>23.5</contextValue>
          <metadata>
             <contextMetadata>
                <name>ID</name>
                <type>string</type>
                <value>ground</value>
             </contextMetadata>
          </metadata>
        </contextAttribute>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>23.8</contextValue>
          <metadata>
             <contextMetadata>
                <name>ID</name>
                <type>string</type>
                <value>wall</value>
             </contextMetadata>
          </metadata>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>APPEND</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
    {
      "type": "Room",
      "isPattern": "false",
      "id": "Room1",
      "attributes": [
        {
          "name": "temperature",
          "type": "float",
          "value": "23.5",
          "metadatas": [
            {
              "name": "ID",
              "type": "string",
              "value": "ground"
            }
          ]
        },
        {
          "name": "temperature",
          "type": "float",
          "value": "23.8",
          "metadatas": [
          {
            "name": "ID",
            "type": "string",
            "value": "wall"
          }
          ]
        }
      ]
    }
  ],
  "updateAction": "APPEND"
}
EOF

Now, we can query for temperature to get both instances:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <attributeList>
     <attribute>temperature</attribute>
  </attributeList>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
    {
      "type": "Room",
      "isPattern": "false",
      "id": "Room1"
    }
  ],
  "attributes": [
    "temperature"
  ]
}
EOF

We can update an specific instance (e.g. ground), letting the other untouched:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>30</contextValue>
          <metadata>
             <contextMetadata>
                <name>ID</name>
                <type>string</type>
                <value>ground</value>
             </contextMetadata>
          </metadata>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>UPDATE</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
    {
      "type": "Room",
      "isPattern": "false",
      "id": "Room1",
      "attributes": [
      {
        "name": "temperature",
        "type": "float",
        "value": "30",
        "metadatas": [
        {
          "name": "ID",
          "type": "string",
          "value": "ground"
        }
        ]
      }
      ]
    }
  ],
  "updateAction": "UPDATE"
}
EOF

Check it using again queryContext (ground has changed to 30ºC but wall has its initial value of 23.8º C).

To avoid ambiguities, you cannot mix the same attribute with and without ID. The following entity creation will fail:

XML JSON (since release 0.9.0)
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room2</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>23.5</contextValue>
          <metadata>
             <contextMetadata>
                <name>ID</name>
                <type>string</type>
                <value>ground</value>
             </contextMetadata>
          </metadata>
        </contextAttribute>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>23.8</contextValue>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>APPEND</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
    {
      "type": "Room",
      "isPattern": "false",
      "id": "Room1",
      "attributes": [
        {
          "name": "temperature",
          "type": "float",
          "value": "23.5",
          "metadatas": [
            {
              "name": "ID",
              "type": "string",
              "value": "ground"
            }
          ]
        },
        {
          "name": "temperature",
          "type": "float",
          "value": "23.8"
        }
      ]
    }
  ],
  "updateAction": "APPEND"
}
EOF
XML JSON (since release 0.9.0)
 ...
 <statusCode>
   <code>472</code>
   <reasonPhrase>request parameter is invalid/not allowed</reasonPhrase>
   <details>action: APPEND - entity: (Room1, Room) - offending attribute: temperature</details>
 </statusCode>
 ...
...
"statusCode": {
  "code": "472",
  "details": "action: APPEND - entity: (Room1, Room) - offending attribute: temperature",
  "reasonPhrase": "request parameter is invalid/not allowed"
}
...

Finally, you can use also the following convenience operations with attributes using ID metadata:

  • GET /v1/contextEntities/Room1/attributes/temperature/ground: to get an specific attribute identified by ID
  • PUT /v1/contextEntities/Room1/attributes/temperature/ground (using as payload updateContextElementRequest, as described in a previous section).
  • DELETE /v1/contextEntities/Room1/attributes/temperature/ground: to remove an specific attribute identified by ID (see DELETE attribute semantics described in a previous section).

Using empty types

You can use empty types in entities in NGSI9/NGSI10 operations. In fact, convenience operations implicitly use empty types in this way by default (however note that since 0.16.0 you can use the "/type/<type>/id/<id>" pattern instead of "<id>" in convenience operations URLs to specify a type).

Moreover, you can use empty entity types in discover context availability or query context operations. In this case, the absence of type in the query is interpreted as "any type".

For example, let's consider having the following context in Orion Context Broker:

  • Entity 1:
    • ID: Room1
    • Type: Room
  • Entity 2:
    • ID: Room1
    • Type: Space

A discoveryContextAvailability/querycontext using:

    ...
    <entityIdList>
      <entityId type="" isPattern="false">
        <id>Room1</id>
      </entityId>
    </entityIdList>
    ...

will match both Entity 1 and Entity 2.

Release note for 0.17.0 and newer: attribute type is no longer used as part of the attribute identification. Attributes can be created without type in updateContext APPEND. If type is left empty in subsequent updateContext UPDATEs, then the type is not updated and attribute keeps its previous type.

Extending duration

We have seen that registrations and subscriptions (both context and context availability ones) have a duration. The expiration date is calculated using the following formula:

  • expiration = current-time + duration

The behavior of the broker regarding expired elements is the following:

  • For registrations: an expired registration is not taken into account in discoverContextAvailability request processing, but it is still updatable.
  • For subscriptions: an expired subscription is not taken into account to send new notifications based on it, but it is still updatable (using updateContextSubscription/updateContextSubscriptionAvailability) and it can be canceled (using unsubscribeContext/unsubscribeContextAvailability).

Note that Orion Context Broker doesn't remove expired elements from the database, but that they can be easily deleted as described in the administration manual.

Finally, take into account that the expiration is recalculated on updates, not expanded. Let's clarify this with an example. Let's suppose that at 18:30 you do a subscription with PT1H duration (i.e. one hour). Thus, it will expire at 19:30. Next, at 19:00 you do an update using again PT1H as duration. So, that hour period is not added to 19:30 (the previous expiration limit) but added to the 19:00 (the current time). Thus, the new expiration time is 20:00.

Only-type entity registrations using convenience operations

Since release 0.9.0: new in this release.

You can use the NGSI9 "contextEntityTypes" convenience operations to register entity types without an specific ID (whatever it means in the context of your context-based application :). Let's illustrate with an example.

Let's register the "Funny" entity type (note that we are not specifying any entity ID):

(curl localhost:1026/v1/registry/contextEntityTypes/Funny -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) << EOF
<?xml version="1.0"?>
<registerProviderRequest>
   <duration>P1M</duration>
   <providingApplication>http://mysensors.com/Funny</providingApplication>
</registerProviderRequest>
EOF

Now, let's discover on that type:

curl localhost:1026/v1/registry/contextEntityTypes/Funny -s -S --header 'Content-Type: application/xml' | xmllint --format -
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Funny" isPattern="false">
            <id/>
          </entityId>
        </entityIdList>
        <providingApplication>http://mysensors.com/Funny</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>

As you can see, the ID element is empty (it makes sense, as we didn't specify any ID at registration).

Moreover, you can register attributes in these registrations, e.g:

(curl localhost:1026/v1/registry/contextEntityTypes/MoreFunny/attributes/ATT -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) << EOF
<?xml version="1.0"?>
<registerProviderRequest>
   <duration>P1M</duration>
   <providingApplication>http://mysensors.com/Funny</providingApplication>
</registerProviderRequest>
EOF
curl localhost:1026/v1/registry/contextEntityTypes/MoreFunny -s -S --header 'Content-Type: application/xml' | xmllint --format -
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="MoreFunny" isPattern="false">
            <id/>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>ATT</name>
            <type/>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Funny</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>

Structured attribute values

New since version 0.11.0.

Apart from simple strings such as "22.5" or "yellow", you can use complex structures as attribute values. In particular, an attribute can be set to a vector or to a key-value map (usually referred to as an "object") using updateContext (or equivalent convenience operation). These values are retrieved with a queryContext on that attribute (or equivalent convenience operation) and notifyContext notifications sent as a consequence of a NGSI10 subscriptions.

Vector or key-map values correspond directly to JSON vectors and JSON objects, respectively. Thus, the following updateContext request sets the value of attribute "A" to a vector and the value of attribute B to a key-map object (we use UPDATE as actionType, but this can be used at initial entity or attribute creation with APPEND).

(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
 {
   "contextElements": [
       {
           "type": "T1",
           "isPattern": "false",
           "id": "E1",
           "attributes": [
           {
               "name": "A",
               "type": "T",
               "value": [ "22" , 
                          {
                             "x": [ "x1", "x2"], 
                             "y": "3" 
                          }, 
                          [ "z1", "z2" ] 
                        ]
           },
           {
               "name": "B",
               "type": "T",
               "value": {
                  "x": { 
                          "x1": "a", 
                          "x2": "b" 
                  },
                  "y": [ "y1", "y2" ]
               }
           }
           ]
       }
   ],
   "updateAction": "UPDATE"
 }
EOF

In the case of XML, the structure is a bit less "natural" than in JSON, but equivalent:

(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
 <updateContextRequest>
 <contextElementList>
   <contextElement>
     <entityId type="T1" isPattern="false">
       <id>E1</id>
     </entityId>
     <contextAttributeList>
       <contextAttribute>
         <name>A</name>
         <type>T</type>
         <contextValue type="vector">
            <item>22</item>
            <item>
              <x type="vector">
                <item>x1</item>
                <item>x2</item>
              </x>
              <y>3</y>
            </item>
            <item type="vector">
              <item>z1</item>
              <item>z2</item>
            </item>
         </contextValue>
       </contextAttribute>
       <contextAttribute>
         <name>B</name>
         <type>T</type>
         <contextValue>
            <x>
              <x1>a</x1>
              <x2>b</x2>
            </x>
            <y type="vector">
               <item>y1</item>
               <item>y2</item>
            </y>
         </contextValue>
       </contextAttribute>
     </contextAttributeList>
   </contextElement>
 </contextElementList>
 <updateAction>UPDATE</updateAction>
 </updateContextRequest>
EOF

The particular rules applied to XML format are the following ones:

  • Tags not using the "type" attribute equal to "vector" represent key-map object, taking into account the following:
    • Each child tag is considered a key-map pair of the object, whose key is the tag name and the value is the content of the tag. The value can be either a string or a tag (which can represent either a vector or a key-map object).
    • It is not allowed to have 2 child tags with the same name (a parse error is generated in that case).
  • Tags using the "type" attribute equal to "vector" represent a vector, taking into account the following:
    • Each child tag is considered a vector element, whose value is the content of the tag. The value can be either a string or a tag (which can represent either a vector or a key-map object). The name of the tag is not taken into account (vector elements have no name, otherwise the vector wouldn't be an actual vector, but a key-map).
    • In updateContext, you can use whatever name you want for child tags. However, all the child tags must have the same name (otherwise a parse error is generated).
  • Except for "type", XML attributes are not allowed within the sub-tree of an structured value (a parse error is generated as a consequence).

The value of the attribute is stored internally by Orion Context Broker in a format-independent representation. Thus, you can updateContext a structured attribute using JSON, then queryContext that attribute using XML (or vice-versa) and you get the equivalent result. The internal format-independent representation ignores the tag names for vector items set using XML (as described above), so the queryContextResponse/notifyContextRequest in XML uses always a predefined tag name for that: <item> (that may not match the tag name used in the updateContext operation setting that value; in fact, the updateContext operation could have been done with JSON, in which case the tag name for vector items has no sense at all).

Note that in order to align XML/JSON representations, the final "leaf" elements of the structured attribute values after travesing all vectors and key-maps are always considered as strings. String is the "natural" type for elements in XML, but not in JSON (that allow other types, such as integer). Thus, take into account that although you can use for instance a JSON integer as a field value in updates (such as {"x": 3 }), you will retrieve a string in queries and notifications (i.e. { "x": "3" }).

Geolocation capabilities

New since version 0.11.0.

Orion Context Broker has several capabilities related to geolocation that are described in this section. It is strictly required to use MongoDB 2.4 or higher in order to use geolocation features (see requirements section in the installation manual).

Defining location attribute

Entities can have a location, specified by one of its attributes. In order to state which attribute (among all the ones belonging to the entity) defines the location, the "location" metadata is used. For example, the following updateContext request creates the entity "Madrid" (of type "City") with attribute "position" defined as location.

XML JSON
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="City" isPattern="false">
        <id>Madrid</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>position</name>
          <type>coords</type>
          <contextValue>40.418889, -3.691944</contextValue>
          <metadata>
            <contextMetadata>
              <name>location</name>
              <type>string</type>
              <value>WGS84</value>
            </contextMetadata>
          </metadata>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>APPEND</updateAction>
</updateContextRequest>
EOF
(curl localhost:1026/v1/updateContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextElements": [
  {
    "type": "City",
    "isPattern": "false",
    "id": "Madrid",
    "attributes": [
    {
      "name": "position",
      "type": "coords",
      "value": "40.418889, -3.691944",
      "metadatas": [
      {
        "name": "location",
        "type": "string",
        "value": "WGS84"
      }
      ]
    }
    ]
  }
  ],
  "updateAction": "APPEND"
}
EOF

Additional comments:

  • Note that you can use different attributes to specify the location in different entities, e.g. entity "Car1" could be using "position" attribute, while entity "Phone22" could use attribute "coordinates".
  • In order to avoid inconsistencies, only one attribute at a time can be defined as location. If you want to redefine the attribute of an entity used for location, first you have to DELETE it, then APPEND the new one (check the section about adding and removing attributes dynamically).
  • The value of the location metadata is the coordinates system used. Current version only support WGS84 (which is the one used internally by the MongoDB database) but other systems may be added in future versions.
  • The value of the location attribute is a string with two numbers separated by a comma (","): the first number is the latitude and the second is the longitude. Only decimal notation is allowed (e.g. "40.418889"), degree-minute-second notation is not allowed (e.g. "40°44'55''N").

Geo-located queries

Entities location can be used in queryContext (or equivalent convenience operations). To do so, we use the scope element, using "FIWARE::Location" as scopeType and an area specification as scopeValue. The query result includes only the entities located in that area, i.e. context elements belonging to entities not included in the area are not taken into account. Regarding area specification, Orion Context Broker allows the following possibilities:

  • Area internal to a circumference, given its centre and radius.
  • Area external to a circumference, given its centre and radius.
  • Area internal to a polygon (e.g. a terrain zone, a city district, etc.), given its vertices.
  • Area external to a polygon (e.g. a terrain zone, a city district, etc.), given its vertices.
  • Area unions or intersections (e.g. the intersection of a circle and a polygon) are not supported in the current version.

In order to illustrate geo-located queries with polygons, let's consider the following scenario: three entities (A, B and C, of type "Point") have been created in Orion Context Broker, each one in the coordinates shown in the following picture.

Let's consider a query whose scope is the internal area to the square defined by coordinates (0, 0), (0, 6), (6, 6) and (6, 0).

To define a polygon, we use the polygon element which, in sequence, include a vertexList. A vertexList is composed by a list of vertex elements, each one containing a couple of elements (latitude and longitude) that provide the coordinates of the vertex. The result of the query would be A and B.

XML JSON
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
        <entityId type="Point" isPattern="true">
          <id>.*</id>
        </entityId>
  </entityIdList>
  <attributeList>
  </attributeList>
  <restriction>
    <scope>
      <operationScope>
        <scopeType>FIWARE::Location</scopeType>
        <scopeValue>
          <polygon>
            <vertexList>
              <vertex>
                <latitude>0</latitude>
                <longitude>0</longitude>
              </vertex>
              <vertex>
                <latitude>0</latitude>
                <longitude>6</longitude>
              </vertex>
              <vertex>
                <latitude>6</latitude>
                <longitude>6</longitude>
              </vertex>
              <vertex>
                <latitude>6</latitude>
                <longitude>0</longitude>
              </vertex>
            </vertexList>
          </polygon>
        </scopeValue>
      </operationScope>
    </scope>
  </restriction>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
  {
    "type": "Point",
    "isPattern": "true",
    "id": ".*"
  }
  ],
  "restriction": {
    "scopes": [
    {
      "type" : "FIWARE::Location",
      "value" : {
        "polygon": {
          "vertices": [
          {
            "latitude": "0",
            "longitude": "0"
          },
          {
            "latitude": "0",
            "longitude": "6"
          },
          {
            "latitude": "6",
            "longitude": "6"
          },
          {
            "latitude": "6",
            "longitude": "0"
          }
          ]
        }
      }
    }
    ]
  }
}
EOF

Let's consider a query whose scope is the internal area to the rectangle defined by coordinates (3, 3), (3, 8), (11, 8) and (11, 3).

The result of the query would be B and C.

XML JSON
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
        <entityId type="Point" isPattern="true">
          <id>.*</id>
        </entityId>
  </entityIdList>
  <attributeList>
  </attributeList>
  <restriction>
    <scope>
      <operationScope>
        <scopeType>FIWARE::Location</scopeType>
        <scopeValue>
          <polygon>
            <vertexList>
              <vertex>
                <latitude>3</latitude>
                <longitude>3</longitude>
              </vertex>
              <vertex>
                <latitude>3</latitude>
                <longitude>8</longitude>
              </vertex>
              <vertex>
                <latitude>11</latitude>
                <longitude>8</longitude>
              </vertex>
              <vertex>
                <latitude>11</latitude>
                <longitude>3</longitude>
              </vertex>
            </vertexList>
          </polygon>
        </scopeValue>
      </operationScope>
    </scope>
  </restriction>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
  {
    "type": "Point",
    "isPattern": "true",
    "id": ".*"
  }
  ],
  "restriction": {
    "scopes": [
    {
      "type" : "FIWARE::Location",
      "value" : {
        "polygon": {
          "vertices": [
          {
            "latitude": "3",
            "longitude": "3"
          },
          {
            "latitude": "3",
            "longitude": "8"
          },
          {
            "latitude": "11",
            "longitude": "8"
          },
          {
            "latitude": "11",
            "longitude": "3"
          }
          ]
        }
      }
    }
    ]
  }
}
EOF

However, if we consider the query to the external area to that rectangle, the result of the query would be A. To specify that we refer to the area external to the polygon we include the inverted element set to "true".

XML JSON
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
        <entityId type="Point" isPattern="true">
          <id>.*</id>
        </entityId>
  </entityIdList>
  <attributeList>
  </attributeList>
  <restriction>
    <scope>
      <operationScope>
        <scopeType>FIWARE::Location</scopeType>
        <scopeValue>
          <polygon>
            <vertexList>
              <vertex>
                <latitude>3</latitude>
                <longitude>3</longitude>
              </vertex>
              <vertex>
                <latitude>3</latitude>
                <longitude>8</longitude>
              </vertex>
              <vertex>
                <latitude>11</latitude>
                <longitude>8</longitude>
              </vertex>
              <vertex>
                <latitude>11</latitude>
                <longitude>3</longitude>
              </vertex>
            </vertexList>
            <inverted>true</inverted>
          </polygon>
        </scopeValue>
      </operationScope>
    </scope>
  </restriction>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
  {
    "type": "Point",
    "isPattern": "true",
    "id": ".*"
  }
  ],
  "restriction": {
    "scopes": [
    {
      "type" : "FIWARE::Location",
      "value" : {
        "polygon": {
          "vertices": [
          {
            "latitude": "3",
            "longitude": "3"
          },
          {
            "latitude": "3",
            "longitude": "8"
          },
          {
            "latitude": "11",
            "longitude": "8"
          },
          {
            "latitude": "11",
            "longitude": "3"
          }
          ],
          "inverted": "true"
        }
      }
    }
    ]
  }
}
EOF

Let's consider a query whose scope is the internal area to the triangle defined by coordinates (0, 0), (0, 6) and (6, 0).

The result of the query would be A.

XML JSON
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
        <entityId type="Point" isPattern="true">
          <id>.*</id>
        </entityId>
  </entityIdList>
  <attributeList>
  </attributeList>
  <restriction>
    <scope>
      <operationScope>
        <scopeType>FIWARE::Location</scopeType>
        <scopeValue>
          <polygon>
            <vertexList>
              <vertex>
                <latitude>0</latitude>
                <longitude>0</longitude>
              </vertex>
              <vertex>
                <latitude>0</latitude>
                <longitude>6</longitude>
              </vertex>
              <vertex>
                <latitude>6</latitude>
                <longitude>0</longitude>
              </vertex>
            </vertexList>
          </polygon>
        </scopeValue>
      </operationScope>
    </scope>
  </restriction>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
  {
    "type": "Point",
    "isPattern": "true",
    "id": ".*"
  }
  ],
  "restriction": {
    "scopes": [
    {
      "type" : "FIWARE::Location",
      "value" : {
        "polygon": {
          "vertices": [
          {
            "latitude": "0",
            "longitude": "0"
          },
          {
            "latitude": "0",
            "longitude": "6"
          },
          {
            "latitude": "6",
            "longitude": "0"
          }
          ]
        }
      }
    }
    ]
  }
}
EOF

However, if we consider the query to the external area to that triangle (using the inverted element set to "true"), the result of the query would be B and C.

XML JSON
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
        <entityId type="Point" isPattern="true">
          <id>.*</id>
        </entityId>
  </entityIdList>
  <attributeList>
  </attributeList>
  <restriction>
    <scope>
      <operationScope>
        <scopeType>FIWARE::Location</scopeType>
        <scopeValue>
          <polygon>
            <vertexList>
              <vertex>
                <latitude>0</latitude>
                <longitude>0</longitude>
              </vertex>
              <vertex>
                <latitude>0</latitude>
                <longitude>6</longitude>
              </vertex>
              <vertex>
                <latitude>6</latitude>
                <longitude>0</longitude>
              </vertex>
            </vertexList>
            <inverted>true</inverted>
          </polygon>
        </scopeValue>
      </operationScope>
    </scope>
  </restriction>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
  {
    "type": "Point",
    "isPattern": "true",
    "id": ".*"
  }
  ],
  "restriction": {
    "scopes": [
    {
      "type" : "FIWARE::Location",
      "value" : {
        "polygon": {
          "vertices": [
          {
            "latitude": "0",
            "longitude": "0"
          },
          {
            "latitude": "0",
            "longitude": "6"
          },
          {
            "latitude": "6",
            "longitude": "0"
          }
          ],
          "inverted": "true"
        }
      }
    }
    ]
  }
}
EOF

Now, in order to illustrate circle areas, let's consider the following scenario: three entities (representing the cities of Madrid, Alcobendas and Leganes) have been created in Orion Context Broker. The coordinates for Madrid are (40.418889, -3.691944); the coordinates for Alcobendas are (40.533333, -3.633333) and the coordinates for Leganes are (40.316667, -3.75). The distance between Madrid and Alcobendas is around 13.65 km, and the distance between Madrid and Leganes is arround 12.38 km (based on: http://boulter.com/gps/distance/).

Let's consider a query whose scope is inside a radius of 13.5 km (13500 meters) centred in Madrid.

To define a circle, we use the circle element which, in sequence, include a three elements: centerLatitude (the latitude of the circle center), centerLongitude (the longitude of the circle center) and radius (in meters). The result of the query would be Madrid and Leganes.

XML JSON
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
        <entityId type="City" isPattern="true">
          <id>.*</id>
        </entityId>
  </entityIdList>
  <attributeList>
  </attributeList>
  <restriction>
    <scope>
      <operationScope>
        <scopeType>FIWARE::Location</scopeType>
        <scopeValue>
          <circle>
            <centerLatitude>40.418889</centerLatitude>
            <centerLongitude>-3.691944</centerLongitude>
            <radius>13500</radius>
          </circle>
        </scopeValue>
      </operationScope>
    </scope>
  </restriction>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
  {
    "type": "City",
    "isPattern": "true",
    "id": ".*"
  }
  ],
  "restriction": {
    "scopes": [
    {
      "type" : "FIWARE::Location",
      "value" : {
        "circle": {
          "centerLatitude": "40.418889",
          "centerLongitude": "-3.691944",
          "radius": "13500"
        }
      }
    }
    ]
  }
}
EOF

Let's consider a query whose scope is inside a radius of 15 km (15000 meters) centred in Madrid.

The result of the query would be Madrid, Leganes and Alcobendas.

XML JSON
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
        <entityId type="City" isPattern="true">
          <id>.*</id>
        </entityId>
  </entityIdList>
  <attributeList>
  </attributeList>
  <restriction>
    <scope>
      <operationScope>
        <scopeType>FIWARE::Location</scopeType>
        <scopeValue>
          <circle>
            <centerLatitude>40.418889</centerLatitude>
            <centerLongitude>-3.691944</centerLongitude>
            <radius>15000</radius>
          </circle>
        </scopeValue>
      </operationScope>
    </scope>
  </restriction>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
  {
    "type": "City",
    "isPattern": "true",
    "id": ".*"
  }
  ],
  "restriction": {
    "scopes": [
    {
      "type" : "FIWARE::Location",
      "value" : {
        "circle": {
          "centerLatitude": "40.418889",
          "centerLongitude": "-3.691944",
          "radius": "15000"
        }
      }
    }
    ]
  }
}
EOF

Let's consider a query whose scope is outside a radius of 13.5 km (13500 meters) centred in Madrid.

We use the inverted element set to "true". The result of the query would be Alcobendas.

XML JSON
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
        <entityId type="City" isPattern="true">
          <id>.*</id>
        </entityId>
  </entityIdList>
  <attributeList>
  </attributeList>
  <restriction>
    <scope>
      <operationScope>
        <scopeType>FIWARE::Location</scopeType>
        <scopeValue>
          <circle>
            <centerLatitude>40.418889</centerLatitude>
            <centerLongitude>-3.691944</centerLongitude>
            <radius>13500</radius>
            <inverted>true</inverted>
          </circle>
        </scopeValue>
      </operationScope>
    </scope>
  </restriction>
</queryContextRequest>
EOF
(curl localhost:1026/v1/queryContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
  {
    "type": "City",
    "isPattern": "true",
    "id": ".*"
  }
  ],
  "restriction": {
    "scopes": [
    {
      "type" : "FIWARE::Location",
      "value" : {
        "circle": {
          "centerLatitude": "40.418889",
          "centerLongitude": "-3.691944",
          "radius": "13500",
          "inverted": "true"
        }
      }
    }
    ]
  }
}
EOF

Context Broker Federation

Release Note (0.7.0 FIWARE 3.1.2): new since this release.

This section described "push" federation (in the sense notifyContext sent by one Orion instance are processed by other Orion instance). However, since 0.15.0, the registring Context Providers and request forwarding functionality can be used to implement a kind of "pull" federation (in which one Orion instance fowards a query/update to another Orion instance). Note that an importand difference between two approaches is that in the "push" mode all the Orion instances update its local state, while in the "push" approach all the intermediate Orion instances acts as "proxy" without storing the data locally.

Apart from processing updateContext and registerContext (usually issued by a client application) Orion Context Broker can process notifyContextRequest and notifyContextAvailabilityRequest with the same semantics. This opens the door to interesting federation scenarios (one example is the FI-LAB context management platform).

File:Federation.png

Consider the following setup: three context broker instances running in the same machine (of course, this is not a requirement but makes things simpler to test this feature), in ports 1030, 1031 and 1032 respectively and using different databases (named A, B and C to be brief). Let's start each instance (run each command in a separate terminal):

contextBroker -fg -port 1030 -db orion1030
contextBroker -fg -port 1031 -db orion1031
contextBroker -fg -port 1032 -db orion1032

Next, let's send a subscribeContext to A (to make B subscribe to updates made in A). Note that the URL used in the reference has to be "/v1/notifyContext":

(curl localhost:1030/v1/subscribeContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0"?>
<subscribeContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <reference>http://localhost:1031/v1/notifyContext</reference>
  <duration>P1M</duration>
  <notifyConditions>
    <notifyCondition>
      <type>ONCHANGE</type>
      <condValueList>
        <condValue>temperature</condValue>
      </condValueList>
    </notifyCondition>
  </notifyConditions>
  <throttling>PT5S</throttling>
</subscribeContextRequest>
EOF

Next, let's send a subscribeContext to B (to make C subscribe to updates made in B). The subscription is basically the same, only the port in the curl line and reference elements are different.

(curl localhost:1031/v1/subscribeContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0"?>
<subscribeContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <reference>http://localhost:1032/v1/notifyContext</reference>
  <duration>P1M</duration>
  <notifyConditions>
    <notifyCondition>
      <type>ONCHANGE</type>
      <condValueList>
        <condValue>temperature</condValue>
      </condValueList>
    </notifyCondition>
  </notifyConditions>
  <throttling>PT5S</throttling>
</subscribeContextRequest>
EOF

Now, let's create an entity in context broker A.

(curl localhost:1030/v1/updateContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<updateContextRequest>
  <contextElementList>
    <contextElement>
      <entityId type="Room" isPattern="false">
        <id>Room1</id>
      </entityId>
      <contextAttributeList>
        <contextAttribute>
          <name>temperature</name>
          <type>float</type>
          <contextValue>23</contextValue>
        </contextAttribute>
      </contextAttributeList>
    </contextElement>
  </contextElementList>
  <updateAction>APPEND</updateAction>
</updateContextRequest>
EOF

Given the subscriptions in place, a notifyContextRequest is automatically sent from A to B. That event at B causes in sequence a notifyContextRequest to be sent to C. So, at the end, the creation of an entity in A causes the creation of the same entity (with the same attribute values) in C. You can check it by doing a queryContext to C:

(curl localhost:1032/v1/queryContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<queryContextRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <attributeList/>
</queryContextRequest>
EOF
<?xml version="1.0"?>
<queryContextResponse>
  <contextResponseList>
    <contextElementResponse>
      <contextElement>
        <entityId type="Room" isPattern="false">
          <id>Room1</id>
        </entityId>
        <contextAttributeList>
          <contextAttribute>
            <name>temperature</name>
            <type>float</type>
            <contextValue>23</contextValue>
          </contextAttribute>
        </contextAttributeList>
      </contextElement>
      <statusCode>
        <code>200</code>
        <reasonPhrase>OK</reasonPhrase>
      </statusCode>
    </contextElementResponse>
  </contextResponseList>
</queryContextResponse>

In the current context broker version, the semantics of nofityContextRequest are the same that updateContext APPEND or, if the context element already exist, the semantics of updateContext UPDATE. Thus, federation doesn't provide exact mirroring: a updateContext DELETE to one context broker will not produce the same effect in the federated context broker.

This mechanism works similarly with registerContext and subscribeContextAvailability. In this case, the URL for the reference element is "/v1/registry/notifyContextAvailability".

Mixing JSON and XML requests

Release Note (0.9.0 FIWARE 3.2.2): new since this release (as was the one in which JSON was introduced).

You can use XML and JSON at the same time. For example, you can do a registerContext in XML for a given entity, the discoverContextAvailability in JSON to retrieve it.

Cross-format notifications

New for NGSI10 since 0.13.0. Also for NGIS9 since 0.21.0

Default behavior for NGSI10/9 notifications is to use the same format (XML or JSON) that was used at creation time of the subscription (/v1/subscribeContext or /v1/registr/subscribeContextAvailability). If another format is desired, the URL attribute "notifyFormat" must be used at subscription time:

(curl 'localhost:1026/v1/subscribeContext?notifyFormat=json' -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format -) <<EOF
<?xml version="1.0"?>
<subscribeContextRequest>
   ...
</subscribeContextRequest>
EOF

The above example shows a subscribeContext done using XML, that will receive notifications in JSON.

Currently, cross-format notifications are not supported in NGSI9 subscribeContextAvailability.

HTTP and NGSI response codes

Two independent response codes are being considered in the API responses: one "internal" at NGSI level (i.e. encoded in the REST HTTP response payload) and other "external" at HTTP level (the HTTP response code itself). Note that this manual focuses on the NGSI aspects of the API, thus we always assume in this documentation (unless otherwise noted) that HTTP code is "200 OK".

In order to illustrate the existence of both codes and their independence, let's consider a queryContext operation on a non-existing entity (e.g. "foo"). Note the -v flag in the curl command, in order to print the HTTP response codes and headers:

# curl localhost:1026/v1/contextEntities/foo -s -S --header 'Content-Type: application/xml' -v | xmllint --format -
* About to connect() to localhost port 1026 (#0)
*   Trying ::1... connected
* Connected to localhost (::1) port 1026 (#0)
> GET /v1/contextEntities/foo HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.13.1.0 zlib/1.2.3 libidn/1.18 libssh2/1.2.2
> Host: localhost:1026
> Accept: */*
> Content-Type: application/xml
>
< HTTP/1.1 200 OK
< Content-Length: 316
< Content-Type: application/xml
< Date: Mon, 31 Mar 2014 10:13:45 GMT
<
{ [data not shown]
* Connection #0 to host localhost left intact
* Closing connection #0
<?xml version="1.0"?>
<contextElementResponse>
  <contextElement>
    <entityId type="" isPattern="false">
      <id>foo</id>
    </entityId>
  </contextElement>
  <statusCode>
    <code>404</code>
    <reasonPhrase>No context element found</reasonPhrase>
    <details>Entity id: 'foo'</details>
  </statusCode>
</contextElementResponse>

Note that in this case the NGSI response code is "404 No context element found" while the HTTP is "200 OK". Thus, in other words, the communication at HTTP level was ok, although an error condition (the entity doesn't exist in Orion Context Broker database) happened at the NGSI level.

The following example shows a case of an HTTP level problem, due to a client attempting to get the response in a MIME type not supported by Orion (in this case "text/plain"). In this case, an HTTP response code "406 Not Acceptable" is generated.

# curl localhost:1026/v1/contextEntities/foo -s -S --header 'Accept: text/plain' -v | xmllint --format -
* About to connect() to localhost port 1026 (#0)
*   Trying ::1... connected
* Connected to localhost (::1) port 1026 (#0)
> GET /v1/contextEntities/foo HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.13.1.0 zlib/1.2.3 libidn/1.18 libssh2/1.2.2
> Host: localhost:1026
> Accept: text/plain
>
< HTTP/1.1 406 Not Acceptable
< Content-Length: 196
< Content-Type: application/xml
< Date: Mon, 31 Mar 2014 10:16:16 GMT
<
{ [data not shown]
* Connection #0 to host localhost left intact
* Closing connection #0
<?xml version="1.0"?>
<orionError>
  <code>406</code>
  <reasonPhrase>Not Acceptable</reasonPhrase>
  <details>acceptable types: 'application/xml' but Accept header in request was: 'text/plain'</details>
</orionError>

Security considerations

Orion Context Broker doesn't implement any access control mechanism, i.e. any application can issue any request on it as long as it has connectivity to the host and port where the broker is running. However, access control are provided by other mechanisms, as the access control framework provided by FIWARE GEs.

Since 0.12.0: Orion Context Broker supports HTTPS, using the -https options (which in addition needs the -key and -cert options, to especify the files containing the private key and certificates for the server, respectively). Check the command line options section in the administration manual for details. Note that current Orion version cannot run in both HTTP and HTTPS at the same time, i.e. using -https disables HTTP.

Since 0.13.0: Apart from using HTTPS in the API server exported by Orion, you can also use HTTPS in notifications. In order to do so:

  • You have to use the "https" protocol schema in URL in you reference element in subscribeContext or subscribeContextAvailability subscriptions, e.g.
...
    <reference>https://mymachime.example.com:1028/notify</reference>
...
  • You have to use Rush as relayer (as the HTTPS encoding is implemented in Rush). See how to run Orion using Rush for additional information on this topic.

Known limitations

Attribute values

Although NGSI specifies that the value for attributes is xsd:any (which basically means "anything"), the current Orion Context Broker version only allows plain strings. E.g:

...
  <contextValue>This is a nice string</contextValue>
...

That use to be the case in almost any situation, so you probably don't have to worry about this. However, take into account that you should avoid using the following attribute values in updateContext:

  • Strings that could be interpreted as XML (either well-formed or wrong-formed), e.g:
...
  <contextValue><tag>value</tag></contextValue>
...
  <contextValue>value</tag></contextValue>
...
  • The escaped version of the above, e.g.
...
  <contextValue>&lt;tag&gt;value&lt;/tag&gt;</contextValue>
...
  <contextValue>value&lt;/tag&gt;</contextValue>
...

Using the above patterns for attribute values may lead to unexpected behaviour in the broker.

Request maximum size

The current maximum request size in Orion Context Broker is 1 MB. This limit should suffice the most of the use cases and, at the same time, avoids denial of service due to too large requests. If you don't take this limitation into account, you will get messages such the following ones:

<?xml version="1.0"?>
<queryContextResponse>
  <errorCode>
    <code>413</code>
    <reasonPhrase>Payload Too Large</reasonPhrase>
    <details>payload size: 1500748</details>
  </errorCode>
</queryContextResponse>

Or, if you are sending a huge request, this one:

<html>
  <head><title>Internal server error</title></head>
  <body>Some programmer needs to study the manual more carefully.</body>
</html>

(Please ignore the "Some programmer needs to study the manual more carefully" text. Developers of the HTTP library in which Orion Context Broker is based seem to be funny guys :) :)

If you find this 1MB limit too coarse, send us an email so we can consider your feedback in future releases.

Notification maximum size

Notification maximum size is set to 8MB in Orion Context Broker version 0.9.1. Larger notifications will not be sent by context broker and you will get the following trace in the log file:

HTTP request to send is too large: N bytes

where N is the number of bytes of the too large notification.

Cross-format notifications

This limitation was overcome for NGSI10 notifications in version 0.13.0. It is still being a limitation for NGSI9 notifications.

Orion Context Broker 0.9.0 introduced JSON, an alternative to XML for requests and responses. However, note that if you do a subscribeContext/subscribeContextAvailability in a given format, the notifyContextRequest/notifyContextAvailabilityRequest messages sent as a consequence of that subscription are sent in the same format. For example, if you do subscribeContext using XML, you will get notifyContextRequest in XML. Currently, it is no possible to receive notifications in the opposite format (in this case, JSON).

Content-Length header is required

Orion Context Broker expects always a Content-Length header in all client requests, otherwise the client will receive a "411 Length Required" response. This is due to the way the underlying HTTP library (microhttpd) works, see details in this email thread in the microhttpd mailing list.

Authorization

Authorization is provided by FIWARE PEP Proxy GE. At the present moment, there are two GE implementantions (GEis) that can work with Orion Context Broker:

In the above links you will find the documentation about how to use both GEis.

Deprecated Features

Deprecated features are features that Orion stills support but that are not mantained or evolved any longer. In particular:

  • Bugs or issues related with deprecated features and not affecting any other feature are not addressed (they are closed in github.com as soon as they are spotted).
  • Deprecated functionality is eventually removed from Orion. Thus you are strongly encouraged to change your implementations using Orion in order not rely on deprecated functionality.

Configuration Manager role

Deprecated since 0.21.0

The Orion Context Broker can run in "Configuration Management Role", which is a work-mode specially thought for scenarios in which the broker plays the role of Configuration Manager in the FIWARE platform (as you would probably have guessed :).

This is done using the "-ngsi9", "-fwdHost" and "-fwdPort" command line options at start time (see the administration manual to know how to configure the broker at start time) as described below:

  • Using "-ngsi9" disables NGSI10, i.e. any attempt to use NGSI10 operations will result in an error.
  • Register Context requests are forwarded to the host and port specified by "-fwdHost" and "-fwdPort". In other words, each time the Orion Context Broker receives a registerContext request (for creating new registrations or updating existing ones) a registerContext (with the same registration information) is sent to fwdHost/fwdPort (where usually another instance of Orion Context Broker runs, configured in normal mode). Note that the registrationID in Orion "ConfMan" and in Orion "normal" will be different, but it is not a problem as Orion "ConfMan" keeps an ID mapping. Using "-fwdPort 0" (or not using the option at all) disables registerContext forwarding.

Note that both "-ngsi9" and "-fwdHost/-fwdPort" are independent, e.g. you can enable registerContext forwarding using "-fwdHost/-fwdPort" without disabling NGSI10 with "-ngsi9". However, to get the proper behaviour of a Configuration Manager you need to use all three options.

Associations

Deprecated since 0.21.0

Associations allow to configure relationship between context elements. For example, let's suppose that you have completed the steps of the tutorial about registerContext operation and that you want to create an association between adjacent room temperatures, so you can specify that the temperature of a given room is the temperature of your neighbour in right direction. Let's name this association "right_neighbour". The knowledge of this relationship is useful for building an application to detect "heat waves" that propagates along the rooms of a building.

Associations are created using registerContext operations, but instead of a "normal" contextRegistration element (with entityIdList and contextRegistrationAttributeList) we use a registrationMetadata element:

XML JSON
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
  <registerContextRequest>
    <contextRegistrationList>
      <contextRegistration>
        <registrationMetadata>
          <contextMetadata>
            <name>right_neighbour</name>
            <type>Association</type>
              <value>
                <entityAssociation>
                  <sourceEntityId type="Room" isPattern="false">
                    <id>Room1</id>
                  </sourceEntityId>
                  <targetEntityId type="Room" isPattern="false">
                    <id>Room2</id>
                  </targetEntityId>
                </entityAssociation>
                <attributeAssociationList>
                  <attributeAssociation>
                    <sourceAttribute>temperature</sourceAttribute>
                    <targetAttribute>temperature</targetAttribute>
                  </attributeAssociation>
              </attributeAssociationList>
            </value>
          </contextMetadata>
        </registrationMetadata>
        <providingApplication>http://www.fi-ware.eu/NGSI/association</providingApplication>
      </contextRegistration>
    </contextRegistrationList>
    <duration>P1M</duration>
</registerContextRequest>
EOF
(curl localhost:1026/v1/registry/registerContext -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "contextRegistrations": [
    {
      "metadatas": [
        {
          "name": "right_neighbour",
          "type": "Association",
          "value": {
            "source": {
              "id": "Room1",
              "type": "Room",
              "isPattern": "false"
            },
            "target": {
              "id": "Room2",
              "type": "Room",
              "isPattern": "false"
            },
            "attributeAssociations": [
              {
                "source": "temperature",
                "target": "temperature"
              }
            ]
          }
        }
      ],
      "providingApplication": "http://www.fi-ware.eu/NGSI/association"
    }
  ],
  "duration": "P1M",
  "registrationId": ""
}
EOF

From version 0.13.0, the XML tags for associations (attributeAssociationList and attributeAssociation) start with lowercase 'a'.


The elements within registrationMetadata are the following:

  • A name for the association ("right_neighbour" in our case)
  • A type, whose value must be "Association"
  • The entityAssociation element, that specifies the source and target of the association at entity level. In this case, the target is Room2 and the source is Room1. In our example, both target and source have the same type ("Room") but you can also associate entities of different types, e.g. a Car associated to a garage Room.
  • The attributeAssociationList which defines the attribute associations. In this case, we are using a "temperature" (of source Room) to "temperature" (of target Room) association. Although in the example the associated attributes have the same name, this is not mandatory.

Note that although it doesn't make sense to specify a providing application for associations (as they are links between context elements, but no context elements themselves) we have to include one in the request to conform with NGSI specification. We use http://www.fi-ware.eu/NGSI/association by convention.

Associations are like normal registrations, e.g. they have an expiration and a registration ID that is returned in the response:

XML JSON
<registerContextResponse>
  <registrationId>51c45dafe955db1d6e22c729</registrationId>
  <duration>P1M</duration>
</registerContextResponse>
{
    "duration": "P1M",
    "registrationId": "51c45dafe955db1d6e22c729"
}

In order to navigate associations we use the usual discoverContextAvailability operation, but adding a scope restriction to specify that we are looking for associated targets and source. For example, if we want to discover the sources of Room2 temperature we will use the following request:

XML JSON
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<discoverContextAvailabilityRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room2</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>temperature</attribute>
  </attributeList>
  <restriction>
    <scope>
      <operationScope>
        <scopeType>IncludeAssociations</scopeType>
        <scopeValue>SOURCES</scopeValue>
      </operationScope>
    </scope>
  </restriction>
</discoverContextAvailabilityRequest>
EOF
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
    {
      "type": "Room",
      "isPattern": "false",
      "id": "Room2"
    }
  ],
  "attributes": [
    "temperature"
  ],
  "restriction": {
    "scopes": [
      {
        "type" : "IncludeAssociations",
        "value" : "SOURCES"
      }
    ]
  }
}
EOF

The response is as follows:

XML JSON
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Room" isPattern="false">
            <id>Room1</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>temperature</name>
            <type>float</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Rooms</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
    <contextRegistrationResponse>
      <contextRegistration>
        <registrationMetadata>
          <contextMetadata>
            <name>right_neighbour</name>
            <type>Association</type>
            <value>
              <entityAssociation>
                <sourceEntityId type="Room" isPattern="false">
                  <id>Room1</id>
                </sourceEntityId>
                <targetEntityId type="Room" isPattern="false">
                  <id>Room2</id>
                </targetEntityId>
              </entityAssociation>
              <attributeAssociationList>
                <attributeAssociation>
                  <sourceAttribute>temperature</sourceAttribute>
                  <targetAttribute>temperature</targetAttribute>
                </attributeAssociation>
              </attributeAssociationList>
            </value>
          </contextMetadata>
        </registrationMetadata>
        <providingApplication>http://www.fi-ware.eu/NGSI/association</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>

{
  "contextRegistrationResponses": [
    {
      "contextRegistration": {
        "attributes": [
          {
            "isDomain": "false",
            "name": "temperature",
            "type": "float"
          }
        ],
        "entities": [
          {
            "id": "Room1",
            "isPattern": "false",
            "type": "Room"
          }
        ],
        "providingApplication": "http://mysensors.com/Rooms"
      }
    },
    {
      "contextRegistration": {
        "metadatas": [
          {
            "name": "right_neighbour",
            "type": "Association",
            "value": {
              "attributeAssociations": [
                {
                  "source": "measurement",
                  "target": "temperature"
                }
              ],
              "entities": {
                "source": {
                  "id": "Room1",
                  "isPattern": "false",
                  "type": "Room"
                },
                "target": {
                  "id": "Room2",
                  "isPattern": "false",
                  "type": "Room"
                }
                }
            }
          }
        ],
        "providingApplication": "http://www.fi-ware.eu/NGSI/association"
      }
    }
  ]
}

The response includes the source of the association and the association itself (in its corresponding registrationMetadata element).

If we want to discover in the opposite way, i.e. the targets of Room1 temperature we use the following request:

XML JSON
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<discoverContextAvailabilityRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>temperature</attribute>
  </attributeList>
  <restriction>
    <scope>
      <operationScope>
        <scopeType>IncludeAssociations</scopeType>
        <scopeValue>TARGETS</scopeValue>
      </operationScope>
    </scope>
  </restriction>
</discoverContextAvailabilityRequest>
EOF
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
    {
      "type": "Room",
      "isPattern": "false",
      "id": "Room1"
    }
  ],
  "attributes": [
    "temperature"
  ],
  "restriction": {
    "scopes": [
      {
        "type" : "IncludeAssociations",
        "value" : "TARGETS"
      }
    ]
  }
}
EOF

and in this case we will get the target of the association and the association itself (in its corresponding registrationMetadata element).

XML JSON
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Room" isPattern="false">
            <id>Room2</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>temperature</name>
            <type>float</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Rooms</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
    <contextRegistrationResponse>
      <contextRegistration>
        <registrationMetadata>
          <contextMetadata>
            <name>right_neighbour</name>
            <type>Association</type>
            <value>
              <entityAssociation>
                <sourceEntityId type="Room" isPattern="false">
                  <id>Room1</id>
                </sourceEntityId>
                <targetEntityId type="Room" isPattern="false">
                  <id>Room2</id>
                </targetEntityId>
              </entityAssociation>
              <attributeAssociationList>
                <attributeAssociation>
                  <sourceAttribute>temperature</sourceAttribute>
                  <targetAttribute>temperature</targetAttribute>
                </attributeAssociation>
              </attributeAssociationList>
            </value>
          </contextMetadata>
        </registrationMetadata>
        <providingApplication>http://www.fi-ware.eu/NGSI/association</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>

{
  "contextRegistrationResponses": [
    {
      "contextRegistration": {
        "attributes": [
          {
            "isDomain": "false",
            "name": "temperature",
            "type": "float"
          }
        ],
        "entities": [
          {
            "id": "Room2",
            "isPattern": "false",
            "type": "Room"
          }
        ],
        "providingApplication": "http://mysensors.com/Rooms"
      }
    },
    {
      "contextRegistration": {
        "metadatas": [
          {
            "name": "right_neighbour",
            "type": "Association",
            "value": {
              "attributeAssociations": [
                {
                  "source": "measurement",
                  "target": "temperature"
                }
              ],
              "entities": {
                "source": {
                  "id": "Room1",
                  "isPattern": "false",
                  "type": "Room"
                },
                "target": {
                  "id": "Room2",
                  "isPattern": "false",
                  "type": "Room"
                }
                }
            }
          }
        ],
        "providingApplication": "http://www.fi-ware.eu/NGSI/association"
      }
    }
  ]
}

Just to compare, have a look at what happens with the discoverContextAvailability request but in a "conventional" way, i.e. without the scope element:

XML JSON
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/xml' -d @- | xmllint --format - ) <<EOF
<?xml version="1.0"?>
<discoverContextAvailabilityRequest>
  <entityIdList>
    <entityId type="Room" isPattern="false">
      <id>Room1</id>
    </entityId>
  </entityIdList>
  <attributeList>
    <attribute>temperature</attribute>
  </attributeList>
</discoverContextAvailabilityRequest>
EOF
(curl localhost:1026/v1/registry/discoverContextAvailability -s -S --header 'Content-Type: application/json' --header 'Accept: application/json' -d @- | python -mjson.tool) <<EOF
{
  "entities": [
    {
      "type": "Room",
      "isPattern": "false",
      "id": "Room1"
    }
  ],
  "attributes": [
    "temperature"
  ]
}
EOF

In this case, the response provides the Room1 information and no association metadata is included (this is the usual behaviour, as described previously in this document):

XML JSON
<?xml version="1.0"?>
<discoverContextAvailabilityResponse>
  <contextRegistrationResponseList>
    <contextRegistrationResponse>
      <contextRegistration>
        <entityIdList>
          <entityId type="Room" isPattern="false">
            <id>Room1</id>
          </entityId>
        </entityIdList>
        <contextRegistrationAttributeList>
          <contextRegistrationAttribute>
            <name>temperature</name>
            <type>float</type>
            <isDomain>false</isDomain>
          </contextRegistrationAttribute>
        </contextRegistrationAttributeList>
        <providingApplication>http://mysensors.com/Rooms</providingApplication>
      </contextRegistration>
    </contextRegistrationResponse>
  </contextRegistrationResponseList>
</discoverContextAvailabilityResponse>

{
  "contextRegistrationResponses": [
    {
      "contextRegistration": {
        "attributes": [
          {
            "isDomain": "false",
            "name": "temperature",
            "type": "float"
          }
        ],
        "entities": [
          {
            "id": "Room1",
            "isPattern": "false",
            "type": "Room"
          }
        ],
        "providingApplication": "http://mysensors.com/Rooms"
      }
    }
  ]
}

An important use case for associations in the FIWARE platform is the one in which the IoT Broker GE works in combination with Orion Context Broker acting in Configuration Manager role. In this case, the IoT Broker uses associations to create things-device associations in the Orion Context Broker (e.g. things are Rooms and devices are sensors in that rooms). Have a look at the association extension definition for more detail on this.

Release Note (0.5.0 FIWARE 2.3.3): associations are a beta feature introduced in this version. Thus, it is limited and doesn't provide all the functionality described in the association extension definition. In particular:

  • Entity-Entity "pure" associations (i.e. without specifying particular attribute-attribute associations) has not been tested.
  • More than one element in the attributeAssociationList in registerContext has not been tested.
  • Recursive associations have not been tested.
  • We have tested association on entities/attributes registered using standard operations. We haven't tested using convenience operations.

FI-LAB context management platform

The content of this section has been moved to: https://forge.fiware.org/plugins/mediawiki/wiki/fiware/index.php/FIWARE_Lab_Context_Management_Platform

Additional information and resources

This document provides a comprehensive description of Orion Context Broker usage scenarios and capabilities. Thus, it provides all the information you need to build context-based applications using Orion Context Broker. However, if you want to expand your knowledge on the matter you can check the following resources:

  • Installation and Administration Manual. It describes administration aspects of Orion Context Broker, as well as sanity check and diagnosis procedures.
  • Orion Context Broker webminar materials. You can access the last webminar presentation and video recording. They provide an introduction to basic context management context, the NGSI API and the role of Orion Context Broker in the FIWARE architecture. In addition, the video includes a live demonstration of the broker in real operation.
  • Page for Orion-based Publish/Subscribe Context Broker GE in the FIWARE catalogue. These pages provide links to the documentation and software packages, along with information about the instances installed in the FIWARE testbed for each Use Case project and the terms and conditions under which the GEis can be used.
  • The specification of the FIWARE NGSI10 and NGSI9 RESTful API specifications. These are the "formal" NGSI API specifications used by all NGSI-based GEs in FIWARE (as the Orion Context Broker). In addition, you can have a look at the NGSI OMA specification in which FIWARE specifications are based. The FIWARE NGSI binding XSD release 1.0 can be found in this file.

Sample code

This section contains code snippets (most of them contributed by external developers) that can be used as examples for programming with Orion Context Broker in different technologies. Note that Orion Context Broker evolves with time so it could happen that the code examples get obsolete in some moment (the publication date is provides as reference).

Java

Thanks to Kurento (http://www.kurento.org) a Java library that shows how to access to Orion Context Broker: https://github.com/KurentoLegacy/kmf-orion-connector/tree/develop (published June 2014).

Thanks to Alejandro Villamarin (published around October 2013):

   import com.sun.jersey.api.client.Client;
   import com.sun.jersey.api.client.ClientResponse;
   import com.sun.jersey.api.client.WebResource;

   //Test the Orion Broker, query for Room
   String urlString = "http://orion.lab.fiware.org:1026/v1/queryContext";
   String XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + 
		"<queryContextRequest>" + 
		"<entityIdList>" + 
		"<entityId type=\"Room\" isPattern=\"true\">" + 
		"<id>Room.*</id>" + 
		"</entityId>" +
		"</entityIdList>" + 
		"<attributeList>" + 
		"<attribute>temperature</attribute>" + 
		"</attributeList>" + 
		"</queryContextRequest>";      
   try { 	  
      Client client = Client.create();
      WebResource webResource = client.resource(urlString);
      ClientResponse response = webResource.type("application/xml").header("X-Auth-Token", "your_auth_token").post(ClientResponse.class, XML);
   
      if (response.getStatus() != 200) {
         System.err.println(response.getEntity(String.class));
  	 throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
      }
   
      System.out.println("Output from Server .... \n");
      String output = response.getEntity(String.class);
      System.out.println(output);
   
   } 
   catch (Exception e) {
      System.err.println("Failed. Reason is " + e.getMessage());
   }

JavaScript

Using JQuery AJAX, thanks to Marco Vereda Manchego (original post) (published around October 2013):

 function capture_sensor_data(){  
 var contentTypeRequest = $.ajax({  
      url: 'http://orion.lab.fiware.org:1026/v1/queryContext',  
      data: '<?xml version="1.0" encoding="UTF-8"?>  
                <queryContextRequest>  
                     <entityIdList>  
                          <entityId type = "Sensor" isPattern="true">  
                               <id>urn:smartsantander:testbed:.*</id>   
                          </entityId>   
                     </entityIdList>   
                     <attributeList>   
                          <attribute>Latitud</attribute>  
                          <attribute>Longitud</attribute>  
                     </attributeList>       
                </queryContextRequest>  ',  
      type: 'POST',  
      dataType: 'xml',  
      contentType: 'application/xml',  
      headers: { 'X-Auth-Token' :   
           'you_auth_token'}  
      });  
      contentTypeRequest.done(function(xml){   
           var latitud = new Array();  
           var longitud = new Array();  
           var i = 0;       
           var len = $(xml).find("contextAttribute").children().size();  
           $(xml).find('contextAttribute').each(function(){  
                     if (  $(this).find('type').text() == "urn:x-ogc:def:phenomenon:IDAS:1.0:latitude"  
                           && $(this).find('contextValue').text() != "" )  
                     {   
                          latitud[i] = $(this).find('contextValue').text(); i=i+1;  
                     }  
                     if ($(this).find('type').text() == "urn:x-ogc:def:phenomenon:IDAS:1.0:longitude"  
                            && $(this).find('contextValue').text() != ""       )  
                     {   
                          longitud[i] = $(this).find('contextValue').text();   
                     }  
                                                                  });  
           for (var j=0; j<i; j++){                 
                console.log("DEBUG :" + latitud[j] + "," + longitud[j]);                 
           }                      
      });                 
      contentTypeRequest.fail(function(jqXHR, textStatus){     
           console.log( "DEBUG :  Ajax request failed... (" + textStatus + ' - ' + jqXHR.responseText + ")." );  
      });  
      contentTypeRequest.always(function(jqXHR, textStatus){       });  
 }

Arduino

Thanks to Enrique Hernandez Zurita, using Orion 0.11.0 and 0.12.0 (published around June 2014):

#include <SPI.h>
#include <WiFi.h>
#include <WString.h>
char ssid[] = "SSID"; 
char pass[] = "CONTRASEÑA";    
          
int status = WL_IDLE_STATUS;
IPAddress server(130,206,82,115);
WiFiClient client;
String line="";
int value=0;
int led=9;

void setup() {
  
    pinMode(led,OUTPUT);
    Serial.begin(9600); 
    while (!Serial) {  ;
    }
    if (WiFi.status() == WL_NO_SHIELD) {

    Serial.println("WiFi shield not present"); 
    while(true);
    } 
    while (status != WL_CONNECTED) { 
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid); 
    status = WiFi.begin(ssid, pass);
    delay(5000); 
    } 
    Serial.println("Connected to wifi");
    Serial.println("\nStarting connection to server..."); 
    
    if (client.connect(server, 1026)) {
    Serial.println("connected to server");
    client.println("POST /NGSI10/queryContext HTTP/1.1");
    client.println("Host: 130.206.82.115:1026");
    client.println("User-Agent: Arduino/1.1");
    client.println("Connection: close");
    client.println("Content-Type: application/xml");
    client.print("Content-Length: ");
    client.println("227");
    client.println();
    client.println("<?xml version=\"1.0\" encoding=\"UFT-8\"?>");
    client.println("<queryContextRequest>");
    client.println("<entityIdList>");
    client.println("<entityId type=\"UPCT:ACTUATOR\" isPattern=\"false\">");
    client.println("<id>UPCT:ACTUATOR:1</id>");
    client.println("</entityId>");
    client.println("</entityIdList>");
    client.println("<attributeList/>");
    client.println("</queryContextRequest>");
    client.println();
    
    Serial.println("XML Sent");
    
    }else{Serial.println("Impossible to connect");}
}

void loop() {
    if(value=1){digitalWrite(led, HIGH);}
  
    while (client.available()) {
    char c = client.read();
    Serial.write(c);
    line=line + c;
        if(c==10){
        value=searchValue(line,value);
        line="";
        }
     }
  
     if (!client.connected()) {
     Serial.println();
     Serial.println("disconnecting from server.");
     Serial.println(value);
     client.stop();
     while(true);
     }
}


int searchValue(String s,int i) {
  int beginning,ending;
  String val;
  beginning=s.indexOf('>');
  ending=s.indexOf('<', beginning+1);
    if(s.startsWith("contextValue",s.indexOf('<')+1)){
    val =s.substring(beginning+1,ending);
    return val.toInt();
    }else{return i;}
}
Personal tools
Create a book