Java Client

Java

The Sentilo Java Client is a library developed for working with webapps or standalone java applications. You can download the source code from https://github.com/sentilo/sentilo-client-sample-java.

For this example, we’ll use a basic maven based web application, that retrieve some system data and send it as a sensor observation to the Sentilo Platform. This webapp is named sentilo-client-java-sample and you’ll find it into the source code (see above).

Hardware

We don’t need any specific hardware for running this example, only one PC with Internet connection.

Software

You’ll need some software packages, as you’re developing in Java environment:

  • Java SE 1.8
  • Eclipse IDE or STS Spring IDE
  • The Sentilo Client Java Library, which you can download and install it as a Maven dependency into your project (please, see the pom file in the project form more information)
  • Tomcat 7+
  • Some other Maven dependencies(you can see them in the pom file of the example project):
    • Hibernate
    • Spring
    • And some other…

The example

The code

You must download the sample webapp project from Git repository: https://github.com/sentilo/sentilo-client-sample-java

Once you have the project, open it with Eclipse or another IDE and construct it using Maven goals: clean package, for downloading dependencies, compile the code and package it.

After that your project is compiled and packaged, you can deploy it in a Tomcat 7 webapp container. We recommend that you use Eclipse or STS Spring IDE to develop and open this example, and deploy it with the Apache deployment plugins.

Now you can then navigate into the project and edit the source code.

The properties file

You must modify the properties file application.properties located in src/main/resources/properties in order to provide your correct Sentilo Platform Client configurations. There’re some values that are for testing purposes, and they may not be changed.

# Sentilo Platform API Services IP
rest.client.host=YOUR_SENTILO_PLATFORM_CLIENT_ADDRESS

# User configurations
rest.client.identityKey=YOUR_IDENTITY_KEY
rest.client.provider=samples-provider
rest.client.component=sample-component
rest.client.component.type=generic
rest.client.component.location=41.387015 2.170047
rest.client.sensor=sample-sensor-java
rest.client.sensor.type=status
rest.client.sensor.dataType=TEXT
rest.client.sensor.location=41.387015,2.170047

This settings should be updated:

  • rest.client.host: provide a correct host or ip address of your Sentilo Platform Client, and replace the YOUR_SENTILO_PLATFORM_CLIENT_ADDRESS with it
  • rest.client.identityKey: provide your correct application security token, and replace the YOUR_IDENTITY_KEY with it
  • optionally, you can provide your component / sensor locations, modifying the values rest.client.component.location and rest.client.sensor.location

The samples controller

There’s a Spring MWC controller which displays a view with the sensor data retrieved from system and the publish result. Navigate to src/main/java and open this resource org.sentilo.samples.controller.SamplesController.

This is a Spring Framework Controller that creates a view where you’ll see a sample data value obtained from the System, and then send it as observation to your Sentilo Platform instance. The webapp is based on Maven & Spring foundations, so you must modify and provide some configurations before start the example execution (see above).

/**
 * SamplesController
 *
 * Executes a basic Sentilo Java Client Platform example which connects to the server and publish some data to a sample sensor.
 * In this case we're getting info from the system with the runtime properties object
 *
 * @author openTrends
 */
@Controller
public class SamplesController {

  private final Logger logger = LoggerFactory.getLogger(SamplesController.class);

  private static final String VIEW_SAMPLES_RESPONSE = "samples";

  private static final int SLEEP_TIME = 1;

  @Autowired
  private PlatformTemplate platformTemplate;

  @Resource
  private Properties samplesProperties;

