Note: I assume basic knowledge of HANA Cloud Platform IoT Services on your part. If you are very new to the concept, this blog series by Aaron Williams – Using the SAP HCP IoT Services should get you started.


HANA Cloud Platform IoT services provides an RDMS API to register device types, devices and message types. This RDMS API can be called from any client which supports REST calls over HTTPS. The IoT services also provides a cockpit for creating IoT types- device types, message types and devices. But I have always felt that if we have to create a large number of these types then it is better to do so via APIs.

In this post, I have included 2 ways to trigger the RDMS APIs. First is the simplest – use a REST client of your choice and call the respective URLs with parameters. Second is more exciting – a java application running on HCP that calls the REST APIs and creates the types.

You need an account on HANA Cloud Platform to run these steps. You can get a free developer account on HCP for demo and training purposes. Once the account is created, go to the cockpit: https://account.hanatrial.ondemand.com/cockpit -> Services and enable IoT Services.


Table of Contents

PART 1: Use a REST Client to call RDMS APIs

Simplest way to call the RDMS API is REST Client on a browser. I used Postman on Google Chrome and steps I followed are:

  1. Open Google Chrome, to install click here: https://www.google.co.in/chrome/browser/desktop/
  2. Install Postman App in Chrome via the Chrome Web Store. You can of course use any other client.
  3. Once installed, go to chrome://apps/ and launch Postman.
  4. In the Builder tab, select request type as POST.
  5. URL should be the endpoint URL for device types. This is a bit confusing to HCP application developers as you have encountered too many URLS till this point (HCP cockpit URL, IoT Services cockpit URL, RDMS APIs URL…) Two ways to get the URL –
    1. The URL looks like this: https://iotrdmsiotservices-<accountname>.hanatrial.ondemand.com/com.sap.iotservices.dms/api/devicetypes. Replace the account name and the host name. This is the URL for my trial account, for a customer/partner account use the appropriate host.
    2. If you want to copy the URL go to HCP cockpit -> Services -> Internet of Things Services -> Go to Service. This opens the IoT Services cockpit. Click on the arrow next to user name and click on About. The section on Device Management API gives you required URLs.
  6. Create 2 headers –
    1. Content-Type: application/json
    2. Authorization: Basic Auth and give username and password. Postman provides a separate tab (just below URL) to input the username and password. Postman encodes this information using Base64 encoding and includes it as part of header. Don’t include username and password in headers without encoding! (the request won’t work anyways since the IoT services expects encoding plus you are hugely compromising security)
  7. Insert Body text as this and press Send.

{
"name": "Coffee Machine Device Type”
}












If everything goes fine, you will see a 200 response code with JSON body which gives name of the device type, id of the newly created device type and token for future authorization.

You can repeat the same steps for creating a message type and device.

Parameters for creating Message Types:

  1. URL: https://iotrdmsiotservices-<accountname>.hanatrial.ondemand.com/com.sap.iotservices.dms/api/messagetypes
  2. Headers: You can use Basic Auth for Authorization (as used for creating device types) or use the Authorization token received while creating the device type.
    1. Content-Type: application/json
    2. Authorization: Basic Auth and give username and password.  Or Authorization: Bearer <token>
  3. Body

{
    "device_type": "<device type id received in response while creating device type>",
      "name": "Raw Material Levels",
    "direction": "fromDevice",
    "fields": [
          {
              "position": 1,
              "name": "MilkLevel",
                "type": "double"
          },
          {
                "position": 2,
                "name": "WaterLevel",
                "type": "double"
            }
    ]
}











Parameters for creating Device:

  1. URL: https://iotrdmsiotservices-<accountname>.hanatrial.ondemand.com/com.sap.iotservices.dms/api/devices
  2. Headers: You can use Basic Auth for Authorization (as used for creating device types) or use the Authorization token received while creating the device type.
    1. Content-Type: application/json
    2. Authorization: Basic Auth and give username and password.  Or Authorization: Bearer <token>
  3. Body:

{
      "name": "Coffee Machine Device 1",
      "device_type": "<device type id received in response while creating device type>"
}











Voila! Let’s go to the cockpit and check if the types are created.

