By Johan Vos
javamag-logo

Originally published in the Jan/Feb 2015 issue of Java Magazine. Subscribe today

A performant and scalable cloud-based monitoring system for collecting data from embedded devices

The number of embedded devices that are connected to the internet – the Internet of Things (IoT) – is growing every day. Many of these devices need an internet connection to retrieve data from and send data to a variety of backend services. But the internet connection often is also required in order to manage the devices and the software they are running.

In order for a device to be manageable, it should at least be possible to monitor it. An operator, or the person who is responsible for the maintenance of the device, wants to know the current state of a device, its history, its current operations, and so on. With the growing number of connected devices, monitoring these devices becomes a huge but important challenge. In this article, we will explore a use case where cloud-based monitoring of connected devices allows the operation of a large number of kiosk systems.

The Use Case

Screen Shot 2015-02-20 at 14.52.10CultuurNet Vlaanderen is a government organization in Belgium. At the request of the Flemish authorities, this organization tries to increase public enthusiasm for culture. In 2012, CultuurNet launched the Uitpas project, a system based on a near field communication (NFC) card that allows its users to earn points when attending cultural events and to exchange points for rewards. Users can scan their cards at kiosk systems (see Figure 1), which are built around a Raspberry Pi connected to an NFC reader and an LCD screen.

A kiosk system is also called a check-in device (CID), because the original goal was to “check in” a user for a cultural activity.
Initially, Uitpas was rolled out in three cities only, with about 30 CIDs spread around cultural locations in those cities, for example, near theaters, cinemas, museums, and so on. Those CIDs were operated by CultuurNet on behalf of the three cities.

The success of Uitpas in the three cities triggered an interest by more cities, so more CIDs were put into production, along with a growing number of operational entities. Each city is now expected to manage its own devices and operations.

Clearly, management can’t be done manually anymore. Also, the need for distributed management (each city should be able to monitor and fix its own devices) leads to operational challenges. In order to address these challenges, CultuurNet is now using a cloud-based monitoring system that monitors all registered CIDs and provides access to different operators. Each operator – typically, an IT manager employed by the city – can monitor and manage only the CIDs for that manager’s city.

The solution is powered end to end by Java. The CIDs run JavaFX on a Raspberry Pi. They send their monitoring information to the IoT Monitor Cloud – the central piece of the architecture, which runs a Java EE 7 implementation (GlassFish). The client monitoring application is, again, a JavaFX application. Figure 2 shows this setup.

Screen Shot 2015-02-20 at 15.22.41

The different components all use Java, but in different environments. The IoT Monitor Cloud collects and stores all information from all CIDs. This component uses Java EE 7 APIs and runs in GlassFish instances in the Amazon EC2 cloud. It uses Amazon DynamoDB for storage capabilities and the open source elasticsearch engine for search capabilities.

CIDs to Cloud

The CIDs send monitoring data about functional events, as well as logging data, to the IoT Monitor Cloud. Because logging data is easily obtained in a Java application, this functionality can be used in any application. The CID application internally uses the logger shown in Listing 1.

Listing 1

public static final Logger LOGGER = Logger.getLogger("be.uitpas.pi");

All system-related events (for example, a card being scanned, a network request being made, a network request timing out) are logged using standard Java logging commands, for example:

LOGGER.log(Level.SEVERE, "Could not read card.");

A specific MonitorHandler, which is an extension of LogHandler, is created and added to the created logger, as shown in Listing 2. By doing so, the monitorHandler will be notified about all entries that are logged on the LOGGER instance.

Listing 2

final MonitorHandler monitorHandler = new MonitorHandler();
            LOGGER.addHandler(monitorHandler);
            monitorHandler.setLevel(Level.ALL);

public static final Logger LOGGER = Logger.getLogger(“be.uitpas.pi”);
The MonitorHandler itself is defined as shown in Listing 3. Whenever something is logged in the application by calling LOGGER .log(…), the publish method on the MonitorHandler is called, and the log message is provided with additional information such as a time stamp, thread information, the method, and so on.

Listing 3

public final class MonitorHandler extends java.util.logging.StreamHandler {
@Override
    public void publish(final java.util.logging.LogRecord record) {
...
    }
}