  @RequestMapping(value = {"/", "/home"})
  public String runSamples(final Model model) {

    // All this data must be created in the Catalog Application before starting this
    // sample execution. At least the identity key and the provider id must be
    // declared in the system
    String restClientIdentityKey = samplesProperties.getProperty("rest.client.identityKey");
    String providerId = samplesProperties.getProperty("rest.client.provider");

    // For this example we have created a generic component with a status sensor that accepts text
    // type observations, only for test purpose
    String componentId = samplesProperties.getProperty("rest.client.component");
    String sensorId = samplesProperties.getProperty("rest.client.sensor");

    logger.info("Starting samples execution...");

    String observationsValue = null;
    String errorMessage = null;

    try {
      // Get some system data from runtime
      Runtime runtime = Runtime.getRuntime();
      NumberFormat format = NumberFormat.getInstance();
      StringBuilder sb = new StringBuilder();
      long maxMemory = runtime.maxMemory();
      long allocatedMemory = runtime.totalMemory();
      long freeMemory = runtime.freeMemory();

      sb.append("free memory: " + format.format(freeMemory / 1024) + "<br/>");
      sb.append("allocated memory: " + format.format(allocatedMemory / 1024) + "<br/>");
      sb.append("max memory: " + format.format(maxMemory / 1024) + "<br/>");
      sb.append("total free memory: " + format.format((freeMemory + (maxMemory - allocatedMemory)) / 1024) + "<br/>");

      // In this case, we're getting CPU status in text mode
      observationsValue = sb.toString();

      logger.info("Observations values: " + observationsValue);

      // Create the sample sensor, only if it doesn't exists in the catalog
      createSensorIfNotExists(restClientIdentityKey, providerId, componentId, sensorId);

      // Publish observations to the sample sensor
      sendObservations(restClientIdentityKey, providerId, componentId, sensorId, observationsValue);
    } catch (Exception e) {
      logger.error("Error publishing sensor observations: " + e.getMessage(), e);
      errorMessage = e.getMessage();
    }

    logger.info("Samples execution ended!");

    model.addAttribute("restClientIdentityKey", restClientIdentityKey);
    model.addAttribute("providerId", providerId);
    model.addAttribute("componentId", componentId);
    model.addAttribute("sensorId", sensorId);
    model.addAttribute("observations", observationsValue);

    ObjectMapper mapper = new ObjectMapper();

    try {
      if (errorMessage != null && errorMessage.length() > 0) {
        Object json = mapper.readValue(errorMessage, Object.class);
        model.addAttribute("errorMsg", mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json));
      } else {
        model.addAttribute("successMsg", "Observations sended successfully");
      }
    } catch (Exception e) {
      logger.error("Error parsing JSON: {}", e.getMessage(), e);
      errorMessage += (errorMessage.length() > 0) ? "<br/>" : "" + e.getMessage();
      model.addAttribute("errorMsg", errorMessage);
    }