https://iotcockpitiotservices-<accountname>.hanatrial.ondemand.com/com.sap.iotservices.cockpit/# .

Now you have used the RDMS APIs at least once. But this is still cumbersome if you have to create a large number of device types, message types and devices. So we come to part 2 of the blog post.

PART 2: Use a Java Application to call RDMS APIs

I created a java application that uses this API and following section shows how the application is built. You can modify the code to insert details for your device types/ devices/ message types and run the application.

Note & Disclaimer

This is a sample code and should not be used productively. It only shows how the APIs can be used or can be used for demo purposes.

I have purposefully limited the code to call the RDMS APIs in background and not included any fancy UI. Two reasons – to keep code at its bare minimum and still make it understandable and extendable, a fancy UI just adds more clicks which is what we are trying to get away from (IoT services provides a very good UI already).

Note: The code snippets for all the classes are attached to the blog. Download the attached file, rename it to class_code.zip and extract the zip. The zip contains 4 files, one for each class.

Prerequisites:

  1. Install Eclipse tools for application development on HCP: https://tools.hana.ondemand.com/#cloud
  2. Download and extract SAP HANA Cloud Platform SDK for Java EE 6 Web Profile: https://tools.hana.ondemand.com/#cloud
  3. Download javax.json.jar to satisfy JSON dependency in the project code: http://www.java2s.com/Code/Jar/j/Downloadjavaxjson10jar.htm
  4. Configure Eclipse workspace for development: https://help.hana.ondemand.com/help/frameset.htm?e815ca4cbb5710148376c549fd74c0db.html This set-up should include these steps:
    1. Set correct JRE (Should point to JVM or JDK)
    2. Set Server Runtime Environment to point to HCP SDK folder
    3. Proxy settings (if required)
    4. Create a server for your HCP account

Step 1: Create a Dynamic Web project

In case you do not want to start from scratch and are ok with a workaround, import hello-world application from the samples folder of HCP SDK and modify the HelloWorldServlet.java class.


We start with creating an empty Dynamic Web project. Give a project name and select the appropriate Target Runtime (see prerequisite 4.b). Keep the rest settings to default in New Project Wizard.

Step 2: Add library for JSON dependency

Once the project is created or imported, create a folder with name lib under WEB-INF folder. Copy javax.json.jar to this lib folder. Add the javax.json.jar to build path of the project for local compilation. All jars from the WEB_INF->lib folder are included during deployment of the application.

Step 3: Create a destination

Create folder named destinations under the project root. Create file with name iotrdms and no file extension. This file is used to store parameters of destination required for this project.

Content of iotrdms file:


Description=IoT RDMS API
Type=HTTP
Authentication=BasicAuthentication
Name=iotrdms
CloudConnectorVersion=2
ProxyType=Internet
URL=https://iotrdmsiotservices-<accountname>.<hostname>/com.sap.iotservices.dms/api
User=











Set the user name and URL correctly. For example, for a user P123456789 on trial, paramters would be –

Step 4: Create Servlet Class: IoTRDMSServlet.java

Create a java class in the source folder with name: IoTRDMSServlet and package: com.sap.cloud.iot.rdms and copy the code from IoTRDMSServlet.java.txt into the class.

Code for IoTRDMSServlet.java file:


package com.sap.cloud.iot.rdms;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonValue;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sap.cloud.iot.rdms.sample.model.Device;
import com.sap.cloud.iot.rdms.sample.model.DeviceType;
import com.sap.cloud.iot.rdms.sample.model.MessageType;
import com.sap.core.connectivity.api.configuration.ConnectivityConfiguration;
import com.sap.core.connectivity.api.configuration.DestinationConfiguration;
public class IoTRDMSServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;
  private static final String MESSAGE_TYPE_DIRECTION_FROM_DEVICE = "fromDevice";
  private static final String DOUBLE_TYPE = "double";
  private static final String STRING_TYPE = "string";
  private static ArrayList<DeviceType> deviceTypes = new ArrayList<DeviceType>();
  private static ArrayList<Device> devices = new ArrayList<Device>();
  private static ArrayList<MessageType> messageTypes = new ArrayList<MessageType>();
  private void initData() {
  // 1st device type
  {
      DeviceType dt = new DeviceType("Coffee Machine Type");
      deviceTypes.add(dt);
      MessageType mt1 = new MessageType(dt, "Raw Material Levels",
      MESSAGE_TYPE_DIRECTION_FROM_DEVICE);
      mt1.createField("1", "MilkLevel", DOUBLE_TYPE);
      mt1.createField("2", "WaterLevel", DOUBLE_TYPE);
      messageTypes.add(mt1);
      MessageType mt2 = new MessageType(dt, "Power Status",
      MESSAGE_TYPE_DIRECTION_FROM_DEVICE);
      mt2.createField("1", "PowerOn", STRING_TYPE);
      messageTypes.add(mt2);
      devices.add(new Device(dt, "Coffee Machine 1"));
      devices.add(new Device(dt, "Coffee Machine 2"));
      devices.add(new Device(dt, "Coffee Machine 3"));
      devices.add(new Device(dt, "Coffee Machine 4"));
      devices.add(new Device(dt, "Coffee Machine 5"));
  }
  // 2nd device type
  {
      DeviceType dt = new DeviceType("Mars Rover Type");
      deviceTypes.add(dt);
      MessageType mt1 = new MessageType(dt, "Position", MESSAGE_TYPE_DIRECTION_FROM_DEVICE);
      mt1.createField("1", "Latitude", DOUBLE_TYPE);
      mt1.createField("2", "Longitude", DOUBLE_TYPE);
      messageTypes.add(mt1);
      devices.add(new Device(dt, "Mars Rover 1"));
  }
      // and you can create as many types as you want but be careful to use
      // correct device type while creating message types and devices
  }
  /** {@inheritDoc} */
  @Override
  public void doGet(HttpServletRequest request, HttpServletResponse response)  throws ServletException, IOException {
      initData();
      response.getWriter().println("<p>initializing configuration!</p>");
      DestinationConfiguration destinationConfiguration = getDestinationConfiguration();
      if (destinationConfiguration == null) {
            throw new ServletException("Failed to establish a connectivity to the destination.");
      }
      registerDeviceTypes(response, destinationConfiguration);
      registerMessageTypes(response, destinationConfiguration);
      registerDevices(response, destinationConfiguration);
  }
  private void registerMessageTypes(HttpServletResponse response, DestinationConfiguration destinationConfiguration) throws IOException {
      String destinationURL = destinationConfiguration.getProperty("URL");
      URL messageTypesURL;
      try {
            messageTypesURL = new URL(destinationURL + "/messagetypes");
      } catch (MalformedURLException e) {
            throw new MalformedURLException("Failed to build a HTTP URL for creating message types request.");
      }
      for (Iterator<MessageType> iterator = messageTypes.iterator(); iterator .hasNext();) {
            MessageType mtobject = (MessageType) iterator.next();
            String payload = mtobject.getJsonFormattedString();
            sendRestRequest(response, destinationConfiguration, payload, messageTypesURL);
            response.getWriter().println(  "<p>" + payload + " json body sent</p>");
            response.getWriter().println(  "<p>" + mtobject.getName() + " created successfully</p>");
      }
  }
  private void registerDeviceTypes(HttpServletResponse response, DestinationConfiguration destinationConfiguration) throws IOException {
      String destinationURL = destinationConfiguration.getProperty("URL");
      URL deviceTypesURL;
      try {
          deviceTypesURL = new URL(destinationURL + "/devicetypes");
      } catch (MalformedURLException e) {
                throw new MalformedURLException(  "Failed to build a HTTP URL for creating device types request.");
      }
      for (Iterator<DeviceType> iterator = deviceTypes.iterator(); iterator.hasNext();) {
            DeviceType dtobject = (DeviceType) iterator.next();
            String payload = dtobject.getJsonFormattedString();
            response.getWriter().println( "<p>" + payload + " json body sent</p>");
            String restResponse = sendRestRequest(response, destinationConfiguration, payload, deviceTypesURL);
            response.getWriter().println( "<p>" + dtobject.getName() + " created successfully</p>");
            response.getWriter().println(restResponse);
            JsonReader reader = Json .createReader(new StringReader(restResponse));
            JsonObject readObject = reader.readObject();
            JsonValue deviceTypeId = readObject.get("id");
            dtobject.setId(deviceTypeId.toString());
      }
  }
  private void registerDevices(HttpServletResponse response, DestinationConfiguration destinationConfiguration)  throws IOException {
      String destinationURL = destinationConfiguration.getProperty("URL");
      URL devicesURL;
      try {
            devicesURL = new URL(destinationURL + "/devices");
      } catch (MalformedURLException e) {
            throw new MalformedURLException( "Failed to build a HTTP URL for creating device types request.");
      }
      for (Iterator<Device> iterator = devices.iterator(); iterator.hasNext();) {
      Device device = (Device) iterator.next();
      String payload = device.getJsonFormattedString();
      sendRestRequest(response, destinationConfiguration, payload, devicesURL);
      response.getWriter().println( "<p>" + payload + " json body sent</p>");
      response.getWriter().println( "<p>" + device.getName() + " created successfully</p>");
      }
  }
  private String sendRestRequest(HttpServletResponse response, DestinationConfiguration destinationConfiguration, String payload, URL deviceTypesURL) throws IOException {
      HttpURLConnection urlConnection;
      try {
            urlConnection = (HttpURLConnection) deviceTypesURL.openConnection();
      } catch (IOException e) {
            throw new IOException( "Failed to open a HTTP URL connection to the destination.",  e);
      }
      String user = destinationConfiguration.getProperty("User");
      String password = destinationConfiguration.getProperty("Password");
      String base64 = new sun.misc.BASE64Encoder().encode((user + ":" + password).getBytes());
      urlConnection.setRequestProperty("Authorization", "Basic " + base64);
      // prepare for HTTP POST
      urlConnection.setDoOutput(true);
      urlConnection.setDoInput(true);
      urlConnection.setUseCaches(false);
      urlConnection.setRequestMethod("POST");
      urlConnection.setRequestProperty("Content-Type", "application/json" + ";charset=UTF-8");
      OutputStream outputStream = urlConnection.getOutputStream();
      OutputStreamWriter osw = new OutputStreamWriter(outputStream);
      BufferedWriter bw = new BufferedWriter(osw);
      bw.write(payload);
      bw.flush();
      bw.close();
      String responseMessage = urlConnection.getResponseMessage();
      InputStream inputStream = urlConnection.getInputStream();
      InputStreamReader isr = new InputStreamReader(inputStream);
      BufferedReader br = new BufferedReader(isr);
      responseMessage = br.readLine();
      urlConnection.disconnect();
      return responseMessage;
  }
  private DestinationConfiguration getDestinationConfiguration()  throws ServletException {
    DestinationConfiguration destinationConfiguration = null;
    try {
            InitialContext initialContext = new InitialContext();
            ConnectivityConfiguration connectivityConfiguration = (ConnectivityConfiguration) initialContext.lookup("java:comp/env/connectivityConfiguration");
            destinationConfiguration = connectivityConfiguration.getConfiguration("iotrdms");
      } catch (NamingException e) {
            throw new ServletException("connectivity configuration failed");
      }
      return destinationConfiguration;
  }
}





