To meet the need to manage devices and obtain responses from the devices, IoT Platform provides synchronous services. This article describes how to call a synchronous service.

Background information

A server calls an API operation that is provided by IoT Platform to invoke a synchronous service. IoT Platform sends a request to a device. Then, the device responds to the request within a specific period of time and returns the results to IoT Platform. Servers use synchronous services to manage devices and obtain results from the devices. IoT Platform calls synchronous services by using Revert-RPC (RRPC). For more information, see Device properties, events, and services.

Usage notes

  • Before you can call a synchronous service, make sure that the device that that you use the synchronous service to manage is online.
  • After IoT Platform calls a synchronous service, the maximum timeout period for IoT Platform to wait before a response is received is 8 seconds. If no response is received within 8 seconds, a timeout error occurs.

Step 1: Create a service

  1. Log on to the IoT Platform console by using an Alibaba Cloud account.
  2. In the left-side navigation pane, choose Devices > Products. On the Products page, find the product that you want to view and click View in the Actions column.
  3. On the Product Details page, click the Define Feature tab and create a custom feature, as shown in the following figure.
    Add a custom service

    For information about how to set the parameters for a custom feature, see Add a TSL feature.

  4. Add an input parameter and an output parameter, as shown in the following figure.
    Figure 1. Input parameter
    Input parameter
    Figure 2. Output parameter
    Output parameter

