Consumer Examples
Now, let’s assume, you want to use the data of the just created offering with your new parking app. Using the BIG IoT Consumer Lib, it is very easy to make your service use the BIG IoT Marketplace to query relevant data sources and access them.
The Consumer Lib offers the following main functionalities:
- Discovering offerings at the Marketplace according to a search specification
- Subscribing to offerings
- Accessing offerings in per request or in a continuous fashion
The Java code of this example available here.
Create a consumer object
Creating the lifecycle object for consumer operation is very similar to the provider case, described above. That’s why we don’t want to repeat the explanation.
To get started with the Consumer Lib, you have to create an instance of the Consumer
class. The constructor requires your specific consumer ID and a marketplace URI.
To authenticate with the marketplace, you have to use the authenticate method provided by the consumer
object. This method requires the marketplace API key and a consumer certificate, which you both received when you registered on the BIG IoT Marketplace portal.
Consumer consumer = Consumer
.create(
/*your provider id*/, "https://market.big-iot.org")
.authenticate(/*your provider secret*/);
Like for the instantiation of providers, there is also the option for a properties file based initialization of a provider.
Consumer consumer = Consumer
.create("example.properties")
.authenticate();
If you want to connect to a different marketplace, just create another consumer
object using another marketplace URL.
Query for offerings
Now, that you are authenticated at the marketplace, you can search for relevant parking sensor data to feed into your service. To do that, you query the marketplace using the OfferingQuery
object. The query gets constructed using a builder pattern which first, creates the empty OfferingQuery
object that is completed with additional query filters, such as a specific search region, a desired accounting type, a maximum price, etc. The marketplace will return all matching offerings for this query. In this example, we search for everything what is possible and therefore possibly missing other offerings which are not so similar. Of course, you can reorder and omit attributes. Removing search parameters widens the query.
OfferingQuery query = OfferingQuery
.create("DemoParkingQuery")
.withName("Demo Parking Query")
.withCategory("urn:big-iot:ParkingSpaceCategory")
.withTimePeriod(
TimePeriod.create(new DateTime("2018-01-01T0:00Z"), DateTime.now()))
.inRegion(
BoundingBox.create(Location.create(40.0,8.0),Location.create(45.0,12.0)))
.addInputData("schema:longitude", ValueType.NUMBER)
.addInputData("schema:latitude", ValueType.NUMBER)
.addOutputData("schema:longitude", ValueType.NUMBER)
.addOutputData("schema:latitude", ValueType.NUMBER)
.addOutputData("datex:parkingSpaceStatus", ValueType.TEXT)
.withPricingModel(BigIotTypes.PricingModel.PER_ACCESS)
.withMaxPrice(Euros.amount(0.5))
.withLicenseType(LicenseType.CREATIVE_COMMONS);
NOTE 1: A full list of already defined and supported semantic categories is available here. Via the Marketplace user interface, you can also create new categories during creation of an offering. Those ‘“proposed”’ types can then also be used in your code.
To execute the query on the marketplace, the consumer
object provides multiple discover methods. They have different signatures taking different programming preferences into account.
The first variant uses a CompletableFuture
as a return type, which is a promise on a list of OfferingDescriptions
introduced in Java 8. The following code shows how to discover offerings getting them as a CompletableFuture
on the list of OfferingDescriptions
.
CompletableFuture<List<SubscribableOfferingDescription>> listFuture =
consumer.discover(query);
/*do something in-between*/
List<SubscribableOfferingDescription> offerings = listFuture.get();
Offering offering = offerings.get(0).subscribe().get(); //just take the first
The discover call is non-blocking. So, you could do something in-between, e.g. handing over the CompletableFuture
object to your management thread. Of course, you can directly wait for the result by calling the get method. The motivation of using CompletableFuture
here is, that you can come easily from an asynchronous behavior to a blocking behavior and further you can utilize reactive concepts if you want. For example by calling thenAppl
y as a monad on the CompletableFuture
object allows you to apply functions once the list of offering descriptions is received.
listFuture.thenApply(SubscribableOfferingDescription::showOfferingDescriptions);
The discovery is just returning offering descriptions, before you can utilize the offering behind, you have to subscribe to them. This authorizes you und is a precondition if charging applies. Therefore a corresponding subscribe method exists, which returns an Offering
object. The Offering
object provides different access methods as described later.
You can also use callbacks for discovering offerings. Here is an example for this.
query.discover((r,l) -> {
SubscribableOfferingDescription.showOfferingDescriptions(l)
});
The callback function in this example again just prints the returned offering descriptions, however usually you would provide your offering selection logic here, that selects the appropriate offerings for your use case. The example again utilizes the functional programming features introduced in Java 8. With lambdas you can express functionality without much boilerplate code. Alternatively every other instance of DiscoverResponseHandler
is accepted by discover.
As a side note: Of cause you can reuse your query object for subsequent queries. Only if you want to change something regarding the filtering you have to create a new OfferingQuery
object.
Select an offering
When your discovery finds multiple offerings, you might wonder, which one to choose. Maybe you performed a wide search in order to find as much hits as possible. But you probably have selecion criteria to identify the best result. The lib supports also the automatic application of multiple selection criteria on the result of a discovery. Start with the creator method of OfferingSelector
and build your selection.
consumer
.discover(query)
.thenApply((list) -> OfferingSelector
.create()
.cheapest()
.mostPermissive()
.select(list));
Access offerings
Now, we want to read data from the offering. First, we have to specify with which parameter we want to access the offering.
AccessParameters accessParameters =
AccessParameters
.create()
.addRdfTypeValue("schema:longitude"), 12.3)
.addRdfTypeValue("schema:latitude"), 42.73);
As you see, values are assigned to RDF types. This is different to assigning them to names, e.g. used in the web service request. In BIG IoT we want to boost semantical modeling of offerings. We assume that using the semantical model to access offering leverages interoperability better. But if you like, there is also the addNameValue
method.
To access offerings, we distinguish between two access schemes: one-time access and continuous access. One-time means that you execute an access request every time you want to get new data. Continuous refers to the reception of data as a feed.
For one-time access, the Consumer Lib supports again different programming styles. You can either use callback functions for pure asynchronous access or you can use a CompletableFuture
to do reactive programming or even having a blocking call. In either case, you have to provide an AccessParameters
object for the access call.
CompletableFuture<AccessResponse> response = offering.accessOneTime(accessParameters);
response.thenAccept((r) -> System.out::println);
We use the one-time access method and pass the parameters object that restricts the access to the specified longitude and latitude coordinates. Since we use accessOneTime
returning a CompletableFuture
, we can apply a function on the result. Here we forward the result to println
.
In order to process the response in our application logic, you can use
response.asJsonNode();
It returns the parsed response as Jackson JsonNode
with you can traverse the data. If you prefer the raw JSON body, use
response.getBody();
Continuous access of offerings
Maybe we want to show the returned parking data in real time on a dashboard. It would be even nicer if we could access the parking data continuously.
AccessFeed accessFeed = offering
.accessContinuous(
accessParameters,
Duration.standardSeconds(60),
(o,r)->System.out.println("feed result = " + r),
(o,r)->System.out.println("Ups, something failed"));
You probably noticed that the call is very similar to the access on a per request base. We use the accessContinuously
method of the OfferingQuery
object which requires the accessParameters
object, a duration, a success callback and a failure callback in case something went wrong. The difference is that, when calling accessCalling
we create a feed, which requires a lifecycle management. The accessFeed object brings functionality for stopping (stop
), resuming (resume
) and getting the status of a feed subscription (status), which we don’t want to use now.
If you want to stop accessing an offering, you can unsubscribe accordingly.
offering.unsubscribe();
consumer.terminate();
Make sure to always call the terminate method of the consumer object before stopping your application in order to terminate any open network connections.
That’s it! You have just learned how to use the BIG IoT Library as a data provider as well as a data consumer. If you like, continue with more examples