The MonitorHandler will send the log information to the IoT Monitor Cloud using DataFX, which is a JavaFX-based framework that brings enterprise functionality to JavaFX. One of its components is the DataSources component, which facilitates REST-based communication with back-end systems. DataFX respects the JavaFX threading model and does its work on a background thread, using the JavaFX Application thread to report back to the application.

The code snippet in Listing 4 shows how DataFX executes a REST request from an embedded device to the IoT Monitor Cloud, which provides an endpoint at the fictive address http://iotmonitor .cloud. The actual request contains more form parameters, but for readability those are omitted.

Listing 4

  RestSource restSource = RestSourceBuilder.create()
                .host(“http://iotmonitor.cloud”)
                .path("pimonitor")
                .path("rest/monitor/log")
                .timeout(5000)
                .formParam("cid", myCidIdentifier)
                .formParam("message", myMessage)
                .formParam("date", String.valueOf(record.getMillis()))
                .build();
        ObjectDataProvider<String> odp = ObjectDataProviderBuilder.create()
                .dataReader(restSource).resultProperty(answer)
                .build();
        odp.setExecutor(executorService);
        final Worker<String> retrieve = odp.retrieve();

As can be seen from the code, a RestSource is created for making an HTTP connection to an end-point, thereby providing typical information about the path, the parameters, and connection settings such as the timeout value. The RestSource is then passed to an ObjectDataProvider, which makes the request and puts the result into a property named answer. An ExecutorService instance is then passed to the DataFX ObjectDataProvider, because it can be expected that on some occasions, lots of logging information outside the scope of this article. See http://datafx.io for more information.

One of the most common issues with connected devices is the loss of connectivity. Clearly, sending a log message about lost connectivity is not going to work. In this case, the MonitorHandler will store all log messages to a file system. Once the connection is restored, the messages will be sent to the IoT Monitor Cloud.

JavaFX Monitor Client Application

The JavaFX Monitor client application connects to the IoT Monitor Cloud and visualizes the monitor- ing information that the operator is allowed to see. The IoT Monitor Cloud maintains a list of registered operators, and associates them with one or more card systems. A card system is a group of CIDs that belong together, typically all CIDs in a specific city.

Screen Shot 2015-02-20 at 15.24.29

Authentication is, therefore, a crucial component in the IoT Monitor Cloud setup. Figure 3 shows the login screen of the JavaFX Monitor client. Once the operator is authenticated, the monitoring console is shown (see Figure 4). The top-left corner of the console shows the list of card systems the operator has access to, and the CIDs registered with those card systems are shown below. The main part of the console shows the monitoring data originating from the different CIDs that the operator is managing.

Screen Shot 2015-02-20 at 15.25.11

Typically, there is a huge amount of data to be visualized. Therefore, the application contains a number of selectors and filters that allow the operator to select a particular part of the data, for example:

    •  A time selector, for showing only information within a specific time range.
    • A level selector, for selecting messages of a specific level or a range of levels.
    • A CID selector, for selecting one or more CIDs for which to inspect logs. Clicking a specific CID in the list on the left selects the specified CID and shows its monitoring data in the list. If no specific CID has been selected, data from all CIDs is shown.

When analyzing data, operators typically modify the selectors frequently. If all changes to selectors would lead to calls to the back end, the application would not be very responsive. However, JavaFX in combination with Java 8 features allows operators to manipulate the data set on the client. When the application is started, some data is retrieved. If more data is needed, it will be loaded into the application, and data that is no longer needed can be garbage collected. This allows the application to take advantage of the memory and processing capabilities of the client system it is running on. Typically, the JavaFX Monitor application has a lot more data entries in its memory than it visualizes in the list.

The list component in the main part of the console is defined as follows:

private ListView<Event> eventsListView

The items that are to be rendered in this ListView are kept in a JavaFX SortedList named sortedEvents. This list is associated with the ListView as follows:

eventsListView.setItems( sortedEvents);

All data for monitoring items that are retrieved from the IoT Monitor Cloud are captured in an ObservableList of type Event that is named events:

ObservableList<Event> events;

New events are loaded from the back end using DataFX with code that is similar to the code in Listing 4, or they are sent from the IoT Monitor Cloud to the JavaFX Monitor application over a WebSocket, which will be discussed shortly. In both cases, the fact that events are stored in an ObservableList allows the ListView that renders the events to take all data into account—including events that are added after the ListView has been created.

Note that the raw events are stored in an ObservableList named events, whereas the events that are passed to the ListView
are stored in a SortedList named sortedEvents. There is a relationship between those two lists. First, the raw list of events is filtered to contain only those events that match the different selectors. This is done using the Java 8 Stream API, which provides filtering functionality. We have an intermediate field named filteredEvents of type FilteredList that is initially defined as shown in Listing 5.

Listing 5

FilteredList<Event> filteredEvents = events.filtered (e → true);

The FilteredList class was added in JavaFX 8, and it provides an extremely powerful approach for maintaining a list of elements based on an original list, but taking into account filter criteria that are specified as a predicate. From this initialization, it is clear that initially, all raw events are considered to be included in the filteredEvents list. Whenever a selector changes, the filtered predicate is changed:

filteredEvents.setPredicate( validEvent())

validEvent() returns a predicate that checks whether a given candidate event should be contained in the filteredList.

In our case, the boundary conditions imposed by the three selectors mentioned previously must be satisfied before a candidate event is accepted. This is shown in the implementation of the validEvent() method in Listing 6.

Listing 6

private Predicate<Event> validEvent() {
    return selectedCid()
            .and(isShowLevel())
            .and(isInTimeRange());
}

The return statement in this method contains three methods that each returns a predicate by itself. We will examine only the code for the isShowLevel() predicate (see Listing 7); however, it should be clear that the other functions are constructed in the same way—but they are slightly more complex. This predicate will check that for a given event e, the log level is at least equal to the level selected by the selector.

Listing 7

private Predicate<Event> isShowLevel() {
    return e → e.getLogLevel().intValue() >= selectedLevel.intValue();
}

The filteredEvents could now be shown in the ListView, but there is no guarantee on the order in which the events would be shown. This can be fixed by having the Event class implement the java.util.Comparable interface, and implement it in such a way that the most-recent events are always shown first. The SortedList class was added in JavaFX 8, and it allows instances of a ListView to be sorted according to their Comparator, which is what we use:

SortedList<Event> sortedEvents = filteredEvents.sorted();

It is this SortedList, which extends ObservableList, that is passed to the ListView. Thus, by using some Java 8 functionality, we are able to use a simple approach to render only relevant entries in a sorted way, based on a large set of raw, unsorted data.

WebSocket

The time selector shown in Figure 4 shows a tab labeled “Live.” This allows real-time monitoring.

Real-time monitoring of devices is often very important, because it can prevent issues from being escalated into big problems. It also helps operators to detect problems rather than requiring users to call an operator to report a problem. Therefore, the IoT Monitor Cloud is capable of sending data entries on the fly, as soon as they arrive in the system.

Both the IoT Monitor Cloud as well as the JavaFX Monitor application leverage JSR 356, the Java API for WebSocket. As soon as a user logs in successfully to the JavaFX Monitor application, the application establishes a WebSocket connection to the IoT Monitor Cloud. The DataFX framework we discussed before contains a WebSocket component as well. This component lever- ages the client portions of JSR 356 and opens a WebSocket connection to the IoT Monitor Cloud. Because the IoT Monitor Cloud is running on top of GlassFish, which is the reference implementation of Java EE 7, it contains an implementation of JSR 356 out of the box. Hence, it is very easy to register a WebSocket endpoint in the IoT Monitor Cloud that will broadcast all new incoming events to the clients that are entitled to read this information.

Conclusion

In this article, we talked about the importance of being able to monitor IoT devices in the field. When a large number of devices is being used, and when those devices generate lots of data, a cloud-based solution is preferred for collecting the data, especially if there are different groups of users with different sets of permissions. The Java EE 7 APIs allow you to develop a cloud-based monitoring system that is performant and scalable while addressing the needs of clients (for example, by providing support for WebSocket).

The visualization of the monitoring data can be achieved using JavaFX. We saw that the Java 8 APIs help in filtering and sorting relevant data based on a number of criteria. This exemplifies Java everywhere: The embedded devices are running Oracle Java SE Embedded, the IoT Monitor Cloud is running GlassFish 4.1 instances, and the monitoring client is a self-contained application created with JavaFX 8.

Cloud Based Monitoring of IoT Devices

Profile photo of voxxed
About The Author
-

1 Comment

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>