Step 2: Create a device SDK-based application

  1. Add the following dependencies to the pom.xml file.
    <dependency>
      <groupId>com.aliyun.alink.linksdk</groupId>
      <artifactId>iot-linkkit-java</artifactId>
      <version>1.2.0.1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.40</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>23.0</version>
    </dependency>

    For more information, see Remote Configuration.

  2. Add a Java class and specify the information about your device in the Config.* file.
    /*   
     * Copyright © 2019 Alibaba. All rights reserved.
     */
    package com.aliyun.iot.demo.alink;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    import com.alibaba.fastjson.JSONObject;
    import com.aliyun.alink.dm.api.DeviceInfo;
    import com.aliyun.alink.dm.api.InitResult;
    import com.aliyun.alink.linkkit.api.ILinkKitConnectListener;
    import com.aliyun.alink.linkkit.api.IoTMqttClientConfig;
    import com.aliyun.alink.linkkit.api.LinkKit;
    import com.aliyun.alink.linkkit.api.LinkKitInitParams;
    import com.aliyun.alink.linksdk.cmp.connect.channel.MqttPublishRequest;
    import com.aliyun.alink.linksdk.cmp.core.base.AMessage;
    import com.aliyun.alink.linksdk.cmp.core.base.ARequest;
    import com.aliyun.alink.linksdk.cmp.core.base.AResponse;
    import com.aliyun.alink.linksdk.cmp.core.base.ConnectState;
    import com.aliyun.alink.linksdk.cmp.core.listener.IConnectNotifyListener;
    import com.aliyun.alink.linksdk.cmp.core.listener.IConnectSendListener;
    import com.aliyun.alink.linksdk.tools.AError;
    import com.google.common.util.concurrent.ThreadFactoryBuilder;
    
    /**
     * Call a synchronous service.<br>
     */
    public class SyncServiceProcessor {
    
      // ===================The start of the segment where you specify the required parameters===========================
      // Specify the information about your device in the Config.* file.
      // Specify the ID of the region.
      private static String regionId = "cn-shanghai";
      // Specify the ProductKey that you can obtain from the device certificate.
      private static String productKey = "Config.productKey";
      // Specify the DeviceName that you can obtain from the device certificate.
      private static String deviceName = "Config.deviceName";
      // Specify the DeviceSecret that you can obtain from the device certificate.
      private static String deviceSecret = "Config.deviceSecret";
      // ===================The end of the segment where you specify the required parameters===========================
    
      private static ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
          Runtime.getRuntime().availableProcessors() * 2, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100),
          new ThreadFactoryBuilder().setDaemon(true).setNameFormat("http2-downlink-handle-%d").build(),
          new ThreadPoolExecutor.AbortPolicy());
    
      /**
       * 1 Start the application to simulate an online device. <br>
       * 2 Initialize an InvokeSyncService object to call a synchronous service.<br>
       * 
       * @param args
       * @throws InterruptedException
       */
      public static void main(String[] args) throws InterruptedException {
    
        // Listen for downstream data.
        registerNotifyListener();
    
        // Connect the device to IoT Platform.
        connect(productKey, deviceName, deviceSecret);
      }
    
      /**
       * Establish a connection.
       * 
       * @param productKey The key of the product.
       * @param deviceName The name of the device.
       * @param deviceSecret The DeviceSecret.
       * @throws InterruptedException
       */
      private static void connect(String productKey, String deviceName, String deviceSecret) throws InterruptedException {
    
        // Initialize parameters.
        LinkKitInitParams params = new LinkKitInitParams();
    
        // Specify the parameters for MQTT initialization.
        IoTMqttClientConfig config = new IoTMqttClientConfig();
        config.channelHost = productKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883";
        config.productKey = productKey;
        config.deviceName = deviceName;
        config.deviceSecret = deviceSecret;
        params.mqttClientConfig = config;
    
        // Specify the certificate information about the device.
        DeviceInfo deviceInfo = new DeviceInfo();
        deviceInfo.productKey = productKey;
        deviceInfo.deviceName = deviceName;
        deviceInfo.deviceSecret = deviceSecret;
        params.deviceInfo = deviceInfo;
    
        // Initialize the device.
        LinkKit.getInstance().init(params, new ILinkKitConnectListener() {
          public void onError(AError aError) {
            System.out.println("init failed !! code=" + aError.getCode() + ",msg=" + aError.getMsg() + ",subCode="
                + aError.getSubCode() + ",subMsg=" + aError.getSubMsg());
          }
    
          public void onInitDone(InitResult initResult) {
            System.out.println("init success !!") ;
          }
        });
    
        // Before you proceed to the following steps, make sure the initialization is complete. You can specify a value based on your business requirement.
        Thread.sleep(2000);
      }
    
      /**
       * Send a message.
       * 
       @param topic The topic to which the message is sent.
       * @param payload The content of the message.
       */
      private static void publish(String topic, String payload) {
        MqttPublishRequest request = new MqttPublishRequest();
        request.topic = topic;
        request.payloadObj = payload;
        request.qos = 0;
        LinkKit.getInstance().getMqttClient().publish(request, new IConnectSendListener() {
          @Override
          public void onResponse(ARequest aRequest, AResponse aResponse) {
          }
    
          @Override
          public void onFailure(ARequest aRequest, AError aError) {
          }
        });
      }
    
      /**
       * Listen for downstream data.
       */
      private static void registerNotifyListener() {
        LinkKit.getInstance().registerOnNotifyListener(new IConnectNotifyListener() {
          @Override
          public boolean shouldHandle(String connectId, String topic) {
            return true;
          }
    
          @Override
          public void onNotify(String connectId, String topic, AMessage aMessage) {
            // Accept the call from the synchronous service and respond to the service.
            // Create a thread to avoid callback blocks.
            executorService.submit(() -> doService(topic, aMessage));
          }
    
          @Override
          public void onConnectStateChange(String connectId, ConnectState connectState) {
          }
        });
      }
    
      /**
       * Process the request that is used to call the synchronous service.
       * 
       * @param topic The topic of the request.
       * @param aMessage The content of the request.
       */
      private static void doService(String topic, AMessage aMessage) {
    
        String content = new String((byte[]) aMessage.getData());
        System.out.println("Service request topic:" + topic);
        System.out.println("Service request:" + content);
    
        /**
         * The aMessage parameter is a JSON text.
         *   {
         *     "id": "123",
         *     "version": "1.0",
         *     "params": // The required parameters for the synchronous service. These parameters vary based on the definition of the synchronous service.
         *       {
         *         "input": 50
         *       },
         *     "method": "thing.service.{tsl.service.identifier}"
         *   }
         */
        JSONObject request = JSONObject.parseObject(content);
        JSONObject params = request.getJSONObject("params");
        if (! params.containsKey("input")) { // Validate the request parameters.
          return;
        }
        Integer input = params.getInteger("input"); // Obtain the request parameters.
    
        /**
         * The response from the synchronous service is a JSON text.
         *   {
         *     "id": "123",  // The ID of the request.
         * "code": 200, // The HTTP status code 200 indicates a successful call.
         *     "data": {}    // The response from the synchronous service. The response varies based on the definition of the synchronous service.
         *   }
         */
        JSONObject response = new JSONObject();
        JSONObject data = new JSONObject();
        data.put("output", input + 1);
        response.put("id", request.get("id"));
        response.put("code", 200);
        response.put("data", data);
    
        // The response from the synchronous service.
        String respTopic = topic.replace("/request/", "/response/");
        publish(respTopic, response.toString());
        System.out.println("Service response topic:" + respTopic);
        System.out.println("Service response content" + response.toString());
      }
    }

    For information about the common codes that are used by devices, see Common codes on devices.