Step 5: Create Data classes

Create a new package named com.sap.cloud.iot.rdms.model and copy these 3 classes into this package – Device.java, DeviceType.java, MessageType.java.


These classes are used to initialize the data before triggering REST APIs to create these types on the cloud.


Code for MessageType.java:



package com.sap.cloud.iot.rdms.model;
import java.util.ArrayList;
import java.util.Iterator;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
public class MessageType {
  private DeviceType deviceType;
  private String name;
  private String direction;
  private ArrayList<MessageTypeField> fieldArray = new ArrayList<MessageTypeField>();
  public MessageType(DeviceType deviceType, String name, String direction) {
      super();
      this.deviceType = deviceType;
      this.name = name;
      this.direction = direction;
  }
  public void createField(String position, String name, String type) {
      MessageTypeField messageTypeField = new MessageTypeField(position, name, type);
      fieldArray.add(messageTypeField);
  }
  public String getJsonFormattedString() {
      JsonObjectBuilder objBuilder = Json.createObjectBuilder();
      objBuilder.add("device_type", deviceType.getId());
      objBuilder.add("name", name);
      objBuilder.add("direction", direction);
      JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
      for (Iterator<MessageTypeField> iterator = fieldArray.iterator(); iterator.hasNext();) {
            MessageTypeField messageTypeField = (MessageTypeField) iterator.next();
            arrayBuilder.add(messageTypeField.getJsonObject());
      }
      objBuilder.add("fields", arrayBuilder);
      JsonObject build = objBuilder.build();
      String payload = build.toString();
      return payload;
  }
  private class MessageTypeField {
      private String position;
      private String name;
      private String type;
      MessageTypeField(String position, String name, String type) {
            super();
            this.position = position;
            this.name = name;
            this.type = type;
      }
      public JsonObject getJsonObject() {
            JsonObjectBuilder objBuilder = Json.createObjectBuilder();
            objBuilder.add("position", position);
            objBuilder.add("name", name);
            objBuilder.add("type", type);
            JsonObject build = objBuilder.build();
            return build;
      }
  }
  public String getName() {
      return name;
  }
}