    return VIEW_SAMPLES_RESPONSE;
  }

  /**
   * Retrieve catalog information about the sample provider. If the component and/or sensor doesn't
   * exists, it will create them
   *
   * @param identityToken Sample identity token
   * @param providerId Samples provider id
   * @param componentId Samples component id
   * @param sensorId Samples sensor id
   * @return {@link CatalogOutputMessage} object with provider's catalog data
   */
  private CatalogOutputMessage createSensorIfNotExists(String identityToken, String providerId, String componentId, String sensorId) {
    List<String> sensorsIdList = new ArrayList<String>();
    sensorsIdList.add(sensorId);

    // Create a CatalogInputMessage object for retrieve server data
    CatalogInputMessage getSensorsInputMsg = new CatalogInputMessage();
    getSensorsInputMsg.setProviderId(providerId);
    getSensorsInputMsg.setIdentityToken(identityToken);
    getSensorsInputMsg.setComponents(createComponentsList(componentId));
    getSensorsInputMsg.setSensors(createSensorsList(providerId, componentId, sensorsIdList));

    // Obtain the sensors list from provider within a CatalogOutputMessage response object type
    CatalogOutputMessage getSensorsOutputMsg = platformTemplate.getCatalogOps().getSensors(getSensorsInputMsg);

    // Search for the sensor in the list
    boolean existsSensor = false;
    if (getSensorsOutputMsg.getProviders() != null && !getSensorsOutputMsg.getProviders().isEmpty()) {
      for (AuthorizedProvider provider : getSensorsOutputMsg.getProviders()) {
        if (provider.getSensors() != null && !provider.getSensors().isEmpty()) {
          for (CatalogSensor sensor : provider.getSensors()) {
            logger.debug("Retrieved sensor: " + sensor.getComponent() + " - " + sensor.getSensor());
            existsSensor |= sensorId.equals(sensor.getSensor());
            if (existsSensor) {
              break;
            }
          }
        }
      }
    }

    // If the sensor doesn't exists in the retrieved list, we must create it before publishing the
    // observations
    if (!existsSensor) {
      // Create a CatalogInputMessage object for retrieve server data
      CatalogInputMessage registerSensorsInputMsg = new CatalogInputMessage(providerId);
      registerSensorsInputMsg.setIdentityToken(identityToken);
      registerSensorsInputMsg.setComponents(createComponentsList(componentId));
      registerSensorsInputMsg.setSensors(createSensorsList(providerId, componentId, sensorsIdList));

      // Register the new sensor in the server
      platformTemplate.getCatalogOps().registerSensors(registerSensorsInputMsg);
    }

    return getSensorsOutputMsg;
  }

  /**
   * Publish some observations from a sensor
   *
   * @param identityToken Samples Application identity token for manage the rest connections
   * @param providerId Samples provider id
   * @param componentId Samples component id
   * @param sensorId Samples sensor id
   * @param value Observations value, in our case, a String type
   */
  private void sendObservations(String identityToken, String providerId, String componentId, String sensorId, String value) {
    List<String> sensorsIdList = new ArrayList<String>();
    sensorsIdList.add(sensorId);
    createSensorsList(providerId, componentId, sensorsIdList);

    List<Observation> observations = new ArrayList<Observation>();
    Observation observation = new Observation(value, new Date());
    observations.add(observation);

    SensorObservations sensorObservations = new SensorObservations(sensorId);
    sensorObservations.setObservations(observations);

    DataInputMessage dataInputMessage = new DataInputMessage(providerId, sensorId);
    dataInputMessage.setIdentityToken(identityToken);
    dataInputMessage.setSensorObservations(sensorObservations);

    platformTemplate.getDataOps().sendObservations(dataInputMessage);
  }

  /**
   * Create a component list
   *
   * @param componentId Component identifier
   * @return A {@link CatalogComponent} list
   */
  private List<CatalogComponent> createComponentsList(String componentId) {
    List<CatalogComponent> catalogComponentList = new ArrayList<CatalogComponent>();
    CatalogComponent catalogComponent = new CatalogComponent();
    catalogComponent.setComponent(componentId);
    catalogComponent.setComponentType(samplesProperties.getProperty("rest.client.component.type"));
    catalogComponent.setLocation(samplesProperties.getProperty("rest.client.component.location"));
    catalogComponentList.add(catalogComponent);
    return catalogComponentList;
  }

  /**
   * Create a sensor list
   *
   * @param componentId The Sample Component Id
   * @param sensorsIdList A list with the sensor ids to create
   * @return A {@link CatalogSensor} list
   */
  private List<CatalogSensor> createSensorsList(String providerId, String componentId, List<String> sensorsIdList) {
    List<CatalogSensor> catalogSensorsList = new ArrayList<CatalogSensor>();
    for (String sensorId : sensorsIdList) {
      CatalogSensor catalogSensor = new CatalogSensor();
      catalogSensor.setComponent(componentId);
      catalogSensor.setSensor(sensorId);
      catalogSensor.setProvider(providerId);
      catalogSensor.setType(samplesProperties.getProperty("rest.client.sensor.type"));
      catalogSensor.setDataType(samplesProperties.getProperty("rest.client.sensor.dataType"));
      catalogSensor.setLocation(samplesProperties.getProperty("rest.client.sensor.location"));
      catalogSensorsList.add(catalogSensor);
    }
    return catalogSensorsList;
  }
}

What’s happenning?

  • Firts of all, we’re looking for some configuration settings, like the component and sensor names
  • Next, we’re using some runtime status values, so we can the publish them as a observations (mem status, for example)
  • First of all, we check if the sensor has been created before in the Catalog, and if it doesn’t exists we add it
  • After that, we’ll publish the sensor observations
  • Then, we pass all this information to the view for displaying it the navigator window

This is an observation sample:

CPU states: 5.8% user, 1.9% system, 0.0% nice, 0.0% wait, 91.7% idle

The samples page view

And finally, this is the source code of the view:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>

<html>

 <head>

 </head>

 <body>

  <h3>Observations:</h3>
  <p>${observations}</p>

  <br />

  <c:if test="${not empty successMsg}">
   <h3>Success:</h3>
   <p>${successMsg}</p>
  </c:if>

  <c:if test="${not empty errorMsg}">
   <h3>Error:</h3>
   <pre>${errorMsg}</pre>
  </c:if>

  <br />

  <button onclick="location.reload();">Send observations</button>

 </body>

</html>

This source code is quite easy, so don’t need to comment it.

Executing the sample application

Using the Eclipse IDE or copying the WAR file, deploy your webbapp into the Tomcat deployments directory, and start it.

You must access to this url (we assume that you’re in your localhost and your port is the 8080, the default values): http://localhost:8080/sentilo-samples

And then you must see a result page like this:

java_sample_4.jpg

As you can see, there’s a button named Send observations. You can use to re-send observations and reload the page. Every page reload send the observatios to the Sentilo Platform Client.