Step 3: Create a service SDK-based application

  1. Add the following dependencies to the pom.xml file.
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>aliyun-java-sdk-iot</artifactId>
        <version>7.0.0</version>
    </dependency>
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>aliyun-java-sdk-core</artifactId>
        <version>3.5.1</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.40</version>
    </dependency>

    For more information, see Use IoT Platform SDK for Java.

  2. Add a Java class and specify the information about your device in the Config.* file.
    /*   
     * Copyright © 2019 Alibaba. All rights reserved.
     */
    package com.aliyun.iot.demo.alink;
    
    import com.alibaba.fastjson.JSONObject;
    import com.aliyuncs.DefaultAcsClient;
    import com.aliyuncs.exceptions.ClientException;
    import com.aliyuncs.exceptions.ServerException;
    import com.aliyuncs.iot.model.v20180120.InvokeThingServiceRequest;
    import com.aliyuncs.iot.model.v20180120.InvokeThingServiceResponse;
    import com.aliyuncs.profile.DefaultProfile;
    import com.aliyuncs.profile.IClientProfile;
    
    /**
     * Call a synchronous service.<br>
     */
    public class InvokeSyncService {
    
      // ===================The start of the segment where you specify the required parameters===========================
      // Specify the information about your device in the Config.* file.
      // Specify the ID of the region.
      private static String regionId = "cn-shanghai";
      // Specify the AccessKey ID of your Alibaba Cloud account.
      private static String accessKeyID = "Config.accessKey";
      // Specify the AccessKey secret of your Alibaba Cloud account.
      private static String accessKeySecret = "Config.accessKeySecret";
      // Specify the ProductKey that you can obtain from the certificate of the device that processes the request.
      private static String productKey = "Config.productKey";
      // Specify the DeviceName that you can obtain from the certificate of the device that processes the request.
      private static String deviceName = "Config.deviceName";
      // ===================The end of the segment where you specify the required parameters===========================
    
      /**
       * 1. Initialize the SyncServiceProcessor class to simulate an online device.<br>
       * 2. Start the application to call a synchronous service. <br>
       * 
       * @param args
       * @throws ServerException
       * @throws ClientException
       */
      public static void main(String[] args) throws ServerException, ClientException {
    
        // Obtain exceptions from a client.
        DefaultAcsClient client = null;
        try {
          IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyID, accessKeySecret);
          DefaultProfile.addEndpoint(regionId, regionId, "Iot", "iot." + regionId + ".aliyuncs.com");
          client = new DefaultAcsClient(profile);
        } catch (Exception e) {
          System.out.println("create OpenAPI Client failed !! exception:" + e.getMessage());
        }
    
        // Specify the required parameters.
        InvokeThingServiceRequest request = new InvokeThingServiceRequest();
        request.setProductKey(productKey); // The ProductKey in the device certificate.
        request.setDeviceName(deviceName); // The DeviceName in the device certificate.
        request.setIdentifier("MySyncService"); // The identifier of the synchronous service that you want to call. The identifier varies based on the definition of the synchronous service.
        JSONObject json = new JSONObject(); // Create a request parameter. The request parameter is a JSON string.
        json.put("input", 50); // Specify a value based on the definition of the synchronous service.
        request.setArgs(json.toString());
    
        // Obtain a response from the synchronous service.
        InvokeThingServiceResponse response = client.getAcsResponse(request);
        if (response == null) {
          System.out.println("Failed to call the synchronous service");
          return;
        }
        System.out.println("requestId:" + response.getRequestId());
        System.out.println("code:" + response.getCode());
        System.out.println("success:" + response.getSuccess());
        System.out.println("error message:" + response.getErrorMessage());
        if (response ! = null && response.getSuccess()) { // The synchronous service is called. The returned value true indicates that a request is sent to the synchronous service, not that the synchronous service is run.
          System.out.println("The synchronous service is called");
          System.out.println("Message ID:" + response.getData().getMessageId());
          System.out.println("Results" + response.getData().getResult()); // If the synchronous service is called, results are returned.
        } else {
          System.out.println("Failed to call the synchronous service.");
        }
      }
    
    }

    For more information about the InvokeThingService operation, see InvokeThingService.