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
Simplest way to call the RDMS API is REST Client on a browser. I used Postman on Google Chrome and steps I followed are:
{
"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:
{
"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:
{
"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.
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.
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.
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.
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 -
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;
}
}
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;
}
}
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>
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 i....
If the application is already started before you create the destination, then stop and start the application again.
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.
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!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
36 | |
25 | |
17 | |
13 | |
8 | |
7 | |
7 | |
6 | |
6 | |
6 |