提交 04d5289f authored 作者: wangqiang's avatar wangqiang

集成mqtt客户端

上级 ec270989
差异被折叠。
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>windows</id>
<formats>
<format>zip</format>
</formats>
<!-- Workaround to create logs directory -->
<fileSets>
<fileSet>
<directory>${pkg.win.dist}</directory>
<outputDirectory>logs</outputDirectory>
<excludes>
<exclude>*/**</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${pkg.win.dist}/conf</directory>
<outputDirectory>conf</outputDirectory>
<lineEnding>windows</lineEnding>
<excludes>
<exclude>*.der</exclude>
<exclude>*.cer</exclude>
<exclude>*.pfx</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${pkg.win.dist}/conf</directory>
<outputDirectory>conf</outputDirectory>
<includes>
<include>*.der</include>
<include>*.cer</include>
<include>*.pfx</include>
</includes>
</fileSet>
</fileSets>
<files>
<file>
<source>${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</source>
<outputDirectory>lib</outputDirectory>
<destName>${pkg.name}.jar</destName>
</file>
<file>
<source>${pkg.win.dist}/service.exe</source>
<outputDirectory/>
<destName>${pkg.name}.exe</destName>
</file>
<file>
<source>${pkg.win.dist}/service.xml</source>
<outputDirectory/>
<destName>${pkg.name}.xml</destName>
<lineEnding>windows</lineEnding>
</file>
<file>
<source>${pkg.win.dist}/install.bat</source>
<outputDirectory/>
<lineEnding>windows</lineEnding>
</file>
<file>
<source>${pkg.win.dist}/uninstall.bat</source>
<outputDirectory/>
<lineEnding>windows</lineEnding>
</file>
</files>
</assembly>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<appender name="fileLogAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${pkg.logFolder}/${pkg.name}.log</file>
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.priusis" level="INFO" />
<logger name="org.eclipse.milo" level="INFO" />
<logger name="org.eclipse.paho" level="INFO" />
<root level="INFO">
<appender-ref ref="fileLogAppender"/>
</root>
</configuration>
export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@"
export LOG_FILENAME=${pkg.name}.out
export LOADER_PATH=${pkg.installFolder}/conf
pkg.logFolder=${pkg.unixLogFolder}
\ No newline at end of file
pkg.logFolder=${BASE}\\logs
pkg.winWrapperLogFolder=%BASE%\\logs
......@@ -3,14 +3,15 @@ package com.priusis;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, SecurityAutoConfiguration.class})
public class HaikangClientApplication {
public static void main(String[] args) {
SpringApplication.run(HaikangClientApplication.class, args);
}
public static void main(String[] args) {
SpringApplication.run(HaikangClientApplication.class, args);
}
}
package com.priusis.client.data.kv;
import lombok.Data;
import java.io.Serializable;
@Data
public class AttributeKey implements Serializable {
private final String scope;
private final String attributeKey;
}
package com.priusis.client.data.kv;
public interface AttributeKvEntry extends KvEntry {
long getLastUpdateTs();
}
package com.priusis.client.data.kv;
import java.util.Optional;
public class BaseAttributeKvEntry implements AttributeKvEntry {
private final long lastUpdateTs;
private final KvEntry kv;
public BaseAttributeKvEntry(KvEntry kv, long lastUpdateTs) {
this.kv = kv;
this.lastUpdateTs = lastUpdateTs;
}
public BaseAttributeKvEntry(long lastUpdateTs, KvEntry kv) {
this(kv, lastUpdateTs);
}
@Override
public long getLastUpdateTs() {
return lastUpdateTs;
}
@Override
public String getKey() {
return kv.getKey();
}
@Override
public DataType getDataType() {
return kv.getDataType();
}
@Override
public Optional<String> getStrValue() {
return kv.getStrValue();
}
@Override
public Optional<Long> getLongValue() {
return kv.getLongValue();
}
@Override
public Optional<Boolean> getBooleanValue() {
return kv.getBooleanValue();
}
@Override
public Optional<Double> getDoubleValue() {
return kv.getDoubleValue();
}
@Override
public Optional<String> getJsonValue() {
return kv.getJsonValue();
}
@Override
public String getValueAsString() {
return kv.getValueAsString();
}
@Override
public Object getValue() {
return kv.getValue();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BaseAttributeKvEntry that = (BaseAttributeKvEntry) o;
if (lastUpdateTs != that.lastUpdateTs) return false;
return kv.equals(that.kv);
}
@Override
public int hashCode() {
int result = (int) (lastUpdateTs ^ (lastUpdateTs >>> 32));
result = 31 * result + kv.hashCode();
return result;
}
@Override
public String toString() {
return "BaseAttributeKvEntry{" +
"lastUpdateTs=" + lastUpdateTs +
", kv=" + kv +
'}';
}
}
package com.priusis.client.data.kv;
import java.util.Objects;
import java.util.Optional;
public abstract class BasicKvEntry implements KvEntry {
private final String key;
protected BasicKvEntry(String key) {
this.key = key;
}
@Override
public String getKey() {
return key;
}
@Override
public Optional<String> getStrValue() {
return Optional.ofNullable(null);
}
@Override
public Optional<Long> getLongValue() {
return Optional.ofNullable(null);
}
@Override
public Optional<Boolean> getBooleanValue() {
return Optional.ofNullable(null);
}
@Override
public Optional<Double> getDoubleValue() {
return Optional.ofNullable(null);
}
@Override
public Optional<String> getJsonValue() {
return Optional.ofNullable(null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BasicKvEntry)) return false;
BasicKvEntry that = (BasicKvEntry) o;
return Objects.equals(key, that.key);
}
@Override
public int hashCode() {
return Objects.hash(key);
}
@Override
public String toString() {
return "BasicKvEntry{" +
"key='" + key + '\'' +
'}';
}
}
package com.priusis.client.data.kv;
import java.util.Objects;
import java.util.Optional;
public class BooleanDataEntry extends BasicKvEntry {
private final Boolean value;
public BooleanDataEntry(String key, Boolean value) {
super(key);
this.value = value;
}
@Override
public DataType getDataType() {
return DataType.BOOLEAN;
}
@Override
public Optional<Boolean> getBooleanValue() {
return Optional.ofNullable(value);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BooleanDataEntry)) return false;
if (!super.equals(o)) return false;
BooleanDataEntry that = (BooleanDataEntry) o;
return Objects.equals(value, that.value);
}
@Override
public Object getValue() {
return value;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), value);
}
@Override
public String toString() {
return "BooleanDataEntry{" +
"value=" + value +
"} " + super.toString();
}
@Override
public String getValueAsString() {
return Boolean.toString(value);
}
}
package com.priusis.client.data.kv;
public enum DataType {
STRING, LONG, BOOLEAN, DOUBLE, JSON;
}
package com.priusis.client.data.kv;
import java.util.Objects;
import java.util.Optional;
public class DoubleDataEntry extends BasicKvEntry {
private final Double value;
public DoubleDataEntry(String key, Double value) {
super(key);
this.value = value;
}
@Override
public DataType getDataType() {
return DataType.DOUBLE;
}
@Override
public Optional<Double> getDoubleValue() {
return Optional.ofNullable(value);
}
@Override
public Object getValue() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof DoubleDataEntry)) return false;
if (!super.equals(o)) return false;
DoubleDataEntry that = (DoubleDataEntry) o;
return Objects.equals(value, that.value);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), value);
}
@Override
public String toString() {
return "DoubleDataEntry{" +
"value=" + value +
"} " + super.toString();
}
@Override
public String getValueAsString() {
return Double.toString(value);
}
}
package com.priusis.client.data.kv;
import java.util.Objects;
import java.util.Optional;
public class JsonDataEntry extends BasicKvEntry {
private final String value;
public JsonDataEntry(String key, String value) {
super(key);
this.value = value;
}
@Override
public DataType getDataType() {
return DataType.JSON;
}
@Override
public Optional<String> getJsonValue() {
return Optional.ofNullable(value);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof JsonDataEntry)) return false;
if (!super.equals(o)) return false;
JsonDataEntry that = (JsonDataEntry) o;
return Objects.equals(value, that.value);
}
@Override
public Object getValue() {
return value;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), value);
}
@Override
public String toString() {
return "JsonDataEntry{" +
"value=" + value +
"} " + super.toString();
}
@Override
public String getValueAsString() {
return value;
}
}
package com.priusis.client.data.kv;
import java.io.Serializable;
import java.util.Optional;
/**
* Represents attribute or any other KV data entry
*/
public interface KvEntry extends Serializable {
String getKey();
DataType getDataType();
Optional<String> getStrValue();
Optional<Long> getLongValue();
Optional<Boolean> getBooleanValue();
Optional<Double> getDoubleValue();
Optional<String> getJsonValue();
String getValueAsString();
Object getValue();
}
package com.priusis.client.data.kv;
import java.util.Objects;
import java.util.Optional;
public class LongDataEntry extends BasicKvEntry {
private final Long value;
public LongDataEntry(String key, Long value) {
super(key);
this.value = value;
}
@Override
public DataType getDataType() {
return DataType.LONG;
}
@Override
public Optional<Long> getLongValue() {
return Optional.ofNullable(value);
}
@Override
public Object getValue() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof LongDataEntry)) return false;
if (!super.equals(o)) return false;
LongDataEntry that = (LongDataEntry) o;
return Objects.equals(value, that.value);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), value);
}
@Override
public String toString() {
return "LongDataEntry{" +
"value=" + value +
"} " + super.toString();
}
@Override
public String getValueAsString() {
return Long.toString(value);
}
}
package com.priusis.client.data.kv;
import java.util.Objects;
import java.util.Optional;
public class StringDataEntry extends BasicKvEntry {
private static final long serialVersionUID = 1L;
private final String value;
public StringDataEntry(String key, String value) {
super(key);
this.value = value;
}
@Override
public DataType getDataType() {
return DataType.STRING;
}
@Override
public Optional<String> getStrValue() {
return Optional.ofNullable(value);
}
@Override
public Object getValue() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof StringDataEntry))
return false;
if (!super.equals(o))
return false;
StringDataEntry that = (StringDataEntry) o;
return Objects.equals(value, that.value);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), value);
}
@Override
public String toString() {
return "StringDataEntry{" + "value='" + value + '\'' + "} " + super.toString();
}
@Override
public String getValueAsString() {
return value;
}
}
package com.priusis.client.extensions;
import com.priusis.client.service.conf.PcExtensionConfiguration;
/**
* Created by priusis on 29.09.17.
*/
public interface ExtensionService {
PcExtensionConfiguration getCurrentConfiguration();
void init(PcExtensionConfiguration configuration) throws Exception;
void update(PcExtensionConfiguration configuration) throws Exception;
void destroy() throws Exception;
}
package com.priusis.client.extensions;
import com.priusis.client.service.conf.PcExtensionConfiguration;
public abstract class ExtensionUpdate implements ExtensionService {
public void update (PcExtensionConfiguration configurationNode) throws Exception {
destroy();
init(configurationNode);
}
}
package com.priusis.client.extensions.common.conf.mapping;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.priusis.client.data.kv.DataType;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* Created by priusis on 17.01.17.
*/
@Data
@AllArgsConstructor
public class DataTypeMapping {
private DataType dataType;
@JsonCreator
public static DataTypeMapping forValue(String value) {
return new DataTypeMapping(DataType.valueOf(value.toUpperCase()));
}
}
package com.priusis.client.extensions.common.conf.mapping;
import lombok.Data;
/**
* Created by priusis on 16.01.17.
*/
@Data
public class KVMapping {
private String key;
private DataTypeMapping type;
private String value;
private String ts;
private String tsFormat;
}
package com.priusis.client.extensions.http;
import com.priusis.client.data.kv.KvEntry;
import com.priusis.client.extensions.ExtensionUpdate;
import com.priusis.client.extensions.http.conf.HttpConfiguration;
import com.priusis.client.service.MqttDeliveryFuture;
import com.priusis.client.service.MqttRpcDataMessage;
import com.priusis.client.service.conf.PcExtensionConfiguration;
import com.priusis.client.service.core.MqttService;
import com.priusis.client.service.data.DeviceData;
import com.priusis.client.util.ConfigurationTools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static com.priusis.client.util.JsonTools.fromString;
import static com.priusis.client.util.JsonTools.getKvEntries;
@Slf4j
public class DefaultHttpService extends ExtensionUpdate implements HttpService {
private static final int OPERATION_TIMEOUT_IN_SEC = 10;
private final MqttService mqttService;
private PcExtensionConfiguration currentConfiguration;
private HttpConfiguration configuration;
public DefaultHttpService(MqttService mqttService) {
this.mqttService = mqttService;
}
@Override
public PcExtensionConfiguration getCurrentConfiguration() {
return currentConfiguration;
}
@Override
public void init(PcExtensionConfiguration configurationNode) throws IOException {
currentConfiguration = configurationNode;
try {
configuration = ConfigurationTools.readFileConfiguration(configurationNode.getExtensionConfiguration(), HttpConfiguration.class);
} catch (IOException e) {
log.error("[{}] Http service configuration failed!", mqttService.getTenantLabel(), e);
mqttService.onConfigurationError(e, currentConfiguration);
throw e;
}
}
@Override
public void destroy() throws Exception {
}
@Override
public void processRequest(String token, String body) throws Exception {
log.trace("[{}] Processing request body [{}] for token [{}]", mqttService.getTenantLabel(), body, token);
if (configuration != null) {
if (StringUtils.isEmpty(configuration.getToken()) || configuration.getToken().equals(token)) {
processBody(body);
} else {
log.error("[{}] Request token [{}] doesn't match configuration token!", mqttService.getTenantLabel(), token);
throw new SecurityException("Request token [" + token + "] doesn't match configuration token!");
}
}
}
@Override
public File flushRpcDataToFile(MqttRpcDataMessage mqttRpcDataMessage) throws IOException {
return mqttService.flushRpcDataToFile(mqttRpcDataMessage);
}
@Override
public MqttRpcDataMessage readFromFile(String method) throws IOException {
return mqttService.readFromFile(method);
}
private void processBody(String body) throws Exception {
List<KvEntry> attrData = getKvEntries(fromString(body));
DeviceData dd = new DeviceData(attrData);
if (dd != null) {
List<MqttDeliveryFuture> futures = new ArrayList<>();
if (!dd.getAttributes().isEmpty()) {
futures.add(mqttService.onDeviceAttributesUpdate(dd.getAttributes()));
}
for (Future future : futures) {
waitWithTimeout(future);
}
} else {
log.error("[{}] DeviceData is null. Body [{}] was not parsed successfully!", mqttService.getTenantLabel(), body);
throw new IllegalArgumentException("Device Data is null. Body [" + body + "] was not parsed successfully!");
}
}
private void waitWithTimeout(Future future) throws Exception {
future.get(OPERATION_TIMEOUT_IN_SEC, TimeUnit.SECONDS);
}
}
package com.priusis.client.extensions.http;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.priusis.client.extensions.http.conf.HttpRequestProcessingError;
import com.priusis.client.service.MqttRpcDataMessage;
import com.priusis.client.service.TenantManagerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static com.priusis.client.util.JsonTools.fromString;
@RestController
@Slf4j
public class HttpController {
@Autowired
private TenantManagerService service;
private ObjectMapper mapper = new ObjectMapper();
@RequestMapping(value = "/uplink/{token}", method = RequestMethod.POST)
public void handleRequest(@PathVariable String token, @RequestBody String body) throws Exception {
service.processRequest(token, body);
}
@RequestMapping(value = "/rpc_cmd/{method}", method = RequestMethod.GET)
public MqttRpcDataMessage getRpcCmdDataRequest(@PathVariable String method) throws Exception {
MqttRpcDataMessage mqttRpcDataMessage = service.readFromFile(method);
return mqttRpcDataMessage;
}
@RequestMapping(value = "/rpc_cmd", method = RequestMethod.POST)
public void handleRpcCmdDataRequest(@RequestBody String body) {
JsonNode payload = fromString(body);
MqttRpcDataMessage mqttRpcDataMessage = MqttRpcDataMessage.builder()
.method(payload.get("method").asText()).params(payload.get("params").toPrettyString()).build();
// 存储rpc下发的数据
try {
service.flushRpcDataToFile(mqttRpcDataMessage);
} catch (IOException e) {
log.error("Failed to process rpc command persistent : {}", body);
}
}
@ExceptionHandler(Exception.class)
public void handlePriusisiotException(Exception exception, HttpServletResponse response) {
log.debug("Processing exception {}", exception.getMessage(), exception);
if (!response.isCommitted()) {
try {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
if (exception instanceof SecurityException) {
response.setStatus(HttpStatus.FORBIDDEN.value());
mapper.writeValue(response.getWriter(),
new HttpRequestProcessingError("You don't have permission to perform this operation!"));
} else {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
mapper.writeValue(response.getWriter(), new HttpRequestProcessingError(exception.getMessage()));
}
} catch (IOException e) {
log.error("Can't handle exception", e);
}
}
}
}
package com.priusis.client.extensions.http;
import com.priusis.client.extensions.ExtensionService;
import com.priusis.client.service.MqttRpcDataMessage;
import java.io.File;
import java.io.IOException;
public interface HttpService extends ExtensionService {
void processRequest(String token, String body) throws Exception;
File flushRpcDataToFile(MqttRpcDataMessage mqttRpcDataMessage) throws IOException;
MqttRpcDataMessage readFromFile(String method) throws IOException;
}
package com.priusis.client.extensions.http.conf;
import lombok.Data;
@Data
public class HttpConfiguration {
private String token;
}
package com.priusis.client.extensions.http.conf;
import com.priusis.client.extensions.http.conf.mapping.HttpDeviceDataConverter;
import lombok.Data;
import java.util.List;
@Data
public class HttpConverterConfiguration {
private String token;
private List<HttpDeviceDataConverter> converters;
}
package com.priusis.client.extensions.http.conf;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class HttpRequestProcessingError {
private String message;
}
package com.priusis.client.extensions.http.conf.mapping;
import com.priusis.client.service.data.DeviceData;
import com.priusis.client.util.converter.BasicJsonConverter;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import java.util.regex.Pattern;
import static com.priusis.client.util.JsonTools.fromString;
@Data
@EqualsAndHashCode(callSuper = true)
@Slf4j
public class HttpDeviceDataConverter extends BasicJsonConverter {
public static final Pattern TAG_PATTERN = Pattern.compile("\\$\\{(.*?)\\}");
public DeviceData parseBody(String body) {
try {
return parseDeviceData(fromString(body));
} catch (Exception e) {
log.error("Exception occurred while parsing json request body [{}]", body, e);
throw new RuntimeException("Exception occurred while parsing json request body [" + body + "]", e);
}
}
}
package com.priusis.client.service;
import com.priusis.client.data.kv.KvEntry;
import java.util.List;
/**
* Created by priusis on 22.02.17.
*/
public interface AttributesUpdateListener {
void onAttributesUpdated(String deviceName, List<KvEntry> attributes);
}
package com.priusis.client.service;
import com.priusis.client.extensions.ExtensionService;
import com.priusis.client.extensions.http.HttpService;
import com.priusis.client.service.conf.PcCoreConfiguration;
import com.priusis.client.service.conf.PcExtensionConfiguration;
import com.priusis.client.service.core.MqttService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
/**
* Created by priusis on 29.09.17.
*/
@Slf4j
public abstract class DefaultTenantManagerService implements TenantManagerService {
@Autowired
private PcCoreConfiguration configuration;
private Map<String, TenantServiceRegistry> gateways;
private HttpService httpService;
public abstract MqttService getGatewayService(PcCoreConfiguration configuration, Consumer<String> extensionsConfigListener);
@PostConstruct
public void init() {
gateways = new HashMap<>();
String label = configuration.getLabel();
log.info("[{}] Initializing client", configuration.getLabel());
MqttService service = null;
try {
TenantServiceRegistry tenantServiceRegistry = new TenantServiceRegistry();
service = getGatewayService(configuration, c -> {
});
tenantServiceRegistry.setService(service);
for (PcExtensionConfiguration extensionConfiguration : configuration.getExtensions()) {
log.info("[{}] Initializing extension: [{}]", configuration.getLabel(), extensionConfiguration.getType());
ExtensionService extension = tenantServiceRegistry.createExtensionServiceByType(service, extensionConfiguration.getType());
extension.init(extensionConfiguration);
if (extensionConfiguration.getType().equals("HTTP")) {
httpService = (HttpService) extension;
}
}
gateways.put(label, (TenantServiceRegistry) tenantServiceRegistry);
} catch (Exception e) {
log.info("[{}] Failed to initialize the service ", label, e);
try {
if (service != null) {
service.destroy();
}
} catch (Exception exc) {
log.info("[{}] Failed to stop the service ", label, exc);
}
}
}
@Override
public void processRequest(String token, String body) throws Exception {
httpService.processRequest(token, body);
}
@Override
public File flushRpcDataToFile(MqttRpcDataMessage mqttRpcDataMessage) throws IOException {
return httpService.flushRpcDataToFile(mqttRpcDataMessage);
}
@Override
public MqttRpcDataMessage readFromFile(String method) throws IOException {
return httpService.readFromFile(method);
}
@PreDestroy
public void stop() {
for (String label : gateways.keySet()) {
try {
TenantServiceRegistry registry = gateways.get(label);
for (ExtensionService extension : registry.getExtensions().values()) {
try {
extension.destroy();
} catch (Exception e) {
log.info("[{}] Failed to stop the extension ", label, e);
}
}
registry.getService().destroy();
} catch (Exception e) {
log.info("[{}] Failed to stop the service ", label, e);
}
}
}
}
package com.priusis.client.service;
import com.priusis.client.extensions.ExtensionService;
import com.priusis.client.service.core.MqttService;
public interface ExtensionServiceCreation {
ExtensionService createExtensionServiceByType(MqttService mqttService, String type);
}
package com.priusis.client.service;
import io.netty.util.concurrent.Future;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class MessageFuturePair {
Future<? super Void> future;
MqttPersistentMessage message;
}
package com.priusis.client.service;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.function.Consumer;
@Data
@AllArgsConstructor
public class MqttCallbackWrapper {
private Consumer<Void> successCallback;
private Consumer<Throwable> failureCallback;
}
package com.priusis.client.service;
import java.util.concurrent.CompletableFuture;
/**
* Created by priusis on 23.03.17.
*/
public class MqttDeliveryFuture extends CompletableFuture<Boolean> {
}
package com.priusis.client.service;
import io.netty.util.concurrent.Future;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.BlockingQueue;
import java.util.function.Consumer;
@Slf4j
public class MqttMessageReceiver implements Runnable {
private static final int INCOMING_QUEUE_DEFAULT_WARNING_THRESHOLD = 1000;
private PersistentFileService persistentFileService;
private BlockingQueue<MessageFuturePair> incomingQueue;
private Consumer<Void> defaultSuccessCallback = message -> log.debug("Successfully sent message: [{}]", message);
private Consumer<Throwable> defaultFailureCallback = e -> log.warn("Failed to send message: [{}]", e);
private int incomingQueueWarningThreshold;
public MqttMessageReceiver(PersistentFileService persistentFileService,
BlockingQueue<MessageFuturePair> incomingQueue,
int warningThreshold) {
this.persistentFileService = persistentFileService;
this.incomingQueue = incomingQueue;
this.incomingQueueWarningThreshold = warningThreshold == 0 ? INCOMING_QUEUE_DEFAULT_WARNING_THRESHOLD : warningThreshold;
}
@Override
public void run() {
while (!Thread.interrupted()) {
checkIncomingQueueSize();
try {
MessageFuturePair messageFuturePair = incomingQueue.take();
Future<?> future = messageFuturePair.getFuture();
MqttPersistentMessage message = messageFuturePair.getMessage();
if (future.isSuccess()) {
Consumer<Void> successCallback = persistentFileService.getSuccessCallback(message.getId()).orElse(defaultSuccessCallback);
successCallback.accept(null);
persistentFileService.resolveFutureSuccess(message.getId());
} else {
persistentFileService.saveForResend(message);
persistentFileService.getFailureCallback(message.getId()).orElse(defaultFailureCallback).accept(future.cause());
persistentFileService.resolveFutureFailed(message.getId(), future.cause());
log.warn("Failed to send message [{}] due to [{}]", message, future.cause());
}
} catch (InterruptedException e) {
log.info(e.getMessage());
Thread.currentThread().interrupt();
break;
} catch (Throwable e) {
log.error(e.getMessage(), e);
}
}
}
private void checkIncomingQueueSize() {
if (incomingQueue.size() > incomingQueueWarningThreshold) {
log.warn("Incoming queue has [{}] messages which is more than thee specified threshold of [{}]", incomingQueue.size(), incomingQueueWarningThreshold);
}
}
}
package com.priusis.client.service;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
import java.util.UUID;
@Data
@Builder
public class MqttPersistentMessage implements Serializable {
private static final long serialVersionUID = -3133461476074777891L;
private UUID id;
private long timestamp;
private int messageId;
private String topic;
private byte[] payload;
@Override
public String toString() {
return "{payload=" + new String(payload) +
", timestamp=" + timestamp +
", topic='" + topic + '\'' +
", id=" + id +
", messageId=" + messageId +
'}';
}
}
package com.priusis.client.service;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
@Data
@Builder
public class MqttRpcDataMessage implements Serializable {
private static final long serialVersionUID = -3133461476074777891L;
private int requestId;
private String method;
private String params;
@Override
public String toString() {
return "{requestId=" + requestId +
", method=" + method +
", params=" + params +
'}';
}
}
package com.priusis.client.service;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Consumer;
public interface PersistentFileService {
MqttDeliveryFuture persistMessage(String topic, int msgId, byte[] payload,
Consumer<Void> onSuccess, Consumer<Throwable> onFailure) throws IOException;
/**
* Returns a list of the messages that are to be sent. If storage files exist, then the messages from oldest
* storage file are returned and file is deleted. If no storage file exists, then returns the messages that are currently
* in storage buffer and clears it
*
* @return {@see List} of {@see MqttPersistentMessage} to be sent
* @throws IOException
*/
List<MqttPersistentMessage> getPersistentMessages() throws IOException;
/**
* Returns a list of the messages that are to be re-sent
*
* @return {@see List} of {@see MqttPersistentMessage} to be re-sent
* @throws IOException
*/
List<MqttPersistentMessage> getResendMessages() throws IOException;
void resolveFutureSuccess(UUID id);
void resolveFutureFailed(UUID id, Throwable e);
Optional<MqttDeliveryFuture> getMqttDeliveryFuture(UUID id);
boolean deleteMqttDeliveryFuture(UUID id);
Optional<Consumer<Void>> getSuccessCallback(UUID id);
Optional<Consumer<Throwable>> getFailureCallback(UUID id);
void saveForResend(MqttPersistentMessage message) throws IOException;
void saveForResend(List<MqttPersistentMessage> messages) throws IOException;
File flushRpcDataToFile(MqttRpcDataMessage mqttRpcDataMessage) throws IOException;
MqttRpcDataMessage readFromFile(String method) throws IOException;
}
package com.priusis.client.service;
import com.priusis.client.service.conf.PcPersistenceConfiguration;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.PostConstruct;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
@Slf4j
public class PersistentFileServiceImpl implements PersistentFileService {
private static final String STORAGE_FILE_PREFIX = "pc-storage-";
private PcPersistenceConfiguration persistence;
private ConcurrentLinkedDeque<MqttPersistentMessage> sendBuffer;
private ConcurrentLinkedDeque<MqttPersistentMessage> resendBuffer;
private ConcurrentMap<UUID, MqttCallbackWrapper> callbacks;
private Map<UUID, MqttDeliveryFuture> futures;
private File storageDir;
@PostConstruct
public void init() {
callbacks = new ConcurrentHashMap<>();
futures = new ConcurrentHashMap<>();
initStorageDir();
initBuffers();
}
private void initStorageDir() {
storageDir = new File(persistence.getPath());
if (!storageDir.exists()) {
storageDir.mkdirs();
}
}
private void initBuffers() {
sendBuffer = new ConcurrentLinkedDeque<>();
resendBuffer = new ConcurrentLinkedDeque<>();
}
@Override
public MqttDeliveryFuture persistMessage(String topic, int msgId, byte[] payload,
Consumer<Void> onSuccess,
Consumer<Throwable> onFailure) throws IOException {
MqttPersistentMessage message = MqttPersistentMessage.builder().id(UUID.randomUUID())
.topic(topic).messageId(msgId).payload(payload).build();
MqttDeliveryFuture future = new MqttDeliveryFuture();
addMessageToBuffer(message);
callbacks.put(message.getId(), new MqttCallbackWrapper(onSuccess, onFailure));
futures.put(message.getId(), future);
return future;
}
@Override
public List<MqttPersistentMessage> getPersistentMessages() throws IOException {
return getMqttPersistentMessages(sendBuffer);
}
@Override
public List<MqttPersistentMessage> getResendMessages() throws IOException {
return getMqttPersistentMessages(resendBuffer);
}
private List<MqttPersistentMessage> getMqttPersistentMessages(ConcurrentLinkedDeque<MqttPersistentMessage> buffer) throws IOException {
List<MqttPersistentMessage> messages = new ArrayList<>(buffer);
buffer.clear();
return messages;
}
@Override
public void resolveFutureSuccess(UUID id) {
callbacks.remove(id);
MqttDeliveryFuture future = futures.remove(id);
if (future != null) {
future.complete(Boolean.TRUE);
}
}
@Override
public void resolveFutureFailed(UUID id, Throwable e) {
MqttDeliveryFuture future = futures.remove(id);
if (future != null) {
future.completeExceptionally(e);
}
}
@Override
public Optional<MqttDeliveryFuture> getMqttDeliveryFuture(UUID id) {
return Optional.of(futures.get(id));
}
@Override
public boolean deleteMqttDeliveryFuture(UUID id) {
return futures.remove(id) != null;
}
@Override
public Optional<Consumer<Void>> getSuccessCallback(UUID id) {
MqttCallbackWrapper mqttCallbackWrapper = callbacks.get(id);
if (mqttCallbackWrapper == null) {
return Optional.empty();
}
return Optional.ofNullable(mqttCallbackWrapper.getSuccessCallback());
}
@Override
public Optional<Consumer<Throwable>> getFailureCallback(UUID id) {
MqttCallbackWrapper mqttCallbackWrapper = callbacks.get(id);
if (mqttCallbackWrapper == null) {
return Optional.empty();
}
return Optional.ofNullable(mqttCallbackWrapper.getFailureCallback());
}
@Override
public void saveForResend(MqttPersistentMessage message) throws IOException {
if (resendBuffer.size() >= persistence.getBufferSize()) {
log.info("Resend message too long gt bufferSize [{}]", sendBuffer.size());
//resendFileCounter = getFileCounter(resendFiles);
//resendFiles.add(flushBufferToFile(resendBuffer, RESEND_FILE_PREFIX + resendFileCounter));
}
resendBuffer.add(message);
}
@Override
public void saveForResend(List<MqttPersistentMessage> messages) throws IOException {
for (MqttPersistentMessage message : messages) {
saveForResend(message);
}
}
@Override
public File flushRpcDataToFile(MqttRpcDataMessage mqttRpcDataMessage) throws IOException {
ObjectOutputStream outStream = null;
try {
File newFile = new File(storageDir, STORAGE_FILE_PREFIX + mqttRpcDataMessage.getMethod());
outStream = new ObjectOutputStream(new FileOutputStream(newFile));
outStream.writeObject(mqttRpcDataMessage);
return newFile;
} catch (IOException e) {
log.error(e.getMessage(), e);
throw e;
} finally {
try {
if (outStream != null)
outStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
throw e;
}
}
}
@Override
public MqttRpcDataMessage readFromFile(String method) throws IOException {
File file = new File(storageDir, STORAGE_FILE_PREFIX + method);
ObjectInputStream inputStream = null;
MqttRpcDataMessage result = null;
try {
inputStream = new ObjectInputStream(new FileInputStream(file));
result = (MqttRpcDataMessage) inputStream.readObject();
} catch (EOFException e) {
return result;
} catch (ClassNotFoundException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
throw e;
} finally {
try {
if (inputStream != null)
inputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
throw e;
}
}
return result;
}
private void addMessageToBuffer(MqttPersistentMessage message) throws IOException {
if (sendBuffer.size() >= persistence.getBufferSize()) {
log.info("Send message too long gt bufferSize [{}]", sendBuffer.size());
//storageFiles.add(flushBufferToFile(sendBuffer, STORAGE_FILE_PREFIX + storageFileCounter++));
}
sendBuffer.add(message);
}
public void setPersistence(PcPersistenceConfiguration persistence) {
this.persistence = persistence;
}
}
package com.priusis.client.service;
import java.io.File;
import java.io.IOException;
/**
* Created by priusis on 29.09.17.
*/
public interface TenantManagerService {
void processRequest(String token, String body) throws Exception;
File flushRpcDataToFile(MqttRpcDataMessage mqttRpcDataMessage) throws IOException;
MqttRpcDataMessage readFromFile(String method) throws IOException;
}
package com.priusis.client.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.priusis.client.extensions.ExtensionService;
import com.priusis.client.extensions.http.DefaultHttpService;
import com.priusis.client.extensions.http.HttpService;
import com.priusis.client.service.conf.PcExtensionConfiguration;
import com.priusis.client.service.core.MqttService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by priusis on 29.09.17.
*/
@Slf4j
@Data
public class TenantServiceRegistry implements ExtensionServiceCreation {
private MqttService service;
private final Map<String, ExtensionService> extensions;
private final Map<String, HttpService> httpServices;
private static final String STATUS_INIT = "Initialized";
private static final String STATUS_UPDATE = "Updated";
private static final String STATUS_DELETE = "Removed";
private static final String HTTP_EXTENSION = "HTTP";
private static final String OPC_EXTENSION = "OPC UA";
private static final String MQTT_EXTENSION = "MQTT";
private static final String FILE_EXTENSION = "FILE";
public TenantServiceRegistry() {
this.extensions = new HashMap<>();
this.httpServices = new HashMap<>();
}
public void updateExtensionConfiguration(String config) {
log.info("[{}] Updating extension configuration", service.getTenantLabel());
ObjectMapper mapper = new ObjectMapper();
try {
List<PcExtensionConfiguration> updatedConfigurations = new ArrayList<>();
for (JsonNode updatedExtension : mapper.readTree(config)) {
updatedConfigurations.add(mapper.treeToValue(updatedExtension, PcExtensionConfiguration.class));
}
for (String existingExtensionId : extensions.keySet()) {
if (!extensionIdContainsInArray(existingExtensionId, updatedConfigurations)) {
log.info("Destroying extension: [{}]", existingExtensionId);
extensions.get(existingExtensionId).destroy();
extensions.remove(existingExtensionId);
httpServices.remove(existingExtensionId);
service.onConfigurationStatus(existingExtensionId, STATUS_DELETE);
}
}
for (PcExtensionConfiguration updatedConfiguration : updatedConfigurations) {
if (!extensions.containsKey(updatedConfiguration.getId())) {
log.info("Initializing extension: [{}][{}]", updatedConfiguration.getId(), updatedConfiguration.getType());
ExtensionService extension = createExtensionServiceByType(service, updatedConfiguration.getType());
service.onConfigurationStatus(updatedConfiguration.getId(), STATUS_INIT);
if (HTTP_EXTENSION.equals(updatedConfiguration.getType())) {
httpServices.put(updatedConfiguration.getId(), (HttpService) extension);
}
extensions.put(updatedConfiguration.getId(), extension);
} else {
if (!updatedConfiguration.equals(extensions.get(updatedConfiguration.getId()).getCurrentConfiguration())) {
log.info("Updating extension: [{}][{}]", updatedConfiguration.getId(), updatedConfiguration.getType());
extensions.get(updatedConfiguration.getId()).update(updatedConfiguration);
service.onConfigurationStatus(updatedConfiguration.getId(), STATUS_UPDATE);
}
}
}
} catch (Exception e) {
log.info("Failed to read configuration attribute", e);
throw new RuntimeException("Failed to update configuration", e);
}
}
private boolean extensionIdContainsInArray(String extensionId, List<PcExtensionConfiguration> array) {
for (PcExtensionConfiguration configuration : array) {
if (configuration.getId().equalsIgnoreCase(extensionId)) {
return true;
}
}
return false;
}
@Override
public ExtensionService createExtensionServiceByType(MqttService gateway, String type) {
switch (type) {
case HTTP_EXTENSION:
return new DefaultHttpService(gateway);
default:
throw new IllegalArgumentException("Extension: " + type + " is not supported!");
}
}
}
package com.priusis.client.service.conf;
import com.priusis.client.service.core.MqttClientSecurityConfiguration;
import lombok.Data;
/**
* Created by priusis on 18.01.17.
*/
@Data
public class PcConnectionConfiguration {
private String host;
private int port;
private long retryInterval;
private long connectionTimeout;
private int maxInFlight;
private int maxQueueSize;
private int incomingQueueWarningThreshold;
private MqttClientSecurityConfiguration security;
}
package com.priusis.client.service.conf;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* Created by priusis on 29.09.17.
*/
@Configuration
@ConfigurationProperties(prefix = "core")
@Data
public class PcCoreConfiguration {
private String label;
private PcReportingConfiguration reporting;
private PcPersistenceConfiguration persistence;
private PcConnectionConfiguration connection;
private List<PcExtensionConfiguration> extensions;
}
package com.priusis.client.service.conf;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
/**
* Created by priusis on 29.09.17.
*/
@Data
public class PcExtensionConfiguration {
private String id;
private String type;
private JsonNode configuration;
private String extensionConfiguration;
}
package com.priusis.client.service.conf;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* Created by priusis on 24.01.17.
*/
@Data
@Slf4j
public class PcPersistenceConfiguration {
private String type;
private String path;
private int bufferSize;
private long pollingInterval;
}
package com.priusis.client.service.conf;
import lombok.Data;
/**
* Created by priusis on 24.01.17.
*/
@Data
public class PcReportingConfiguration {
private long interval;
private int maxErrorsPerInterval;
}
package com.priusis.client.service.core;
import lombok.Data;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Properties;
/**
* Created by priusis on 18.01.17.
*/
@Data
public class MqttClientSecurityConfiguration {
private String accessToken;
private String keystore;
private String keystorePassword;
private String keystoreKeyAlias;
private String truststore;
private String truststorePassword;
public boolean isTokenBased() {
return !StringUtils.isEmpty(accessToken);
}
public boolean isSsl() {
return !StringUtils.isEmpty(truststore);
}
public void setupSecurityOptions(MqttConnectOptions options) {
if (this.isTokenBased()) {
options.setUserName(this.getAccessToken());
if (!StringUtils.isEmpty(this.getTruststore())) {
Properties sslProperties = new Properties();
sslProperties.put(SSLSocketFactoryFactory.TRUSTSTORE, this.getTruststore());
sslProperties.put(SSLSocketFactoryFactory.TRUSTSTOREPWD, this.getTruststorePassword());
sslProperties.put(SSLSocketFactoryFactory.TRUSTSTORETYPE, "JKS");
sslProperties.put(SSLSocketFactoryFactory.CLIENTAUTH, false);
options.setSSLProperties(sslProperties);
}
} else {
//TODO: check and document this
Properties sslProperties = new Properties();
sslProperties.put(SSLSocketFactoryFactory.KEYSTORE, this.getKeystore());
sslProperties.put(SSLSocketFactoryFactory.KEYSTOREPWD, this.getKeystorePassword());
sslProperties.put(SSLSocketFactoryFactory.KEYSTORETYPE, "JKS");
sslProperties.put(SSLSocketFactoryFactory.TRUSTSTORE, this.getTruststore());
sslProperties.put(SSLSocketFactoryFactory.TRUSTSTOREPWD, this.getTruststorePassword());
sslProperties.put(SSLSocketFactoryFactory.TRUSTSTORETYPE, "JKS");
sslProperties.put(SSLSocketFactoryFactory.CLIENTAUTH, true);
options.setSSLProperties(sslProperties);
}
}
public String getClientId() {
if (this.isTokenBased()) {
return sha256(this.getAccessToken().getBytes(StandardCharsets.UTF_8));
} else {
try {
FileInputStream is = new FileInputStream(this.getKeystore());
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, this.getKeystorePassword().toCharArray());
Key key = keystore.getKey(this.getKeystoreKeyAlias(), this.getKeystorePassword().toCharArray());
if (key instanceof PrivateKey) {
// Get certificate of public key
java.security.cert.Certificate cert = keystore.getCertificate(this.getKeystoreKeyAlias());
// Get public key
PublicKey publicKey = cert.getPublicKey();
return sha256(publicKey.getEncoded());
} else {
throw new RuntimeException("No public key!");
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
private String sha256(byte[] data) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(data);
return Base64Utils.encodeToString(md.digest());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
package com.priusis.client.service.data;
import lombok.Builder;
import lombok.Data;
/**
* Created by priusis on 02.03.17.
*/
@Data
@Builder
public class AttributeRequest {
private final int requestId;
private final String deviceName;
private final String attributeKey;
private final boolean clientScope;
private final String topicExpression;
private final String valueExpression;
}
package com.priusis.client.service.data;
import lombok.Data;
/**
* Created by priusis on 02.03.17.
*/
@Data
public class AttributeRequestKey {
private final int requestId;
private final String deviceName;
}
package com.priusis.client.service.data;
import lombok.Data;
import java.util.function.Consumer;
/**
* Created by priusis on 03.03.17.
*/
@Data
public class AttributeRequestListener {
private final AttributeRequest request;
private final Consumer<AttributeResponse> listener;
}
package com.priusis.client.service.data;
import com.priusis.client.data.kv.KvEntry;
import lombok.Builder;
import lombok.Data;
import java.util.Optional;
/**
* Created by priusis on 02.03.17.
*/
@Data
@Builder
public class AttributeResponse {
private final int requestId;
private final String deviceName;
private final String key;
private final boolean clientScope;
private final Optional<KvEntry> data;
private final String topicExpression;
private final String valueExpression;
}
package com.priusis.client.service.data;
import com.priusis.client.service.AttributesUpdateListener;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* Created by priusis on 23.02.17.
*/
@Data
@AllArgsConstructor
public class AttributesUpdateSubscription {
private String deviceNameFilter;
private AttributesUpdateListener listener;
public boolean matches(String deviceName) {
return deviceName.matches(deviceNameFilter);
}
}
package com.priusis.client.service.data;
import lombok.Data;
/**
* Created by priusis on 20.01.17.
*/
@Data
public class DeviceInfo {
private final String name;
private final String type;
}
package com.priusis.client.service.data;
import lombok.Data;
/**
* Created by priusis on 22.02.17.
*/
@Data
public class RpcCommandData {
private int requestId;
private String method;
private String params;
}
package com.priusis.client.service.data;
import lombok.Data;
/**
* Created by priusis on 22.02.17.
*/
@Data
public class RpcCommandResponse {
private int requestId;
private String deviceName;
private String data;
}
package com.priusis.client.service.update;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class DefaultUpdateService implements UpdateService {
private static final String INSTANCE_ID_FILE = ".instance_id";
private static final String UPDATE_SERVER_BASE_URL = "https://updates.priusisiot.io";
private static final String PLATFORM_PARAM = "platform";
private static final String VERSION_PARAM = "version";
private static final String INSTANCE_ID_PARAM = "instanceId";
@Value("${updates.enabled}")
private boolean updatesEnabled;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private ScheduledFuture checkUpdatesFuture = null;
private RestTemplate restClient = new RestTemplate();
private String platform;
private String version;
private UUID instanceId = null;
@PostConstruct
private void init() {
if (updatesEnabled) {
try {
platform = System.getProperty("platform", "unknown");
version = getClass().getPackage().getImplementationVersion();
if (version == null) {
version = "unknown";
}
Path instanceIdPath = Paths.get(INSTANCE_ID_FILE);
if (Files.exists(instanceIdPath)) {
byte[] data = Files.readAllBytes(instanceIdPath);
if (data != null && data.length > 0) {
try {
instanceId = UUID.fromString(new String(data));
} catch (IllegalArgumentException e) {
}
}
}
if (instanceId == null) {
instanceId = UUID.randomUUID();
Files.write(instanceIdPath, instanceId.toString().getBytes());
}
checkUpdatesFuture = scheduler.scheduleAtFixedRate(checkUpdatesRunnable, 0, 1, TimeUnit.HOURS);
} catch (Exception e) {}
}
}
@PreDestroy
private void destroy() {
try {
if (checkUpdatesFuture != null) {
checkUpdatesFuture.cancel(true);
}
scheduler.shutdownNow();
} catch (Exception e) {}
}
Runnable checkUpdatesRunnable = new Runnable() {
@Override
public void run() {
try {
log.trace("Executing check update method for instanceId [{}], platform [{}] and version [{}]", instanceId, platform, version);
ObjectNode request = new ObjectMapper().createObjectNode();
request.put(PLATFORM_PARAM, platform);
request.put(VERSION_PARAM, version);
request.put(INSTANCE_ID_PARAM, instanceId.toString());
JsonNode response = restClient.postForObject(UPDATE_SERVER_BASE_URL+"/api/pi-gateway/updates", request, JsonNode.class);
if (response.get("updateAvailable").asBoolean()) {
log.info(response.get("message").asText());
}
} catch (Exception e) {
log.trace(e.getMessage());
}
}
};
}
package com.priusis.client.service.update;
public interface UpdateService {
}
package com.priusis.client.util;
import lombok.Data;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
/**
* Created by priusis on 16.01.17.
*/
@Data
public class CertificateInfo {
private final X509Certificate certificate;
private final KeyPair keyPair;
}
package com.priusis.client.util;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.X509Certificate;
/**
* Created by priusis on 16.01.17.
*/
@Slf4j
public class ConfigurationTools {
private static final ObjectMapper mapper = new ObjectMapper();
public static <T> T readConfiguration(JsonNode configurationNode, Class<T> clazz) throws IOException {
try {
return mapper.treeToValue(configurationNode, clazz);
} catch (IOException e) {
log.error("Failed to load {} configuration from {}", clazz, configurationNode);
throw e;
}
}
public static <T> T readFileConfiguration(String configurationFile, Class<T> clazz) throws IOException {
try {
return mapper.readValue(getFileAsStream(configurationFile), clazz);
} catch (IOException e) {
log.error("Failed to load {} configuration from {}", clazz, configurationFile);
throw e;
}
}
public static CertificateInfo loadCertificate(KeystoreConfiguration configuration, Boolean isRemote) throws GeneralSecurityException, IOException {
try {
KeyStore keyStore = KeyStore.getInstance(configuration.getType());
if (isRemote) {
keyStore.load(getResourceAsStream(configuration.getFileContent()), configuration.getPassword().toCharArray());
} else {
keyStore.load(getFileAsStream(configuration.getLocation()), configuration.getPassword().toCharArray());
}
Key key = keyStore.getKey(configuration.getAlias(), configuration.getKeyPassword().toCharArray());
if (key instanceof PrivateKey) {
X509Certificate certificate = (X509Certificate) keyStore.getCertificate(configuration.getAlias());
PublicKey publicKey = certificate.getPublicKey();
KeyPair keyPair = new KeyPair(publicKey, (PrivateKey) key);
return new CertificateInfo(certificate, keyPair);
} else {
throw new GeneralSecurityException(configuration.getAlias() + " is not a private key!");
}
} catch (IOException | GeneralSecurityException e) {
log.error("Keystore configuration: [{}] is invalid!", configuration, e);
throw e;
}
}
private static InputStream getResourceAsStream(String fileContent) {
byte[] decoded = Base64.decodeBase64(fileContent);
return new ByteArrayInputStream(decoded);
}
private static InputStream getFileAsStream(String configurationFile) {
return ConfigurationTools.class.getClassLoader().getResourceAsStream(configurationFile);
}
}
package com.priusis.client.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.priusis.client.data.kv.*;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* Created by priusis on 19.01.17.
*/
@Slf4j
public class JsonTools {
private static final ObjectMapper JSON = new ObjectMapper();
public static ObjectNode newNode() {
return JSON.createObjectNode();
}
public static byte[] toBytes(ObjectNode node) {
return toString(node).getBytes(StandardCharsets.UTF_8);
}
public static byte[] toNodeBytes(ObjectNode node) {
return toString2(node).getBytes(StandardCharsets.UTF_8);
}
private static String toString2(ObjectNode node) {
try {
Map<String, Object> nodeData = new HashMap<>();
for (Iterator<Map.Entry<String, JsonNode>> it = node.fields(); it.hasNext(); ) {
Map.Entry<String, JsonNode> field = it.next();
String key = field.getKey();
JsonNode value = field.getValue();
nodeData.put(key, value);
}
return JSON.writeValueAsString(nodeData);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static JsonNode fromString(String data) {
try {
return JSON.readTree(data);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T fromString(String data, TypeReference<T> type) {
try {
return JSON.readValue(data, type);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static String toString(JsonNode node) {
try {
return JSON.writeValueAsString(node);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static String toString(Map<String, String> map) {
try {
return JSON.writeValueAsString(map);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static void putToNode(ObjectNode node, KvEntry kv) {
switch (kv.getDataType()) {
case BOOLEAN:
node.put(kv.getKey(), kv.getBooleanValue().get());
break;
case STRING:
node.put(kv.getKey(), kv.getStrValue().get());
break;
case LONG:
node.put(kv.getKey(), kv.getLongValue().get());
break;
case DOUBLE:
node.put(kv.getKey(), kv.getDoubleValue().get());
break;
case JSON:
try {
node.put(kv.getKey(), JSON.readTree(kv.getJsonValue().get()));
} catch (JsonProcessingException e) {
log.error("json parse exception ", e);
}
break;
}
}
public static List<KvEntry> getKvEntries(JsonNode data) {
List<KvEntry> attributes = new ArrayList<>();
for (Iterator<Map.Entry<String, JsonNode>> it = data.fields(); it.hasNext(); ) {
Map.Entry<String, JsonNode> field = it.next();
String key = field.getKey();
JsonNode value = field.getValue();
if (value.isBoolean()) {
attributes.add(new BooleanDataEntry(key, value.asBoolean()));
} else if (value.isDouble()) {
attributes.add(new DoubleDataEntry(key, value.asDouble()));
} else if (value.canConvertToLong()) {
attributes.add(new LongDataEntry(key, value.asLong()));
} else {
attributes.add(new StringDataEntry(key, value.asText()));
}
}
return attributes;
}
}
package com.priusis.client.util;
import lombok.Data;
/**
* Created by priusis on 16.01.17.
*/
@Data
public class KeystoreConfiguration {
private String type;
private String location;
private String fileContent;
private String password;
private String alias;
private String keyPassword;
}
package com.priusis.client.util.converter;
import lombok.Data;
@Data
public class AttributesMapping extends TransformerKVMapping {
}
package com.priusis.client.util.converter;
import lombok.Data;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by ashvayka on 16.01.17.
*/
@Data
public class DeviceMapping {
public static final Pattern TAG_PATTERN = Pattern.compile("\\$\\{(.*?)\\}");
private final String deviceNodePattern;
private final String deviceNamePattern;
private final List<AttributesMapping> attributes;
public Set<String> getDeviceNameTags() {
Set<String> tags = new HashSet<>();
addTags(tags, TAG_PATTERN, deviceNamePattern);
return tags;
}
public Set<String> getAllTags() {
Set<String> tags = new HashSet<>();
addTags(tags, TAG_PATTERN, deviceNamePattern);
attributes.forEach(mapping -> addTags(tags, TAG_PATTERN, mapping.getValue()));
return tags;
}
private void addTags(Set<String> tags, Pattern pattern, String expression) {
Matcher matcher = pattern.matcher(expression);
while (matcher.find()) {
String tag = matcher.group();
tags.add(tag.substring(2, tag.length() - 1));
}
}
}
package com.priusis.client.util.converter;
import com.priusis.client.extensions.common.conf.mapping.KVMapping;
import com.priusis.client.util.converter.transformer.DataValueTransformer;
import lombok.Data;
@Data
public class TransformerKVMapping extends KVMapping {
private DataValueTransformer transformer;
}
package com.priusis.client.util.converter.transformer;
public abstract class AbstractDataValueTransformer implements DataValueTransformer {
@Override
public Double transformToDouble(String strValue) {
throw new UnsupportedOperationException(String.format("%s doesn't support transforming to double value", this.getClass().getSimpleName()));
}
@Override
public Long transformToLong(String strValue) {
throw new UnsupportedOperationException(String.format("%s doesn't support transforming to long value", this.getClass().getSimpleName()));
}
@Override
public String transformToString(String strValue) {
throw new UnsupportedOperationException(String.format("%s doesn't support transforming to string value", this.getClass().getSimpleName()));
}
@Override
public Boolean transformToBoolean(String strValue) {
throw new UnsupportedOperationException(String.format("%s doesn't support transforming to boolean value", this.getClass().getSimpleName()));
}
@Override
public String transformToJson(String strValue) {
throw new UnsupportedOperationException(String.format("%s doesn't support transforming to json value", this.getClass().getSimpleName()));
}
@Override
public boolean isApplicable(String strValue) {
return true;
}
}
package com.priusis.client.util.converter.transformer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class BaseDataValueTransformer extends AbstractDataValueTransformer {
static final String INT_TO_DOUBLE_TRANSFORMER_NAME = "intToDouble";
private static final int MAX_DOUBLE_VALUE = 65536;
private static final int DIVIDE_POWER = 10;
private static final ObjectMapper mapper = new ObjectMapper();
@Override
public Double transformToDouble(String strValue) {
Double value = Double.valueOf(strValue);
if (value <= MAX_DOUBLE_VALUE) {
return value / DIVIDE_POWER;
} else {
return (MAX_DOUBLE_VALUE - value) / DIVIDE_POWER;
}
}
@Override
public Long transformToLong(String strValue) {
return Long.valueOf(strValue);
}
@Override
public String transformToString(String strValue) {
return strValue;
}
@Override
public String transformToJson(String strValue) {
//return toJsonStr(strValue);
return strValue;
}
@Override
public Boolean transformToBoolean(String strValue) {
return Boolean.valueOf(strValue);
}
private String toJsonStr(String value) throws JsonProcessingException {
return mapper.writeValueAsString(value);
}
}
package com.priusis.client.util.converter.transformer;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = BaseDataValueTransformer.class, name = BaseDataValueTransformer.INT_TO_DOUBLE_TRANSFORMER_NAME)
})
public interface DataValueTransformer {
Double transformToDouble(String strValue);
Long transformToLong(String strValue);
String transformToString(String strValue);
Boolean transformToBoolean(String strValue);
boolean isApplicable(String strValue);
String transformToJson(String strValue);
}
\ No newline at end of file
package com.priusis.config;
import com.priusis.client.service.DefaultTenantManagerService;
import com.priusis.client.service.PersistentFileService;
import com.priusis.client.service.PersistentFileServiceImpl;
import com.priusis.client.service.TenantManagerService;
import com.priusis.client.service.conf.PcCoreConfiguration;
import com.priusis.client.service.conf.PcPersistenceConfiguration;
import com.priusis.client.service.core.MqttService;
import com.priusis.client.service.core.MqttServiceImpl;
import io.netty.channel.nio.NioEventLoopGroup;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import java.util.function.Consumer;
@Configuration
public class PiClientConfiguration {
public static final int NIO_EVENT_LOOP_GROUP_THREADS = 100;
@Bean
public TenantManagerService getTenantManagerService() {
return new DefaultTenantManagerService() {
@Override
public MqttService getGatewayService(PcCoreConfiguration configuration, Consumer<String> extensionsConfigListener) {
return getGatewayServiceBean(configuration, extensionsConfigListener);
}
};
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public PersistentFileService getPersistentFileServiceBean(PcPersistenceConfiguration pcPersistenceConfiguration) {
PersistentFileServiceImpl persistentFileService = new PersistentFileServiceImpl();
persistentFileService.setPersistence(pcPersistenceConfiguration);
return persistentFileService;
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public MqttService getGatewayServiceBean(PcCoreConfiguration configuration, Consumer<String> extensionsConfigListener) {
MqttServiceImpl gatewayService = new MqttServiceImpl(configuration, extensionsConfigListener);
gatewayService.setPersistentFileService(getPersistentFileServiceBean(configuration.getPersistence()));
return gatewayService;
}
@Bean
public NioEventLoopGroup getNioEventLoopGroupBean() {
return new NioEventLoopGroup(NIO_EVENT_LOOP_GROUP_THREADS);
}
}
package com.priusis.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.Charset;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
// 支持中文编码
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(Charset.forName("UTF-8")));
return restTemplate;
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(5000);//单位为ms
factory.setConnectTimeout(5000);//单位为ms
return factory;
}
}
server:
# Server bind address
address: "0.0.0.0"
port: 8765
spring:
......@@ -7,3 +9,30 @@ spring:
profiles:
active: dev
# Check new version updates parameters
updates:
# Enable/disable updates checking.
enabled: "${UPDATES_ENABLED:false}"
core:
label: "Tenant"
reporting:
interval: 60000
persistence:
type: file
path: D://storage
bufferSize: 6
connection:
host: "${GATEWAY_HOST:127.0.0.1}"
port: 1884
retryInterval: 3000
maxInFlight: 1000
security:
accessToken: "${GATEWAY_ACCESS_TOKEN:1hTbcWoaQvPzl2PpbkTG}"
remoteConfiguration: false
extensions:
-
id: "http"
type: "HTTP"
extensionConfiguration: http-config.json
\ No newline at end of file
===================================================
:: ${application.title} :: ${application.formatted-version}
===================================================
{
"token": "oc-client"
}
\ No newline at end of file
[Unit]
Description=${pkg.name}
After=syslog.target
[Service]
User=${pkg.user}
ExecStart=${pkg.installFolder}/bin/${pkg.name}.jar
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
#!/bin/sh
chown -R ${pkg.user}: ${pkg.logFolder}
chown -R ${pkg.user}: ${pkg.installFolder}
update-rc.d ${pkg.name} defaults
#!/bin/sh
update-rc.d -f ${pkg.name} remove
#!/bin/sh
if ! getent group ${pkg.user} >/dev/null; then
addgroup --system ${pkg.user}
fi
if ! getent passwd ${pkg.user} >/dev/null; then
adduser --quiet \
--system \
--ingroup ${pkg.user} \
--quiet \
--disabled-login \
--disabled-password \
--home ${pkg.installFolder} \
--no-create-home \
-gecos "Priusisiot application" \
${pkg.user}
fi
#!/bin/sh
if [ -e /var/run/${pkg.name}/${pkg.name}.pid ]; then
service ${pkg.name} stop
fi
#!/bin/sh
chown -R ${pkg.user}: ${pkg.logFolder}
chown -R ${pkg.user}: ${pkg.installFolder}
if [ $1 -eq 1 ] ; then
# Initial installation
systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || :
fi
#!/bin/sh
if [ $1 -ge 1 ] ; then
# Package upgrade, not uninstall
systemctl try-restart ${pkg.name}.service >/dev/null 2>&1 || :
fi
#!/bin/sh
getent group ${pkg.user} >/dev/null || groupadd -r ${pkg.user}
getent passwd ${pkg.user} >/dev/null || \
useradd -d ${pkg.installFolder} -g ${pkg.user} -M -r ${pkg.user} -s /sbin/nologin \
-c "Priusisiot application"
#!/bin/sh
if [ $1 -eq 0 ] ; then
# Package removal, not upgrade
systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
fi
@ECHO OFF
setlocal ENABLEEXTENSIONS
@ECHO Detecting Java version installed.
:CHECK_JAVA_64
@ECHO Detecting if it is 64 bit machine
set KEY_NAME="HKEY_LOCAL_MACHINE\Software\Wow6432Node\JavaSoft\Java Runtime Environment"
set VALUE_NAME=CurrentVersion
FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
set ValueName=%%A
set ValueType=%%B
set ValueValue=%%C
)
@ECHO CurrentVersion %ValueValue%
SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
SET VALUE_NAME=JavaHome
if defined ValueName (
FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
set ValueName2=%%A
set ValueType2=%%B
set JRE_PATH2=%%C
if defined ValueName2 (
set ValueName = %ValueName2%
set ValueType = %ValueType2%
set ValueValue = %JRE_PATH2%
)
)
)
IF NOT "%JRE_PATH2%" == "" GOTO JAVA_INSTALLED
:CHECK_JAVA_32
@ECHO Detecting if it is 32 bit machine
set KEY_NAME="HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment"
set VALUE_NAME=CurrentVersion
FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
set ValueName=%%A
set ValueType=%%B
set ValueValue=%%C
)
@ECHO CurrentVersion %ValueValue%
SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
SET VALUE_NAME=JavaHome
if defined ValueName (
FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
set ValueName2=%%A
set ValueType2=%%B
set JRE_PATH2=%%C
if defined ValueName2 (
set ValueName = %ValueName2%
set ValueType = %ValueType2%
set ValueValue = %JRE_PATH2%
)
)
)
IF "%JRE_PATH2%" == "" GOTO JAVA_NOT_INSTALLED
:JAVA_INSTALLED
@ECHO Java 1.8 found!
@ECHO Installing ${pkg.name} ...
%~dp0${pkg.name}.exe install
@ECHO DONE.
GOTO END
:JAVA_NOT_INSTALLED
@ECHO Java 1.8 or above is not installed
@ECHO Please go to https://java.com/ and install Java. Then retry installation.
PAUSE
GOTO END
:END
<service>
<id>${pkg.name}</id>
<name>${project.name}</name>
<description>${project.description}</description>
<workingdirectory>%BASE%\conf</workingdirectory>
<logpath>${pkg.winWrapperLogFolder}</logpath>
<logmode>rotate</logmode>
<env name="LOADER_PATH" value="%BASE%\conf" />
<executable>java</executable>
<startargument>-Dplatform=windows</startargument>
<startargument>-jar</startargument>
<startargument>%BASE%\lib\${pkg.name}.jar</startargument>
</service>
@ECHO OFF
@ECHO Stopping ${pkg.name} ...
net stop ${pkg.name}
@ECHO Uninstalling ${pkg.name} ...
%~dp0${pkg.name}.exe uninstall
@ECHO DONE.
\ No newline at end of file
package com.priusis;
import cn.hutool.core.io.unit.DataSizeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.system.oshi.OshiUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import oshi.hardware.NetworkIF;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RestTest {
@Autowired
private RestTemplate restTemplate;
@Test
public void test1() throws Exception {
/*Map<String, Object> data = new HashMap<>();
// {
// "device": "101",
// "lat": "210.2",
// "lng": "11.21"
//}
data.put("device", "101");
data.put("lat", 210.2);
data.put("lng", 11.21);*/
InetAddress inetAddress = InetAddress.getLocalHost();
NetworkIF networkIF = new NetworkIF();
networkIF.setNetworkInterface(NetworkInterface.getByInetAddress(inetAddress));
Map<String, Object> data = MapUtil.<String, Object>builder()
.put("operatingSystem", OshiUtil.getOs().toString())
.put("mac", networkIF.getMacaddr())
.put("baseboard", OshiUtil.getSystem().getBaseboard().getManufacturer() + " " + OshiUtil.getSystem().getBaseboard().getVersion())
.put("ip", inetAddress.getHostAddress())
.put("cpuModel", OshiUtil.getCpuInfo(0).getCpuModel())
.put("cpuTemp", OshiUtil.getSensors().getCpuTemperature())
.put("memoryCap", DataSizeUtil.format(OshiUtil.getMemory().getTotal()))
.put("diskModel", OshiUtil.getHardware().getDiskStores()[0].getModel())
.put("diskCap", DataSizeUtil.format(OshiUtil.getHardware().getDiskStores()[0].getSize()))
.build();
Map map = restTemplate.postForObject("http://localhost:9090/uplink/oc-client", data, Map.class);
System.out.println(map);
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论