Code for DeviceType.java:



package com.sap.cloud.iot.rdms.model;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
public class DeviceType {
  private String id;
  private String name;
  public DeviceType(String name) {
      super();
      this.name = name;
  }
  public String getJsonFormattedString() {
      JsonObjectBuilder objBuilder = Json.createObjectBuilder();
      objBuilder.add("name", name);
      JsonObject build = objBuilder.build();
      String payload = build.toString();
      return payload;
  }
  public String getId() {
      return id;
  }
  public String getName() {
      return name;
  }
  public void setId(String deviceTypeId) {
      id = deviceTypeId;
  }
}





Code for Device.java:


package com.sap.cloud.iot.rdms.model;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
public class Device {
  private String name;
  private DeviceType deviceType;
  public Device(DeviceType deviceType, String name) {
      super();
      this.deviceType = deviceType;
      this.name = name;
  }
  public String getJsonFormattedString(){
      JsonObjectBuilder objBuilder = Json.createObjectBuilder();
      objBuilder.add("device_type", deviceType.getId());
      objBuilder.add("name", name);
      JsonObject build = objBuilder.build();
      String payload = build.toString();
      return payload;
  }
  public String getName() {
      return name;
  }
}





Step 6: Create deployment descriptor file: web.xml

Create web.xml file under <Project> -> WebContent -> WEB-INF folder.

Content for web.xml file:


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
    <servlet>
        <servlet-name>IoTRDMSServlet</servlet-name>
        <servlet-class> com.sap.cloud.iot.rdms.IoTRDMSServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>IoTRDMSServlet</servlet-name>
        <url-pattern>/</url-pattern>
  </servlet-mapping>
  <resource-ref>
        <res-ref-name>connectivityConfiguration</res-ref-name>
        <res-type>com.sap.core.connectivity.api.configuration.ConnectivityConfiguration</res-type>
    </resource-ref>
</web-app>









Step 7: Deploy the application

Now deploy the application on the Server that we have defined in Prerequisite 4. If you are unsure on how to do this, refer to JAVA Hello World tutorial section on Deploy, Run and Test the Application in the Cloud.


Once the application is successfully deployed, and import the destination that we defined in Step 3. More info on what destinations are, how to configure destinations via Eclipse, Cockpit and Console is here.

If the application is already started before you create the destination, then stop and start the application again.

Step 8: Run the application

Once the application starts, open the application URL from cockpit. If everything is specified correctly you will see a log of device types, devices and message types created. You can also check the IoT Services Cockpit to see if the types are created correctly.

If you see some errors or some types do not get generated, then check if you have followed all the steps mentioned above.

Step 9: Extend the application for your data

The servlet class by default creates a 2 device types, 3 message types and 6 devices. If you need to create multiple types then you can modify the method initData() and run the application again.

Hope this post helps you in creating your own application!

To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

Leave a Reply