
com.huaweicloud.sdk.iot.device.bootstrap.BootstrapClient Maven / Gradle / Ivy
/*
* Copyright (c) 2020-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.huaweicloud.sdk.iot.device.bootstrap;
import com.huaweicloud.sdk.iot.device.IoTDevice;
import com.huaweicloud.sdk.iot.device.client.ClientConf;
import com.huaweicloud.sdk.iot.device.client.CustomOptions;
import com.huaweicloud.sdk.iot.device.transport.ActionListener;
import com.huaweicloud.sdk.iot.device.transport.Connection;
import com.huaweicloud.sdk.iot.device.transport.RawMessage;
import com.huaweicloud.sdk.iot.device.transport.RawMessageListener;
import com.huaweicloud.sdk.iot.device.transport.mqtt.MqttConnection;
import com.huaweicloud.sdk.iot.device.utils.IotUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.security.KeyStore;
import java.util.Base64;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 引导客户端,用于设备引导来获取服务端地址
*/
public class BootstrapClient implements RawMessageListener, Cloneable {
private static final Logger log = LogManager.getLogger(BootstrapClient.class);
/**
* 设备发放的设备侧CA证书,注意与IoTDA的设备侧区分开
*
* @deprecated 避免在sdk中固定路径,此处常量后续将从sdk中移除
*/
@Deprecated
private static final String BOOTSTRAP_CA_RES_PATH = "ca.jks";
/**
* 设备发放定义的TOPIC
*/
private static final String BOOTSTRAP_PUBLISH_TOPIC = "$oc/devices/%s/sys/bootstrap/up";
private static final String BOOTSTRAP_SUBSCRIBE_TOPIC = "$oc/devices/%s/sys/bootstrap/down";
/**
* 客户端配置
*/
private final ClientConf clientConf;
/**
* 平台CA证书提供者,用于设备端验证服务端
*/
private final PlatformCaProvider platformCaProvider;
private final String deviceId;
private final Connection connection;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private ActionListener listener;
/**
* 构造函数,使用密码创建
*
* @param bootstrapUri bootstrap server地址,比如ssl://iot-bs.cn-north-4.myhuaweicloud.com:8883
* @param deviceId 设备id
* @param deviceSecret 设备密码
* @deprecated 为避免在sdk中固定路径,此方法后续将移除,推荐使用 {@link #BootstrapClient(String bootstrapUri, String deviceId, String deviceSecret, PlatformCaProvider platformCaProvider)} 替代本方法
*/
@Deprecated
public BootstrapClient(String bootstrapUri, String deviceId, String deviceSecret) {
this(bootstrapUri, deviceId, deviceSecret, getPlatformCaProvider());
}
/**
* 构造函数,使用密码创建
*
* @param bootstrapUri bootstrap server地址,比如ssl://iot-bs.cn-north-4.myhuaweicloud.com:8883
* @param deviceId 设备id
* @param deviceSecret 设备密码
* @param platformCaProvider 平台CA证书提供者
* @since 1.1.3
*/
public BootstrapClient(String bootstrapUri, String deviceId, String deviceSecret,
PlatformCaProvider platformCaProvider) {
ClientConf conf = new ClientConf();
conf.setServerUri(bootstrapUri);
conf.setFile(platformCaProvider.getBootstrapCaFile());
conf.setDeviceId(deviceId);
conf.setSecret(deviceSecret);
this.clientConf = conf;
this.deviceId = deviceId;
this.platformCaProvider = platformCaProvider;
this.connection = new MqttConnection(conf, this);
log.info("create BootstrapClient, the deviceId is {}", conf.getDeviceId());
}
/**
* 构造函数,使用证书创建
*
* @param bootstrapUri bootstrap server地址,比如ssl://iot-bs.cn-north-4.myhuaweicloud.com:8883
* @param deviceId 设备id
* @param keyStore 证书容器
* @param keyPassword 证书密码
* @deprecated 为避免在sdk中固定路径,此方法后续将移除,推荐使用 {@link #BootstrapClient(String bootstrapUri, String deviceId, KeyStore keyStore, String keyPassword, PlatformCaProvider platformCaProvider)} 替代本方法
*/
@Deprecated
public BootstrapClient(String bootstrapUri, String deviceId, KeyStore keyStore, String keyPassword) {
this(bootstrapUri, deviceId, keyStore, keyPassword, getPlatformCaProvider());
}
/**
* 构造函数,使用证书创建
*
* @param bootstrapUri bootstrap server地址,比如ssl://iot-bs.cn-north-4.myhuaweicloud.com:8883
* @param deviceId 设备id
* @param keyStore 证书容器
* @param keyPassword 证书密码
* @param platformCaProvider 平台CA证书提供者
* @since 1.1.3
*/
public BootstrapClient(String bootstrapUri, String deviceId, KeyStore keyStore, String keyPassword,
PlatformCaProvider platformCaProvider) {
ClientConf conf = new ClientConf();
conf.setServerUri(bootstrapUri);
conf.setFile(platformCaProvider.getBootstrapCaFile());
conf.setDeviceId(deviceId);
conf.setKeyPassword(keyPassword);
conf.setKeyStore(keyStore);
this.clientConf = conf;
this.deviceId = deviceId;
this.platformCaProvider = platformCaProvider;
this.connection = new MqttConnection(conf, this);
log.info("create BootstrapClient, the deviceId is {}", conf.getDeviceId());
}
/**
* 构造函数,自注册场景下证书创建
*
* @param bootstrapUri bootstrap server地址,比如ssl://iot-bs.cn-north-4.myhuaweicloud.com:8883
* @param deviceId 设备id
* @param keyStore 证书容器
* @param keyPassword 证书密码
* @param scopeId scopeId, 自注册场景可从物联网平台获取
* @deprecated 为避免在sdk中固定路径,此方法后续将移除,推荐使用 {@link #BootstrapClient(String bootstrapUri, String deviceId, KeyStore keyStore, String keyPassword,
* String scopeId, PlatformCaProvider platformCaProvider)} 替代本方法
*/
@Deprecated
public BootstrapClient(String bootstrapUri, String deviceId, KeyStore keyStore, String keyPassword,
String scopeId) {
this(bootstrapUri, deviceId, keyStore, keyPassword, scopeId, getPlatformCaProvider());
}
/**
* 构造函数,自注册场景下证书创建
*
* @param bootstrapUri bootstrap server地址,比如ssl://iot-bs.cn-north-4.myhuaweicloud.com:8883
* @param deviceId 设备id
* @param keyStore 证书容器
* @param keyPassword 证书密码
* @param scopeId scopeId, 自注册场景可从物联网平台获取
* @param platformCaProvider 平台CA证书提供者
* @since 1.1.3
*/
public BootstrapClient(String bootstrapUri, String deviceId, KeyStore keyStore, String keyPassword,
String scopeId, PlatformCaProvider platformCaProvider) {
ClientConf conf = new ClientConf();
conf.setServerUri(bootstrapUri);
conf.setFile(platformCaProvider.getBootstrapCaFile());
conf.setDeviceId(deviceId);
conf.setKeyStore(keyStore);
conf.setKeyPassword(keyPassword);
conf.setScopeId(scopeId);
this.clientConf = conf;
this.deviceId = deviceId;
this.platformCaProvider = platformCaProvider;
this.connection = new MqttConnection(conf, this);
log.info("create BootstrapClient, the deviceId is {}", conf.getDeviceId());
}
/**
* 造函数,自注册场景下注册组秘钥创建
*
* @param bootstrapUri bootstrap server地址,比如ssl://iot-bs.cn-north-4.myhuaweicloud.com:8883
* @param deviceId 设备id
* @param groupSecret 注册组秘钥
* @param scopeId scopeId, 自注册场景可从物联网平台获取
* @param platformCaProvider 平台CA证书提供者
*/
public BootstrapClient(String bootstrapUri, String deviceId, String groupSecret, String scopeId,
PlatformCaProvider platformCaProvider) {
ClientConf conf = new ClientConf();
conf.setServerUri(bootstrapUri);
conf.setFile(platformCaProvider.getBootstrapCaFile());
conf.setDeviceId(deviceId);
conf.setSecret(IotUtil.shaHMac(Base64.getDecoder().decode(groupSecret), deviceId, conf.getCheckStamp()));
conf.setScopeId(scopeId);
this.clientConf = conf;
this.deviceId = deviceId;
this.platformCaProvider = platformCaProvider;
this.connection = new MqttConnection(conf, this);
log.info("create BootstrapClient, the deviceId is {}", conf.getDeviceId());
}
@Override
public void onMessageReceived(RawMessage message) {
String bsTopic = String.format(BOOTSTRAP_SUBSCRIBE_TOPIC, this.deviceId);
if (message.getTopic().equals(bsTopic)) {
Future success = executorService.submit(() -> listener.onSuccess(message.toString()), "success");
String result = "";
try {
result = success.get();
} catch (Exception e) {
log.error("get submit result failed, {}", e.getMessage());
}
if (result.equals("success")) {
log.debug("submit task succeeded");
}
}
}
private static DefaultPlatformCaProvider getPlatformCaProvider() {
return new DefaultPlatformCaProvider(BOOTSTRAP_CA_RES_PATH);
}
/**
* 发起设备引导
*
* @param listener 监听器用来接收引导结果
* @throws IllegalArgumentException 参数非法异常
*/
public void bootstrap(ActionListener listener) throws IllegalArgumentException {
this.listener = listener;
if (connection.connect() != 0) {
log.error("connect failed");
listener.onFailure(null, new Exception("connect failed"));
return;
}
String bsTopic = String.format(BOOTSTRAP_SUBSCRIBE_TOPIC, this.deviceId);
connection.subscribeTopic(bsTopic, null, 0);
String topic = String.format(BOOTSTRAP_PUBLISH_TOPIC, this.deviceId);
RawMessage rawMessage = new RawMessage(topic, "");
connection.publishMessage(rawMessage, null);
}
/**
* 发起设备引导
*
* @param listener 监听器用来接收引导结果
* @param message 上报的json数据
* @throws IllegalArgumentException 参数非法异常
*/
public void bootstrap(ActionListener listener, String message) throws IllegalArgumentException {
this.listener = listener;
if (connection.connect() != 0) {
log.error("connect failed");
listener.onFailure(null, new Exception("connect failed"));
return;
}
String bsTopic = String.format(BOOTSTRAP_SUBSCRIBE_TOPIC, this.deviceId);
connection.subscribeTopic(bsTopic, null, 0);
String topic = String.format(BOOTSTRAP_PUBLISH_TOPIC, this.deviceId);
RawMessage rawMessage = new RawMessage(topic, message);
connection.publishMessage(rawMessage, null);
}
/**
* 获取IoTDevice
*
* @param serverUri 服务端地址
* @return 设备类
* @since 1.1.3
*/
public IoTDevice getIoTDevice(String serverUri) {
try {
ClientConf conf = this.clientConf.clone();
conf.setServerUri(serverUri);
conf.setScopeId(null);
conf.setFile(this.platformCaProvider.getIotCaFile());
return new IoTDevice(conf);
} catch (CloneNotSupportedException exp) {
log.error("get iot device failed: {}", exp.getMessage());
return null;
}
}
/**
* 关闭客户端
*/
public void close() {
connection.close();
executorService.shutdown();
}
@Override
public BootstrapClient clone() throws CloneNotSupportedException {
return (BootstrapClient) super.clone();
}
public void setCustomOptions(CustomOptions customOptions) {
this.connection.setCustomOptions(customOptions);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy