Commit 5242864ccccdee77f95d08a80a6340b5f767eb9f

Authored by Andrew Shvayka
1 parent d7c4ff6b

CoAP transport implementation

Showing 46 changed files with 1697 additions and 1045 deletions
@@ -60,10 +60,6 @@ @@ -60,10 +60,6 @@
60 <groupId>org.thingsboard.common.transport</groupId> 60 <groupId>org.thingsboard.common.transport</groupId>
61 <artifactId>transport-api</artifactId> 61 <artifactId>transport-api</artifactId>
62 </dependency> 62 </dependency>
63 - <!--<dependency>-->  
64 - <!--<groupId>org.thingsboard.transport</groupId>-->  
65 - <!--<artifactId>coap</artifactId>-->  
66 - <!--</dependency>-->  
67 <dependency> 63 <dependency>
68 <groupId>org.thingsboard.common.transport</groupId> 64 <groupId>org.thingsboard.common.transport</groupId>
69 <artifactId>mqtt</artifactId> 65 <artifactId>mqtt</artifactId>
@@ -73,6 +69,10 @@ @@ -73,6 +69,10 @@
73 <artifactId>http</artifactId> 69 <artifactId>http</artifactId>
74 </dependency> 70 </dependency>
75 <dependency> 71 <dependency>
  72 + <groupId>org.thingsboard.common.transport</groupId>
  73 + <artifactId>coap</artifactId>
  74 + </dependency>
  75 + <dependency>
76 <groupId>org.thingsboard</groupId> 76 <groupId>org.thingsboard</groupId>
77 <artifactId>dao</artifactId> 77 <artifactId>dao</artifactId>
78 </dependency> 78 </dependency>
@@ -455,7 +455,6 @@ transport: @@ -455,7 +455,6 @@ transport:
455 enabled: "${MQTT_ENABLED:true}" 455 enabled: "${MQTT_ENABLED:true}"
456 bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}" 456 bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
457 bind_port: "${MQTT_BIND_PORT:1883}" 457 bind_port: "${MQTT_BIND_PORT:1883}"
458 - adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}"  
459 timeout: "${MQTT_TIMEOUT:10000}" 458 timeout: "${MQTT_TIMEOUT:10000}"
460 netty: 459 netty:
461 leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}" 460 leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}"
@@ -482,5 +481,4 @@ transport: @@ -482,5 +481,4 @@ transport:
482 enabled: "${COAP_ENABLED:true}" 481 enabled: "${COAP_ENABLED:true}"
483 bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}" 482 bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
484 bind_port: "${COAP_BIND_PORT:5683}" 483 bind_port: "${COAP_BIND_PORT:5683}"
485 - adaptor: "${COAP_ADAPTOR_NAME:JsonCoapAdaptor}"  
486 timeout: "${COAP_TIMEOUT:10000}" 484 timeout: "${COAP_TIMEOUT:10000}"
  1 +<!--
  2 +
  3 + Copyright © 2016-2018 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  20 + <modelVersion>4.0.0</modelVersion>
  21 + <parent>
  22 + <groupId>org.thingsboard.common</groupId>
  23 + <version>2.2.0-SNAPSHOT</version>
  24 + <artifactId>transport</artifactId>
  25 + </parent>
  26 + <groupId>org.thingsboard.common.transport</groupId>
  27 + <artifactId>coap</artifactId>
  28 + <packaging>jar</packaging>
  29 +
  30 + <name>Thingsboard CoAP Transport Common</name>
  31 + <url>https://thingsboard.io</url>
  32 +
  33 + <properties>
  34 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  35 + <main.dir>${basedir}/../../..</main.dir>
  36 + </properties>
  37 +
  38 + <dependencies>
  39 + <dependency>
  40 + <groupId>org.thingsboard.common.transport</groupId>
  41 + <artifactId>transport-api</artifactId>
  42 + </dependency>
  43 + <dependency>
  44 + <groupId>org.eclipse.californium</groupId>
  45 + <artifactId>californium-core</artifactId>
  46 + </dependency>
  47 + <dependency>
  48 + <groupId>org.springframework</groupId>
  49 + <artifactId>spring-context-support</artifactId>
  50 + </dependency>
  51 + <dependency>
  52 + <groupId>org.springframework</groupId>
  53 + <artifactId>spring-context</artifactId>
  54 + </dependency>
  55 + <dependency>
  56 + <groupId>org.slf4j</groupId>
  57 + <artifactId>slf4j-api</artifactId>
  58 + </dependency>
  59 + <dependency>
  60 + <groupId>org.slf4j</groupId>
  61 + <artifactId>log4j-over-slf4j</artifactId>
  62 + </dependency>
  63 + <dependency>
  64 + <groupId>ch.qos.logback</groupId>
  65 + <artifactId>logback-core</artifactId>
  66 + </dependency>
  67 + <dependency>
  68 + <groupId>ch.qos.logback</groupId>
  69 + <artifactId>logback-classic</artifactId>
  70 + </dependency>
  71 + <dependency>
  72 + <groupId>org.springframework.boot</groupId>
  73 + <artifactId>spring-boot-starter-test</artifactId>
  74 + <scope>test</scope>
  75 + </dependency>
  76 + <dependency>
  77 + <groupId>junit</groupId>
  78 + <artifactId>junit</artifactId>
  79 + <scope>test</scope>
  80 + </dependency>
  81 + <dependency>
  82 + <groupId>org.mockito</groupId>
  83 + <artifactId>mockito-all</artifactId>
  84 + <scope>test</scope>
  85 + </dependency>
  86 + </dependencies>
  87 +
  88 +</project>
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.beans.factory.annotation.Autowired;
  21 +import org.springframework.beans.factory.annotation.Value;
  22 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  23 +import org.springframework.stereotype.Component;
  24 +import org.thingsboard.server.common.transport.TransportContext;
  25 +import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;
  26 +
  27 +/**
  28 + * Created by ashvayka on 18.10.18.
  29 + */
  30 +@Slf4j
  31 +@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.coap.enabled}'=='true')")
  32 +@Component
  33 +public class CoapTransportContext extends TransportContext {
  34 +
  35 + @Getter
  36 + @Value("${transport.coap.bind_address}")
  37 + private String host;
  38 +
  39 + @Getter
  40 + @Value("${transport.coap.bind_port}")
  41 + private Integer port;
  42 +
  43 + @Getter
  44 + @Value("${transport.coap.timeout}")
  45 + private Long timeout;
  46 +
  47 + @Getter
  48 + @Autowired
  49 + private CoapTransportAdaptor adaptor;
  50 +
  51 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.californium.core.CoapResource;
  20 +import org.eclipse.californium.core.coap.CoAP.ResponseCode;
  21 +import org.eclipse.californium.core.coap.Request;
  22 +import org.eclipse.californium.core.coap.Response;
  23 +import org.eclipse.californium.core.network.Exchange;
  24 +import org.eclipse.californium.core.network.ExchangeObserver;
  25 +import org.eclipse.californium.core.server.resources.CoapExchange;
  26 +import org.eclipse.californium.core.server.resources.Resource;
  27 +import org.springframework.util.ReflectionUtils;
  28 +import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
  29 +import org.thingsboard.server.common.msg.session.FeatureType;
  30 +import org.thingsboard.server.common.msg.session.SessionMsgType;
  31 +import org.thingsboard.server.common.transport.SessionMsgListener;
  32 +import org.thingsboard.server.common.transport.TransportContext;
  33 +import org.thingsboard.server.common.transport.TransportService;
  34 +import org.thingsboard.server.common.transport.TransportServiceCallback;
  35 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  36 +import org.thingsboard.server.gen.transport.TransportProtos;
  37 +
  38 +import java.lang.reflect.Field;
  39 +import java.util.List;
  40 +import java.util.Optional;
  41 +import java.util.UUID;
  42 +import java.util.concurrent.ConcurrentHashMap;
  43 +import java.util.concurrent.ConcurrentMap;
  44 +import java.util.concurrent.atomic.AtomicInteger;
  45 +import java.util.function.Consumer;
  46 +
  47 +@Slf4j
  48 +public class CoapTransportResource extends CoapResource {
  49 + // coap://localhost:port/api/v1/DEVICE_TOKEN/[attributes|telemetry|rpc[/requestId]]
  50 + private static final int ACCESS_TOKEN_POSITION = 3;
  51 + private static final int FEATURE_TYPE_POSITION = 4;
  52 + private static final int REQUEST_ID_POSITION = 5;
  53 +
  54 + private final CoapTransportContext transportContext;
  55 + private final TransportService transportService;
  56 + private final Field observerField;
  57 + private final long timeout;
  58 + private final ConcurrentMap<String, TransportProtos.SessionInfoProto> tokenToSessionIdMap = new ConcurrentHashMap<>();
  59 +
  60 + public CoapTransportResource(CoapTransportContext context, String name) {
  61 + super(name);
  62 + this.transportContext = context;
  63 + this.transportService = context.getTransportService();
  64 + this.timeout = context.getTimeout();
  65 + // This is important to turn off existing observable logic in
  66 + // CoapResource. We will have our own observe monitoring due to 1:1
  67 + // observe relationship.
  68 + this.setObservable(false);
  69 + observerField = ReflectionUtils.findField(Exchange.class, "observer");
  70 + observerField.setAccessible(true);
  71 + }
  72 +
  73 + @Override
  74 + public void handleGET(CoapExchange exchange) {
  75 + if (transportContext.getQuotaService().isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) {
  76 + log.warn("CoAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort());
  77 + exchange.respond(ResponseCode.BAD_REQUEST);
  78 + return;
  79 + }
  80 +
  81 + Optional<FeatureType> featureType = getFeatureType(exchange.advanced().getRequest());
  82 + if (!featureType.isPresent()) {
  83 + log.trace("Missing feature type parameter");
  84 + exchange.respond(ResponseCode.BAD_REQUEST);
  85 + } else if (featureType.get() == FeatureType.TELEMETRY) {
  86 + log.trace("Can't fetch/subscribe to timeseries updates");
  87 + exchange.respond(ResponseCode.BAD_REQUEST);
  88 + } else if (exchange.getRequestOptions().hasObserve()) {
  89 + processExchangeGetRequest(exchange, featureType.get());
  90 + } else if (featureType.get() == FeatureType.ATTRIBUTES) {
  91 + processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);
  92 + } else {
  93 + log.trace("Invalid feature type parameter");
  94 + exchange.respond(ResponseCode.BAD_REQUEST);
  95 + }
  96 + }
  97 +
  98 + private void processExchangeGetRequest(CoapExchange exchange, FeatureType featureType) {
  99 + boolean unsubscribe = exchange.getRequestOptions().getObserve() == 1;
  100 + SessionMsgType sessionMsgType;
  101 + if (featureType == FeatureType.RPC) {
  102 + sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST : SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST;
  103 + } else {
  104 + sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST : SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST;
  105 + }
  106 + processRequest(exchange, sessionMsgType);
  107 + }
  108 +
  109 + @Override
  110 + public void handlePOST(CoapExchange exchange) {
  111 + if (transportContext.getQuotaService().isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) {
  112 + log.warn("CoAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort());
  113 + exchange.respond(ResponseCode.BAD_REQUEST);
  114 + return;
  115 + }
  116 +
  117 + Optional<FeatureType> featureType = getFeatureType(exchange.advanced().getRequest());
  118 + if (!featureType.isPresent()) {
  119 + log.trace("Missing feature type parameter");
  120 + exchange.respond(ResponseCode.BAD_REQUEST);
  121 + } else {
  122 + switch (featureType.get()) {
  123 + case ATTRIBUTES:
  124 + processRequest(exchange, SessionMsgType.POST_ATTRIBUTES_REQUEST);
  125 + break;
  126 + case TELEMETRY:
  127 + processRequest(exchange, SessionMsgType.POST_TELEMETRY_REQUEST);
  128 + break;
  129 + case RPC:
  130 + Optional<Integer> requestId = getRequestId(exchange.advanced().getRequest());
  131 + if (requestId.isPresent()) {
  132 + processRequest(exchange, SessionMsgType.TO_DEVICE_RPC_RESPONSE);
  133 + } else {
  134 + processRequest(exchange, SessionMsgType.TO_SERVER_RPC_REQUEST);
  135 + }
  136 + break;
  137 + }
  138 + }
  139 + }
  140 +
  141 + private void processRequest(CoapExchange exchange, SessionMsgType type) {
  142 + log.trace("Processing {}", exchange.advanced().getRequest());
  143 + exchange.accept();
  144 + Exchange advanced = exchange.advanced();
  145 + Request request = advanced.getRequest();
  146 +
  147 + Optional<DeviceTokenCredentials> credentials = decodeCredentials(request);
  148 + if (!credentials.isPresent()) {
  149 + exchange.respond(ResponseCode.BAD_REQUEST);
  150 + return;
  151 + }
  152 +
  153 + transportService.process(TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(),
  154 + new DeviceAuthCallback(transportContext, exchange, sessionInfo -> {
  155 + UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
  156 + try {
  157 + switch (type) {
  158 + case POST_ATTRIBUTES_REQUEST:
  159 + transportService.process(sessionInfo,
  160 + transportContext.getAdaptor().convertToPostAttributes(sessionId, request),
  161 + new CoapOkCallback(exchange));
  162 + break;
  163 + case POST_TELEMETRY_REQUEST:
  164 + transportService.process(sessionInfo,
  165 + transportContext.getAdaptor().convertToPostTelemetry(sessionId, request),
  166 + new CoapOkCallback(exchange));
  167 + break;
  168 + case SUBSCRIBE_ATTRIBUTES_REQUEST:
  169 + advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced),
  170 + registerAsyncCoapSession(exchange, request, sessionInfo, sessionId)));
  171 + transportService.process(sessionInfo,
  172 + TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(),
  173 + new CoapNoOpCallback(exchange));
  174 + break;
  175 + case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
  176 + TransportProtos.SessionInfoProto attrSession = lookupAsyncSessionInfo(request);
  177 + if (attrSession != null) {
  178 + transportService.process(attrSession,
  179 + TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(),
  180 + new CoapOkCallback(exchange));
  181 + closeAndDeregister(sessionInfo);
  182 + }
  183 + break;
  184 + case SUBSCRIBE_RPC_COMMANDS_REQUEST:
  185 + advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced),
  186 + registerAsyncCoapSession(exchange, request, sessionInfo, sessionId)));
  187 + transportService.process(sessionInfo,
  188 + TransportProtos.SubscribeToRPCMsg.getDefaultInstance(),
  189 + new CoapNoOpCallback(exchange));
  190 + break;
  191 + case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
  192 + TransportProtos.SessionInfoProto rpcSession = lookupAsyncSessionInfo(request);
  193 + if (rpcSession != null) {
  194 + transportService.process(rpcSession,
  195 + TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(),
  196 + new CoapOkCallback(exchange));
  197 + transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);
  198 + transportService.deregisterSession(rpcSession);
  199 + }
  200 + break;
  201 + case TO_DEVICE_RPC_RESPONSE:
  202 + transportService.process(sessionInfo,
  203 + transportContext.getAdaptor().convertToDeviceRpcResponse(sessionId, request),
  204 + new CoapOkCallback(exchange));
  205 + break;
  206 + case TO_SERVER_RPC_REQUEST:
  207 + transportService.process(sessionInfo,
  208 + transportContext.getAdaptor().convertToServerRpcRequest(sessionId, request),
  209 + new CoapNoOpCallback(exchange));
  210 + break;
  211 + case GET_ATTRIBUTES_REQUEST:
  212 + transportService.registerSyncSession(sessionInfo, new CoapSessionListener(sessionId, exchange), transportContext.getTimeout());
  213 + transportService.process(sessionInfo,
  214 + transportContext.getAdaptor().convertToGetAttributes(sessionId, request),
  215 + new CoapNoOpCallback(exchange));
  216 + break;
  217 + }
  218 + } catch (AdaptorException e) {
  219 + log.trace("[{}] Failed to decode message: ", sessionId, e);
  220 + exchange.respond(ResponseCode.BAD_REQUEST);
  221 + } catch (IllegalAccessException e) {
  222 + log.trace("[{}] Failed to process message: ", sessionId, e);
  223 + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
  224 + }
  225 + }));
  226 + }
  227 +
  228 + private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(Request request) {
  229 + String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString();
  230 + return tokenToSessionIdMap.remove(token);
  231 + }
  232 +
  233 + private String registerAsyncCoapSession(CoapExchange exchange, Request request, TransportProtos.SessionInfoProto sessionInfo, UUID sessionId) {
  234 + String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString();
  235 + tokenToSessionIdMap.putIfAbsent(token, sessionInfo);
  236 + CoapSessionListener attrListener = new CoapSessionListener(sessionId, exchange);
  237 + transportService.registerAsyncSession(sessionInfo, attrListener);
  238 + transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null);
  239 + return token;
  240 + }
  241 +
  242 + private static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) {
  243 + return TransportProtos.SessionEventMsg.newBuilder()
  244 + .setSessionType(TransportProtos.SessionType.ASYNC)
  245 + .setEvent(event).build();
  246 + }
  247 +
  248 + private Optional<DeviceTokenCredentials> decodeCredentials(Request request) {
  249 + List<String> uriPath = request.getOptions().getUriPath();
  250 + if (uriPath.size() >= ACCESS_TOKEN_POSITION) {
  251 + return Optional.of(new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1)));
  252 + } else {
  253 + return Optional.empty();
  254 + }
  255 + }
  256 +
  257 + private Optional<FeatureType> getFeatureType(Request request) {
  258 + List<String> uriPath = request.getOptions().getUriPath();
  259 + try {
  260 + if (uriPath.size() >= FEATURE_TYPE_POSITION) {
  261 + return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION - 1).toUpperCase()));
  262 + }
  263 + } catch (RuntimeException e) {
  264 + log.warn("Failed to decode feature type: {}", uriPath);
  265 + }
  266 + return Optional.empty();
  267 + }
  268 +
  269 + public static Optional<Integer> getRequestId(Request request) {
  270 + List<String> uriPath = request.getOptions().getUriPath();
  271 + try {
  272 + if (uriPath.size() >= REQUEST_ID_POSITION) {
  273 + return Optional.of(Integer.valueOf(uriPath.get(REQUEST_ID_POSITION - 1)));
  274 + }
  275 + } catch (RuntimeException e) {
  276 + log.warn("Failed to decode feature type: {}", uriPath);
  277 + }
  278 + return Optional.empty();
  279 + }
  280 +
  281 + @Override
  282 + public Resource getChild(String name) {
  283 + return this;
  284 + }
  285 +
  286 + private static class DeviceAuthCallback implements TransportServiceCallback<TransportProtos.ValidateDeviceCredentialsResponseMsg> {
  287 + private final TransportContext transportContext;
  288 + private final CoapExchange exchange;
  289 + private final Consumer<TransportProtos.SessionInfoProto> onSuccess;
  290 +
  291 + DeviceAuthCallback(TransportContext transportContext, CoapExchange exchange, Consumer<TransportProtos.SessionInfoProto> onSuccess) {
  292 + this.transportContext = transportContext;
  293 + this.exchange = exchange;
  294 + this.onSuccess = onSuccess;
  295 + }
  296 +
  297 + @Override
  298 + public void onSuccess(TransportProtos.ValidateDeviceCredentialsResponseMsg msg) {
  299 + if (msg.hasDeviceInfo()) {
  300 + UUID sessionId = UUID.randomUUID();
  301 + TransportProtos.DeviceInfoProto deviceInfoProto = msg.getDeviceInfo();
  302 + TransportProtos.SessionInfoProto sessionInfo = TransportProtos.SessionInfoProto.newBuilder()
  303 + .setNodeId(transportContext.getNodeId())
  304 + .setTenantIdMSB(deviceInfoProto.getTenantIdMSB())
  305 + .setTenantIdLSB(deviceInfoProto.getTenantIdLSB())
  306 + .setDeviceIdMSB(deviceInfoProto.getDeviceIdMSB())
  307 + .setDeviceIdLSB(deviceInfoProto.getDeviceIdLSB())
  308 + .setSessionIdMSB(sessionId.getMostSignificantBits())
  309 + .setSessionIdLSB(sessionId.getLeastSignificantBits())
  310 + .build();
  311 + onSuccess.accept(sessionInfo);
  312 + } else {
  313 + exchange.respond(ResponseCode.UNAUTHORIZED);
  314 + }
  315 + }
  316 +
  317 + @Override
  318 + public void onError(Throwable e) {
  319 + log.warn("Failed to process request", e);
  320 + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
  321 + }
  322 + }
  323 +
  324 + private static class CoapOkCallback implements TransportServiceCallback<Void> {
  325 + private final CoapExchange exchange;
  326 +
  327 + CoapOkCallback(CoapExchange exchange) {
  328 + this.exchange = exchange;
  329 + }
  330 +
  331 + @Override
  332 + public void onSuccess(Void msg) {
  333 + exchange.respond(ResponseCode.VALID);
  334 + }
  335 +
  336 + @Override
  337 + public void onError(Throwable e) {
  338 + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
  339 + }
  340 + }
  341 +
  342 + private static class CoapNoOpCallback implements TransportServiceCallback<Void> {
  343 + private final CoapExchange exchange;
  344 +
  345 + CoapNoOpCallback(CoapExchange exchange) {
  346 + this.exchange = exchange;
  347 + }
  348 +
  349 + @Override
  350 + public void onSuccess(Void msg) {
  351 +
  352 + }
  353 +
  354 + @Override
  355 + public void onError(Throwable e) {
  356 + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
  357 + }
  358 + }
  359 +
  360 + public class CoapSessionListener implements SessionMsgListener {
  361 +
  362 + private final CoapExchange exchange;
  363 + private final AtomicInteger seqNumber = new AtomicInteger(2);
  364 +
  365 + CoapSessionListener(UUID sessionId, CoapExchange exchange) {
  366 + this.exchange = exchange;
  367 + }
  368 +
  369 + @Override
  370 + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg msg) {
  371 + try {
  372 + exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
  373 + } catch (AdaptorException e) {
  374 + log.trace("Failed to reply due to error", e);
  375 + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
  376 + }
  377 + }
  378 +
  379 + @Override
  380 + public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg) {
  381 + try {
  382 + exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
  383 + } catch (AdaptorException e) {
  384 + log.trace("Failed to reply due to error", e);
  385 + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
  386 + }
  387 + }
  388 +
  389 + @Override
  390 + public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {
  391 + exchange.respond(ResponseCode.SERVICE_UNAVAILABLE);
  392 + }
  393 +
  394 + @Override
  395 + public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg msg) {
  396 + try {
  397 + exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
  398 + } catch (AdaptorException e) {
  399 + log.trace("Failed to reply due to error", e);
  400 + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
  401 + }
  402 + }
  403 +
  404 + @Override
  405 + public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg msg) {
  406 + try {
  407 + exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
  408 + } catch (AdaptorException e) {
  409 + log.trace("Failed to reply due to error", e);
  410 + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
  411 + }
  412 + }
  413 +
  414 + public int getNextSeqNumber() {
  415 + return seqNumber.getAndIncrement();
  416 + }
  417 + }
  418 +
  419 + public class CoapExchangeObserverProxy implements ExchangeObserver {
  420 +
  421 + private final ExchangeObserver proxy;
  422 + private final String token;
  423 +
  424 + CoapExchangeObserverProxy(ExchangeObserver proxy, String token) {
  425 + super();
  426 + this.proxy = proxy;
  427 + this.token = token;
  428 + }
  429 +
  430 + @Override
  431 + public void completed(Exchange exchange) {
  432 + proxy.completed(exchange);
  433 + TransportProtos.SessionInfoProto session = tokenToSessionIdMap.remove(token);
  434 + if (session != null) {
  435 + closeAndDeregister(session);
  436 + }
  437 + }
  438 + }
  439 +
  440 + private void closeAndDeregister(TransportProtos.SessionInfoProto session) {
  441 + transportService.process(session, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);
  442 + transportService.deregisterSession(session);
  443 + }
  444 +
  445 +}
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java renamed from transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
@@ -21,6 +21,7 @@ import org.eclipse.californium.core.CoapServer; @@ -21,6 +21,7 @@ import org.eclipse.californium.core.CoapServer;
21 import org.eclipse.californium.core.network.CoapEndpoint; 21 import org.eclipse.californium.core.network.CoapEndpoint;
22 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
23 import org.springframework.beans.factory.annotation.Value; 23 import org.springframework.beans.factory.annotation.Value;
  24 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
24 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 25 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
25 import org.springframework.context.ApplicationContext; 26 import org.springframework.context.ApplicationContext;
26 import org.springframework.stereotype.Service; 27 import org.springframework.stereotype.Service;
@@ -37,49 +38,26 @@ import java.net.InetSocketAddress; @@ -37,49 +38,26 @@ import java.net.InetSocketAddress;
37 import java.net.UnknownHostException; 38 import java.net.UnknownHostException;
38 39
39 @Service("CoapTransportService") 40 @Service("CoapTransportService")
40 -@ConditionalOnProperty(prefix = "transport.coap", value = "enabled", havingValue = "true") 41 +@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.coap.enabled}'=='true')")
41 @Slf4j 42 @Slf4j
42 public class CoapTransportService { 43 public class CoapTransportService {
43 44
44 private static final String V1 = "v1"; 45 private static final String V1 = "v1";
45 private static final String API = "api"; 46 private static final String API = "api";
46 47
47 - private CoapServer server;  
48 -  
49 - @Autowired(required = false)  
50 - private ApplicationContext appContext;  
51 -  
52 - @Autowired(required = false)  
53 - private SessionMsgProcessor processor;  
54 -  
55 - @Autowired(required = false)  
56 - private DeviceAuthService authService; 48 + @Autowired
  49 + private CoapTransportContext coapTransportContext;
57 50
58 - @Autowired(required = false)  
59 - private HostRequestsQuotaService quotaService;  
60 -  
61 -  
62 - @Value("${coap.bind_address}")  
63 - private String host;  
64 - @Value("${coap.bind_port}")  
65 - private Integer port;  
66 - @Value("${coap.adaptor}")  
67 - private String adaptorName;  
68 - @Value("${coap.timeout}")  
69 - private Long timeout;  
70 -  
71 - private CoapTransportAdaptor adaptor; 51 + private CoapServer server;
72 52
73 @PostConstruct 53 @PostConstruct
74 public void init() throws UnknownHostException { 54 public void init() throws UnknownHostException {
75 log.info("Starting CoAP transport..."); 55 log.info("Starting CoAP transport...");
76 - log.info("Lookup CoAP transport adaptor {}", adaptorName);  
77 - this.adaptor = (CoapTransportAdaptor) appContext.getBean(adaptorName);  
78 log.info("Starting CoAP transport server"); 56 log.info("Starting CoAP transport server");
79 this.server = new CoapServer(); 57 this.server = new CoapServer();
80 createResources(); 58 createResources();
81 - InetAddress addr = InetAddress.getByName(host);  
82 - InetSocketAddress sockAddr = new InetSocketAddress(addr, port); 59 + InetAddress addr = InetAddress.getByName(coapTransportContext.getHost());
  60 + InetSocketAddress sockAddr = new InetSocketAddress(addr, coapTransportContext.getPort());
83 server.addEndpoint(new CoapEndpoint(sockAddr)); 61 server.addEndpoint(new CoapEndpoint(sockAddr));
84 server.start(); 62 server.start();
85 log.info("CoAP transport started!"); 63 log.info("CoAP transport started!");
@@ -87,7 +65,7 @@ public class CoapTransportService { @@ -87,7 +65,7 @@ public class CoapTransportService {
87 65
88 private void createResources() { 66 private void createResources() {
89 CoapResource api = new CoapResource(API); 67 CoapResource api = new CoapResource(API);
90 - api.add(new CoapTransportResource(processor, authService, adaptor, V1, timeout, quotaService)); 68 + api.add(new CoapTransportResource(coapTransportContext, V1));
91 server.add(api); 69 server.add(api);
92 } 70 }
93 71
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java renamed from transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java
@@ -17,9 +17,31 @@ package org.thingsboard.server.transport.coap.adaptors; @@ -17,9 +17,31 @@ package org.thingsboard.server.transport.coap.adaptors;
17 17
18 import org.eclipse.californium.core.coap.Request; 18 import org.eclipse.californium.core.coap.Request;
19 import org.eclipse.californium.core.coap.Response; 19 import org.eclipse.californium.core.coap.Response;
20 -import org.thingsboard.server.common.transport.TransportAdaptor;  
21 -import org.thingsboard.server.transport.coap.session.CoapSessionCtx; 20 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  21 +import org.thingsboard.server.gen.transport.TransportProtos;
  22 +import org.thingsboard.server.transport.coap.CoapTransportResource;
22 23
23 -public interface CoapTransportAdaptor extends TransportAdaptor<CoapSessionCtx, Request, Response> { 24 +import java.util.UUID;
  25 +import java.util.Optional;
  26 +
  27 +public interface CoapTransportAdaptor {
  28 +
  29 + TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound) throws AdaptorException;
  30 +
  31 + TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound) throws AdaptorException;
  32 +
  33 + TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException;
  34 +
  35 + TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException;
  36 +
  37 + TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException;
  38 +
  39 + Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException;
  40 +
  41 + Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException;
  42 +
  43 + Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException;
  44 +
  45 + Response convertToPublish(CoapTransportResource.CoapSessionListener coapSessionListener, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException;
24 46
25 } 47 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap.adaptors;
  17 +
  18 +import java.util.*;
  19 +
  20 +import com.google.gson.JsonElement;
  21 +import com.google.gson.JsonObject;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.eclipse.californium.core.coap.CoAP;
  24 +import org.eclipse.californium.core.coap.Request;
  25 +import org.eclipse.californium.core.coap.Response;
  26 +import org.springframework.util.StringUtils;
  27 +import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
  28 +import org.thingsboard.server.common.msg.session.SessionContext;
  29 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  30 +import org.thingsboard.server.common.transport.adaptor.JsonConverter;
  31 +import org.springframework.stereotype.Component;
  32 +
  33 +import com.google.gson.JsonParser;
  34 +import com.google.gson.JsonSyntaxException;
  35 +import org.thingsboard.server.gen.transport.TransportProtos;
  36 +import org.thingsboard.server.transport.coap.CoapTransportResource;
  37 +
  38 +@Component("JsonCoapAdaptor")
  39 +@Slf4j
  40 +public class JsonCoapAdaptor implements CoapTransportAdaptor {
  41 +
  42 + @Override
  43 + public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound) throws AdaptorException {
  44 + String payload = validatePayload(sessionId, inbound);
  45 + try {
  46 + return JsonConverter.convertToTelemetryProto(new JsonParser().parse(payload));
  47 + } catch (IllegalStateException | JsonSyntaxException ex) {
  48 + throw new AdaptorException(ex);
  49 + }
  50 + }
  51 +
  52 + @Override
  53 + public TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound) throws AdaptorException {
  54 + String payload = validatePayload(sessionId, inbound);
  55 + try {
  56 + return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload));
  57 + } catch (IllegalStateException | JsonSyntaxException ex) {
  58 + throw new AdaptorException(ex);
  59 + }
  60 + }
  61 +
  62 + @Override
  63 + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException {
  64 + List<String> queryElements = inbound.getOptions().getUriQuery();
  65 + TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
  66 + if (queryElements != null && queryElements.size() > 0) {
  67 + Set<String> clientKeys = toKeys(queryElements, "clientKeys");
  68 + Set<String> sharedKeys = toKeys(queryElements, "sharedKeys");
  69 + if (clientKeys != null) {
  70 + result.addAllClientAttributeNames(clientKeys);
  71 + }
  72 + if (sharedKeys != null) {
  73 + result.addAllSharedAttributeNames(sharedKeys);
  74 + }
  75 + }
  76 + return result.build();
  77 + }
  78 +
  79 + @Override
  80 + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException {
  81 + Optional<Integer> requestId = CoapTransportResource.getRequestId(inbound);
  82 + String payload = validatePayload(sessionId, inbound);
  83 + JsonObject response = new JsonParser().parse(payload).getAsJsonObject();
  84 + return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId.orElseThrow(() -> new AdaptorException("Request id is missing!")))
  85 + .setPayload(response.toString()).build();
  86 + }
  87 +
  88 + @Override
  89 + public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException {
  90 + String payload = validatePayload(sessionId, inbound);
  91 + return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), 0);
  92 + }
  93 +
  94 + @Override
  95 + public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg msg) throws AdaptorException {
  96 + return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg));
  97 + }
  98 +
  99 + @Override
  100 + public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.ToDeviceRpcRequestMsg msg) throws AdaptorException {
  101 + return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg, true));
  102 + }
  103 +
  104 + @Override
  105 + public Response convertToPublish(CoapTransportResource.CoapSessionListener coapSessionListener, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException {
  106 + Response response = new Response(CoAP.ResponseCode.CONTENT);
  107 + JsonElement result = JsonConverter.toJson(msg);
  108 + response.setPayload(result.toString());
  109 + return response;
  110 + }
  111 +
  112 + @Override
  113 + public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException {
  114 + if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0 && msg.getDeletedAttributeKeysCount() == 0) {
  115 + return new Response(CoAP.ResponseCode.NOT_FOUND);
  116 + } else {
  117 + Response response = new Response(CoAP.ResponseCode.CONTENT);
  118 + JsonObject result = JsonConverter.toJson(msg);
  119 + response.setPayload(result.toString());
  120 + return response;
  121 + }
  122 + }
  123 +
  124 + private Response getObserveNotification(int seqNumber, JsonElement json) {
  125 + Response response = new Response(CoAP.ResponseCode.CONTENT);
  126 + response.getOptions().setObserve(seqNumber);
  127 + response.setPayload(json.toString());
  128 + return response;
  129 + }
  130 +
  131 + private String validatePayload(UUID sessionId, Request inbound) throws AdaptorException {
  132 + String payload = inbound.getPayloadString();
  133 + if (payload == null) {
  134 + log.warn("[{}] Payload is empty!", sessionId);
  135 + throw new AdaptorException(new IllegalArgumentException("Payload is empty!"));
  136 + }
  137 + return payload;
  138 + }
  139 +
  140 + private Set<String> toKeys(List<String> queryElements, String attributeName) throws AdaptorException {
  141 + String keys = null;
  142 + for (String queryElement : queryElements) {
  143 + String[] queryItem = queryElement.split("=");
  144 + if (queryItem.length == 2 && queryItem[0].equals(attributeName)) {
  145 + keys = queryItem[1];
  146 + }
  147 + }
  148 + if (keys != null && !StringUtils.isEmpty(keys)) {
  149 + return new HashSet<>(Arrays.asList(keys.split(",")));
  150 + } else {
  151 + return null;
  152 + }
  153 + }
  154 +
  155 +}
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java renamed from transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java
@@ -43,8 +43,6 @@ public class MqttTransportService { @@ -43,8 +43,6 @@ public class MqttTransportService {
43 private String host; 43 private String host;
44 @Value("${transport.mqtt.bind_port}") 44 @Value("${transport.mqtt.bind_port}")
45 private Integer port; 45 private Integer port;
46 - @Value("${transport.mqtt.adaptor}")  
47 - private String adaptorName;  
48 46
49 @Value("${transport.mqtt.netty.leak_detector_level}") 47 @Value("${transport.mqtt.netty.leak_detector_level}")
50 private String leakDetectorLevel; 48 private String leakDetectorLevel;
@@ -38,7 +38,7 @@ @@ -38,7 +38,7 @@
38 <module>transport-api</module> 38 <module>transport-api</module>
39 <module>mqtt</module> 39 <module>mqtt</module>
40 <module>http</module> 40 <module>http</module>
41 - <!--module>coap</module--> 41 + <module>coap</module>
42 </modules> 42 </modules>
43 43
44 </project> 44 </project>
@@ -370,7 +370,7 @@ @@ -370,7 +370,7 @@
370 <version>${project.version}</version> 370 <version>${project.version}</version>
371 </dependency> 371 </dependency>
372 <dependency> 372 <dependency>
373 - <groupId>org.thingsboard.transport</groupId> 373 + <groupId>org.thingsboard.common.transport</groupId>
374 <artifactId>coap</artifactId> 374 <artifactId>coap</artifactId>
375 <version>${project.version}</version> 375 <version>${project.version}</version>
376 </dependency> 376 </dependency>
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +import org.apache.tools.ant.filters.ReplaceTokens
  17 +
  18 +buildscript {
  19 + ext {
  20 + osPackageVersion = "3.8.0"
  21 + }
  22 + repositories {
  23 + jcenter()
  24 + }
  25 + dependencies {
  26 + classpath("com.netflix.nebula:gradle-ospackage-plugin:${osPackageVersion}")
  27 + }
  28 +}
  29 +
  30 +apply plugin: "nebula.ospackage"
  31 +
  32 +buildDir = projectBuildDir
  33 +version = projectVersion
  34 +distsDirName = "./"
  35 +
  36 +// OS Package plugin configuration
  37 +ospackage {
  38 + packageName = pkgName
  39 + version = "${project.version}"
  40 + release = 1
  41 + os = LINUX
  42 + type = BINARY
  43 +
  44 + into pkgInstallFolder
  45 +
  46 + user pkgName
  47 + permissionGroup pkgName
  48 +
  49 + // Copy the actual .jar file
  50 + from(mainJar) {
  51 + // Strip the version from the jar filename
  52 + rename { String fileName ->
  53 + "${pkgName}.jar"
  54 + }
  55 + fileMode 0500
  56 + into "bin"
  57 + }
  58 +
  59 + // Copy the config files
  60 + from("target/conf") {
  61 + exclude "${pkgName}.conf"
  62 + fileType CONFIG | NOREPLACE
  63 + fileMode 0754
  64 + into "conf"
  65 + }
  66 +
  67 +}
  68 +
  69 +// Configure our RPM build task
  70 +buildRpm {
  71 +
  72 + arch = NOARCH
  73 +
  74 + version = projectVersion.replace('-', '')
  75 + archiveName = "${pkgName}.rpm"
  76 +
  77 + requires("java-1.8.0")
  78 +
  79 + from("target/conf") {
  80 + include "${pkgName}.conf"
  81 + filter(ReplaceTokens, tokens: ['pkg.platform': 'rpm'])
  82 + fileType CONFIG | NOREPLACE
  83 + fileMode 0754
  84 + into "${pkgInstallFolder}/conf"
  85 + }
  86 +
  87 + preInstall file("${buildDir}/control/rpm/preinst")
  88 + postInstall file("${buildDir}/control/rpm/postinst")
  89 + preUninstall file("${buildDir}/control/rpm/prerm")
  90 + postUninstall file("${buildDir}/control/rpm/postrm")
  91 +
  92 + user pkgName
  93 + permissionGroup pkgName
  94 +
  95 + // Copy the system unit files
  96 + from("${buildDir}/control/${pkgName}.service") {
  97 + addParentDirs = false
  98 + fileMode 0644
  99 + into "/usr/lib/systemd/system"
  100 + }
  101 +
  102 + directory(pkgLogFolder, 0755)
  103 + link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
  104 + link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
  105 +}
  106 +
  107 +// Same as the buildRpm task
  108 +buildDeb {
  109 +
  110 + arch = "all"
  111 +
  112 + archiveName = "${pkgName}.deb"
  113 +
  114 + requires("openjdk-8-jre").or("java8-runtime").or("oracle-java8-installer").or("openjdk-8-jre-headless")
  115 +
  116 + from("target/conf") {
  117 + include "${pkgName}.conf"
  118 + filter(ReplaceTokens, tokens: ['pkg.platform': 'deb'])
  119 + fileType CONFIG | NOREPLACE
  120 + fileMode 0754
  121 + into "${pkgInstallFolder}/conf"
  122 + }
  123 +
  124 + configurationFile("${pkgInstallFolder}/conf/${pkgName}.conf")
  125 + configurationFile("${pkgInstallFolder}/conf/${pkgName}.yml")
  126 + configurationFile("${pkgInstallFolder}/conf/logback.xml")
  127 +
  128 + preInstall file("${buildDir}/control/deb/preinst")
  129 + postInstall file("${buildDir}/control/deb/postinst")
  130 + preUninstall file("${buildDir}/control/deb/prerm")
  131 + postUninstall file("${buildDir}/control/deb/postrm")
  132 +
  133 + user pkgName
  134 + permissionGroup pkgName
  135 +
  136 + directory(pkgLogFolder, 0755)
  137 + link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/bin/${pkgName}.jar")
  138 + link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
  139 + link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
  140 +}
@@ -27,42 +27,37 @@ @@ -27,42 +27,37 @@
27 <artifactId>coap</artifactId> 27 <artifactId>coap</artifactId>
28 <packaging>jar</packaging> 28 <packaging>jar</packaging>
29 29
30 - <name>Thingsboard COAP Transport</name> 30 + <name>Thingsboard CoAP Transport Service</name>
31 <url>https://thingsboard.io</url> 31 <url>https://thingsboard.io</url>
32 32
33 <properties> 33 <properties>
34 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 34 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
35 <main.dir>${basedir}/../..</main.dir> 35 <main.dir>${basedir}/../..</main.dir>
  36 + <pkg.name>tb-coap-transport</pkg.name>
  37 + <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
  38 + <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
  39 + <pkg.win.dist>${project.build.directory}/windows</pkg.win.dist>
36 </properties> 40 </properties>
37 41
38 <dependencies> 42 <dependencies>
39 <dependency> 43 <dependency>
40 - <groupId>org.thingsboard.common</groupId>  
41 - <artifactId>transport</artifactId>  
42 - </dependency>  
43 - <dependency>  
44 - <groupId>org.eclipse.californium</groupId>  
45 - <artifactId>californium-core</artifactId>  
46 - </dependency>  
47 - <dependency>  
48 - <groupId>org.springframework</groupId>  
49 - <artifactId>spring-context</artifactId> 44 + <groupId>org.thingsboard.common.transport</groupId>
  45 + <artifactId>coap</artifactId>
50 </dependency> 46 </dependency>
51 <dependency> 47 <dependency>
52 - <groupId>org.slf4j</groupId>  
53 - <artifactId>slf4j-api</artifactId>  
54 - </dependency>  
55 - <dependency>  
56 - <groupId>org.slf4j</groupId>  
57 - <artifactId>log4j-over-slf4j</artifactId> 48 + <groupId>org.thingsboard.common</groupId>
  49 + <artifactId>queue</artifactId>
58 </dependency> 50 </dependency>
59 <dependency> 51 <dependency>
60 - <groupId>ch.qos.logback</groupId>  
61 - <artifactId>logback-core</artifactId> 52 + <groupId>org.springframework.boot</groupId>
  53 + <artifactId>spring-boot-starter-web</artifactId>
62 </dependency> 54 </dependency>
63 <dependency> 55 <dependency>
64 - <groupId>ch.qos.logback</groupId>  
65 - <artifactId>logback-classic</artifactId> 56 + <groupId>com.sun.winsw</groupId>
  57 + <artifactId>winsw</artifactId>
  58 + <classifier>bin</classifier>
  59 + <type>exe</type>
  60 + <scope>provided</scope>
66 </dependency> 61 </dependency>
67 <dependency> 62 <dependency>
68 <groupId>org.springframework.boot</groupId> 63 <groupId>org.springframework.boot</groupId>
@@ -81,4 +76,266 @@ @@ -81,4 +76,266 @@
81 </dependency> 76 </dependency>
82 </dependencies> 77 </dependencies>
83 78
  79 + <build>
  80 + <finalName>${pkg.name}-${project.version}</finalName>
  81 + <resources>
  82 + <resource>
  83 + <directory>${project.basedir}/src/main/resources</directory>
  84 + </resource>
  85 + </resources>
  86 + <plugins>
  87 + <plugin>
  88 + <groupId>org.apache.maven.plugins</groupId>
  89 + <artifactId>maven-resources-plugin</artifactId>
  90 + <executions>
  91 + <execution>
  92 + <id>copy-conf</id>
  93 + <phase>process-resources</phase>
  94 + <goals>
  95 + <goal>copy-resources</goal>
  96 + </goals>
  97 + <configuration>
  98 + <outputDirectory>${project.build.directory}/conf</outputDirectory>
  99 + <resources>
  100 + <resource>
  101 + <directory>src/main/resources</directory>
  102 + <excludes>
  103 + <exclude>logback.xml</exclude>
  104 + </excludes>
  105 + <filtering>false</filtering>
  106 + </resource>
  107 + </resources>
  108 + </configuration>
  109 + </execution>
  110 + <execution>
  111 + <id>copy-service-conf</id>
  112 + <phase>process-resources</phase>
  113 + <goals>
  114 + <goal>copy-resources</goal>
  115 + </goals>
  116 + <configuration>
  117 + <outputDirectory>${project.build.directory}/conf</outputDirectory>
  118 + <resources>
  119 + <resource>
  120 + <directory>src/main/conf</directory>
  121 + <filtering>true</filtering>
  122 + </resource>
  123 + </resources>
  124 + <filters>
  125 + <filter>src/main/filters/unix.properties</filter>
  126 + </filters>
  127 + </configuration>
  128 + </execution>
  129 + <execution>
  130 + <id>copy-win-conf</id>
  131 + <phase>process-resources</phase>
  132 + <goals>
  133 + <goal>copy-resources</goal>
  134 + </goals>
  135 + <configuration>
  136 + <outputDirectory>${pkg.win.dist}/conf</outputDirectory>
  137 + <resources>
  138 + <resource>
  139 + <directory>src/main/resources</directory>
  140 + <excludes>
  141 + <exclude>logback.xml</exclude>
  142 + </excludes>
  143 + <filtering>false</filtering>
  144 + </resource>
  145 + <resource>
  146 + <directory>src/main/conf</directory>
  147 + <excludes>
  148 + <exclude>tb-coap-transport.conf</exclude>
  149 + </excludes>
  150 + <filtering>true</filtering>
  151 + </resource>
  152 + </resources>
  153 + <filters>
  154 + <filter>src/main/filters/windows.properties</filter>
  155 + </filters>
  156 + </configuration>
  157 + </execution>
  158 + <execution>
  159 + <id>copy-control</id>
  160 + <phase>process-resources</phase>
  161 + <goals>
  162 + <goal>copy-resources</goal>
  163 + </goals>
  164 + <configuration>
  165 + <outputDirectory>${project.build.directory}/control</outputDirectory>
  166 + <resources>
  167 + <resource>
  168 + <directory>src/main/scripts/control</directory>
  169 + <filtering>true</filtering>
  170 + </resource>
  171 + </resources>
  172 + <filters>
  173 + <filter>src/main/filters/unix.properties</filter>
  174 + </filters>
  175 + </configuration>
  176 + </execution>
  177 + <execution>
  178 + <id>copy-windows-control</id>
  179 + <phase>process-resources</phase>
  180 + <goals>
  181 + <goal>copy-resources</goal>
  182 + </goals>
  183 + <configuration>
  184 + <outputDirectory>${pkg.win.dist}</outputDirectory>
  185 + <resources>
  186 + <resource>
  187 + <directory>src/main/scripts/windows</directory>
  188 + <filtering>true</filtering>
  189 + </resource>
  190 + </resources>
  191 + <filters>
  192 + <filter>src/main/filters/windows.properties</filter>
  193 + </filters>
  194 + </configuration>
  195 + </execution>
  196 + </executions>
  197 + </plugin>
  198 + <plugin>
  199 + <groupId>org.apache.maven.plugins</groupId>
  200 + <artifactId>maven-dependency-plugin</artifactId>
  201 + <executions>
  202 + <execution>
  203 + <id>copy-winsw-service</id>
  204 + <phase>package</phase>
  205 + <goals>
  206 + <goal>copy</goal>
  207 + </goals>
  208 + <configuration>
  209 + <artifactItems>
  210 + <artifactItem>
  211 + <groupId>com.sun.winsw</groupId>
  212 + <artifactId>winsw</artifactId>
  213 + <classifier>bin</classifier>
  214 + <type>exe</type>
  215 + <destFileName>service.exe</destFileName>
  216 + </artifactItem>
  217 + </artifactItems>
  218 + <outputDirectory>${pkg.win.dist}</outputDirectory>
  219 + </configuration>
  220 + </execution>
  221 + </executions>
  222 + </plugin>
  223 + <plugin>
  224 + <groupId>org.apache.maven.plugins</groupId>
  225 + <artifactId>maven-jar-plugin</artifactId>
  226 + <configuration>
  227 + <excludes>
  228 + <exclude>**/logback.xml</exclude>
  229 + </excludes>
  230 + <archive>
  231 + <manifestEntries>
  232 + <Implementation-Title>ThingsBoard CoAP Transport Service</Implementation-Title>
  233 + <Implementation-Version>${project.version}</Implementation-Version>
  234 + </manifestEntries>
  235 + </archive>
  236 + </configuration>
  237 + </plugin>
  238 + <plugin>
  239 + <groupId>org.springframework.boot</groupId>
  240 + <artifactId>spring-boot-maven-plugin</artifactId>
  241 + <configuration>
  242 + <mainClass>org.thingsboard.server.coap.ThingsboardCoapTransportApplication</mainClass>
  243 + <classifier>boot</classifier>
  244 + <layout>ZIP</layout>
  245 + <executable>true</executable>
  246 + <excludeDevtools>true</excludeDevtools>
  247 + <embeddedLaunchScriptProperties>
  248 + <confFolder>${pkg.installFolder}/conf</confFolder>
  249 + <logFolder>${pkg.unixLogFolder}</logFolder>
  250 + <logFilename>${pkg.name}.out</logFilename>
  251 + <initInfoProvides>${pkg.name}</initInfoProvides>
  252 + </embeddedLaunchScriptProperties>
  253 + </configuration>
  254 + <executions>
  255 + <execution>
  256 + <goals>
  257 + <goal>repackage</goal>
  258 + </goals>
  259 + </execution>
  260 + </executions>
  261 + </plugin>
  262 + <plugin>
  263 + <groupId>org.fortasoft</groupId>
  264 + <artifactId>gradle-maven-plugin</artifactId>
  265 + <configuration>
  266 + <tasks>
  267 + <task>build</task>
  268 + <task>buildDeb</task>
  269 + <task>buildRpm</task>
  270 + </tasks>
  271 + <args>
  272 + <arg>-PprojectBuildDir=${project.build.directory}</arg>
  273 + <arg>-PprojectVersion=${project.version}</arg>
  274 + <arg>-PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</arg>
  275 + <arg>-PpkgName=${pkg.name}</arg>
  276 + <arg>-PpkgInstallFolder=${pkg.installFolder}</arg>
  277 + <arg>-PpkgLogFolder=${pkg.unixLogFolder}</arg>
  278 + </args>
  279 + </configuration>
  280 + <executions>
  281 + <execution>
  282 + <phase>package</phase>
  283 + <goals>
  284 + <goal>invoke</goal>
  285 + </goals>
  286 + </execution>
  287 + </executions>
  288 + </plugin>
  289 + <plugin>
  290 + <groupId>org.apache.maven.plugins</groupId>
  291 + <artifactId>maven-assembly-plugin</artifactId>
  292 + <configuration>
  293 + <finalName>${pkg.name}</finalName>
  294 + <descriptors>
  295 + <descriptor>src/main/assembly/windows.xml</descriptor>
  296 + </descriptors>
  297 + </configuration>
  298 + <executions>
  299 + <execution>
  300 + <id>assembly</id>
  301 + <phase>package</phase>
  302 + <goals>
  303 + <goal>single</goal>
  304 + </goals>
  305 + </execution>
  306 + </executions>
  307 + </plugin>
  308 + <plugin>
  309 + <groupId>org.apache.maven.plugins</groupId>
  310 + <artifactId>maven-install-plugin</artifactId>
  311 + <configuration>
  312 + <file>${project.build.directory}/${pkg.name}.deb</file>
  313 + <artifactId>${project.artifactId}</artifactId>
  314 + <groupId>${project.groupId}</groupId>
  315 + <version>${project.version}</version>
  316 + <classifier>deb</classifier>
  317 + <packaging>deb</packaging>
  318 + </configuration>
  319 + <executions>
  320 + <execution>
  321 + <id>install-deb</id>
  322 + <phase>package</phase>
  323 + <goals>
  324 + <goal>install-file</goal>
  325 + </goals>
  326 + </execution>
  327 + </executions>
  328 + </plugin>
  329 + </plugins>
  330 + </build>
  331 + <repositories>
  332 + <repository>
  333 + <id>jenkins</id>
  334 + <name>Jenkins Repository</name>
  335 + <url>http://repo.jenkins-ci.org/releases</url>
  336 + <snapshots>
  337 + <enabled>false</enabled>
  338 + </snapshots>
  339 + </repository>
  340 + </repositories>
84 </project> 341 </project>
  1 +<!--
  2 +
  3 + Copyright © 2016-2018 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
  19 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  20 + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
  21 + <id>windows</id>
  22 +
  23 + <formats>
  24 + <format>zip</format>
  25 + </formats>
  26 +
  27 + <!-- Workaround to create logs directory -->
  28 + <fileSets>
  29 + <fileSet>
  30 + <directory>${pkg.win.dist}</directory>
  31 + <outputDirectory>logs</outputDirectory>
  32 + <excludes>
  33 + <exclude>*/**</exclude>
  34 + </excludes>
  35 + </fileSet>
  36 + <fileSet>
  37 + <directory>${pkg.win.dist}/conf</directory>
  38 + <outputDirectory>conf</outputDirectory>
  39 + <lineEnding>windows</lineEnding>
  40 + </fileSet>
  41 + </fileSets>
  42 +
  43 + <files>
  44 + <file>
  45 + <source>${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</source>
  46 + <outputDirectory>lib</outputDirectory>
  47 + <destName>${pkg.name}.jar</destName>
  48 + </file>
  49 + <file>
  50 + <source>${pkg.win.dist}/service.exe</source>
  51 + <outputDirectory/>
  52 + <destName>${pkg.name}.exe</destName>
  53 + </file>
  54 + <file>
  55 + <source>${pkg.win.dist}/service.xml</source>
  56 + <outputDirectory/>
  57 + <destName>${pkg.name}.xml</destName>
  58 + <lineEnding>windows</lineEnding>
  59 + </file>
  60 + <file>
  61 + <source>${pkg.win.dist}/install.bat</source>
  62 + <outputDirectory/>
  63 + <lineEnding>windows</lineEnding>
  64 + </file>
  65 + <file>
  66 + <source>${pkg.win.dist}/uninstall.bat</source>
  67 + <outputDirectory/>
  68 + <lineEnding>windows</lineEnding>
  69 + </file>
  70 + </files>
  71 +</assembly>
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!--
  3 +
  4 + Copyright © 2016-2018 The Thingsboard Authors
  5 +
  6 + Licensed under the Apache License, Version 2.0 (the "License");
  7 + you may not use this file except in compliance with the License.
  8 + You may obtain a copy of the License at
  9 +
  10 + http://www.apache.org/licenses/LICENSE-2.0
  11 +
  12 + Unless required by applicable law or agreed to in writing, software
  13 + distributed under the License is distributed on an "AS IS" BASIS,
  14 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 + See the License for the specific language governing permissions and
  16 + limitations under the License.
  17 +
  18 +-->
  19 +<!DOCTYPE configuration>
  20 +<configuration>
  21 +
  22 + <appender name="fileLogAppender"
  23 + class="ch.qos.logback.core.rolling.RollingFileAppender">
  24 + <file>${pkg.logFolder}/${pkg.name}.log</file>
  25 + <rollingPolicy
  26 + class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
  27 + <fileNamePattern>${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  28 + <maxFileSize>100MB</maxFileSize>
  29 + <maxHistory>30</maxHistory>
  30 + <totalSizeCap>3GB</totalSizeCap>
  31 + </rollingPolicy>
  32 + <encoder>
  33 + <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
  34 + </encoder>
  35 + </appender>
  36 +
  37 + <logger name="org.thingsboard.server" level="INFO" />
  38 +
  39 + <root level="INFO">
  40 + <appender-ref ref="fileLogAppender"/>
  41 + </root>
  42 +
  43 +</configuration>
  1 +#
  2 +# Copyright © 2016-2018 The Thingsboard Authors
  3 +#
  4 +# Licensed under the Apache License, Version 2.0 (the "License");
  5 +# you may not use this file except in compliance with the License.
  6 +# You may obtain a copy of the License at
  7 +#
  8 +# http://www.apache.org/licenses/LICENSE-2.0
  9 +#
  10 +# Unless required by applicable law or agreed to in writing, software
  11 +# distributed under the License is distributed on an "AS IS" BASIS,
  12 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +# See the License for the specific language governing permissions and
  14 +# limitations under the License.
  15 +#
  16 +
  17 +export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
  18 +export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
  19 +export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
  20 +export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
  21 +export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"
  22 +export LOG_FILENAME=${pkg.name}.out
  23 +export LOADER_PATH=${pkg.installFolder}/conf
  1 +pkg.logFolder=${pkg.unixLogFolder}
  1 +pkg.logFolder=${BASE}\\logs
  2 +pkg.winWrapperLogFolder=%BASE%\\logs
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.coap;
  17 +
  18 +import org.springframework.boot.SpringApplication;
  19 +import org.springframework.boot.SpringBootConfiguration;
  20 +import org.springframework.context.annotation.ComponentScan;
  21 +import org.springframework.scheduling.annotation.EnableAsync;
  22 +import org.springframework.scheduling.annotation.EnableScheduling;
  23 +
  24 +import java.util.Arrays;
  25 +
  26 +@SpringBootConfiguration
  27 +@EnableAsync
  28 +@EnableScheduling
  29 +@ComponentScan({"org.thingsboard.server.coap", "org.thingsboard.server.common", "org.thingsboard.server.transport.coap", "org.thingsboard.server.kafka"})
  30 +public class ThingsboardCoapTransportApplication {
  31 +
  32 + private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
  33 + private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "tb-coap-transport";
  34 +
  35 + public static void main(String[] args) {
  36 + SpringApplication.run(ThingsboardCoapTransportApplication.class, updateArguments(args));
  37 + }
  38 +
  39 + private static String[] updateArguments(String[] args) {
  40 + if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) {
  41 + String[] modifiedArgs = new String[args.length + 1];
  42 + System.arraycopy(args, 0, modifiedArgs, 0, args.length);
  43 + modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM;
  44 + return modifiedArgs;
  45 + }
  46 + return args;
  47 + }
  48 +}
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.transport.coap;  
17 -  
18 -import java.lang.reflect.Field;  
19 -import java.util.List;  
20 -import java.util.Optional;  
21 -  
22 -import lombok.extern.slf4j.Slf4j;  
23 -import org.eclipse.californium.core.CoapResource;  
24 -import org.eclipse.californium.core.coap.CoAP.ResponseCode;  
25 -import org.eclipse.californium.core.coap.Request;  
26 -import org.eclipse.californium.core.network.Exchange;  
27 -import org.eclipse.californium.core.network.ExchangeObserver;  
28 -import org.eclipse.californium.core.server.resources.CoapExchange;  
29 -import org.eclipse.californium.core.server.resources.Resource;  
30 -import org.thingsboard.server.common.data.security.DeviceCredentialsFilter;  
31 -import org.thingsboard.server.common.data.security.DeviceTokenCredentials;  
32 -import org.thingsboard.server.common.msg.session.*;  
33 -import org.thingsboard.server.common.transport.SessionMsgProcessor;  
34 -import org.thingsboard.server.common.transport.adaptor.AdaptorException;  
35 -import org.thingsboard.server.common.transport.auth.DeviceAuthService;  
36 -import org.thingsboard.server.common.transport.quota.QuotaService;  
37 -import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;  
38 -import org.thingsboard.server.transport.coap.session.CoapExchangeObserverProxy;  
39 -import org.thingsboard.server.transport.coap.session.CoapSessionCtx;  
40 -import org.springframework.util.ReflectionUtils;  
41 -  
42 -@Slf4j  
43 -public class CoapTransportResource extends CoapResource {  
44 - // coap://localhost:port/api/v1/DEVICE_TOKEN/[attributes|telemetry|rpc[/requestId]]  
45 - private static final int ACCESS_TOKEN_POSITION = 3;  
46 - private static final int FEATURE_TYPE_POSITION = 4;  
47 - private static final int REQUEST_ID_POSITION = 5;  
48 -  
49 - private final CoapTransportAdaptor adaptor;  
50 - private final SessionMsgProcessor processor;  
51 - private final DeviceAuthService authService;  
52 - private final QuotaService quotaService;  
53 - private final Field observerField;  
54 - private final long timeout;  
55 -  
56 - public CoapTransportResource(SessionMsgProcessor processor, DeviceAuthService authService, CoapTransportAdaptor adaptor, String name,  
57 - long timeout, QuotaService quotaService) {  
58 - super(name);  
59 - this.processor = processor;  
60 - this.authService = authService;  
61 - this.quotaService = quotaService;  
62 - this.adaptor = adaptor;  
63 - this.timeout = timeout;  
64 - // This is important to turn off existing observable logic in  
65 - // CoapResource. We will have our own observe monitoring due to 1:1  
66 - // observe relationship.  
67 - this.setObservable(false);  
68 - observerField = ReflectionUtils.findField(Exchange.class, "observer");  
69 - observerField.setAccessible(true);  
70 - }  
71 -  
72 - @Override  
73 - public void handleGET(CoapExchange exchange) {  
74 - if(quotaService.isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) {  
75 - log.warn("COAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort());  
76 - exchange.respond(ResponseCode.BAD_REQUEST);  
77 - return;  
78 - }  
79 -  
80 - Optional<FeatureType> featureType = getFeatureType(exchange.advanced().getRequest());  
81 - if (!featureType.isPresent()) {  
82 - log.trace("Missing feature type parameter");  
83 - exchange.respond(ResponseCode.BAD_REQUEST);  
84 - } else if (featureType.get() == FeatureType.TELEMETRY) {  
85 - log.trace("Can't fetch/subscribe to timeseries updates");  
86 - exchange.respond(ResponseCode.BAD_REQUEST);  
87 - } else if (exchange.getRequestOptions().hasObserve()) {  
88 - processExchangeGetRequest(exchange, featureType.get());  
89 - } else if (featureType.get() == FeatureType.ATTRIBUTES) {  
90 - processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);  
91 - } else {  
92 - log.trace("Invalid feature type parameter");  
93 - exchange.respond(ResponseCode.BAD_REQUEST);  
94 - }  
95 - }  
96 -  
97 - private void processExchangeGetRequest(CoapExchange exchange, FeatureType featureType) {  
98 - boolean unsubscribe = exchange.getRequestOptions().getObserve() == 1;  
99 - SessionMsgType sessionMsgType;  
100 - if (featureType == FeatureType.RPC) {  
101 - sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST : SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST;  
102 - } else {  
103 - sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST : SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST;  
104 - }  
105 - Optional<SessionId> sessionId = processRequest(exchange, sessionMsgType);  
106 - if (sessionId.isPresent()) {  
107 - if (exchange.getRequestOptions().getObserve() == 1) {  
108 - exchange.respond(ResponseCode.VALID);  
109 - }  
110 - }  
111 - }  
112 -  
113 - @Override  
114 - public void handlePOST(CoapExchange exchange) {  
115 - if(quotaService.isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) {  
116 - log.warn("COAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort());  
117 - exchange.respond(ResponseCode.BAD_REQUEST);  
118 - return;  
119 - }  
120 -  
121 - Optional<FeatureType> featureType = getFeatureType(exchange.advanced().getRequest());  
122 - if (!featureType.isPresent()) {  
123 - log.trace("Missing feature type parameter");  
124 - exchange.respond(ResponseCode.BAD_REQUEST);  
125 - } else {  
126 - switch (featureType.get()) {  
127 - case ATTRIBUTES:  
128 - processRequest(exchange, SessionMsgType.POST_ATTRIBUTES_REQUEST);  
129 - break;  
130 - case TELEMETRY:  
131 - processRequest(exchange, SessionMsgType.POST_TELEMETRY_REQUEST);  
132 - break;  
133 - case RPC:  
134 - Optional<Integer> requestId = getRequestId(exchange.advanced().getRequest());  
135 - if (requestId.isPresent()) {  
136 - processRequest(exchange, SessionMsgType.TO_DEVICE_RPC_RESPONSE);  
137 - } else {  
138 - processRequest(exchange, SessionMsgType.TO_SERVER_RPC_REQUEST);  
139 - }  
140 - break;  
141 - }  
142 - }  
143 - }  
144 -  
145 - private Optional<SessionId> processRequest(CoapExchange exchange, SessionMsgType type) {  
146 - log.trace("Processing {}", exchange.advanced().getRequest());  
147 - exchange.accept();  
148 - Exchange advanced = exchange.advanced();  
149 - Request request = advanced.getRequest();  
150 -  
151 - Optional<DeviceCredentialsFilter> credentials = decodeCredentials(request);  
152 - if (!credentials.isPresent()) {  
153 - exchange.respond(ResponseCode.BAD_REQUEST);  
154 - return Optional.empty();  
155 - }  
156 -  
157 - CoapSessionCtx ctx = new CoapSessionCtx(exchange, adaptor, processor, authService, timeout);  
158 -  
159 -// if (!ctx.login(credentials.get())) {  
160 -// exchange.respond(ResponseCode.UNAUTHORIZED);  
161 -// return Optional.empty();  
162 -// }  
163 -  
164 - AdaptorToSessionActorMsg msg;  
165 - try {  
166 - switch (type) {  
167 - case GET_ATTRIBUTES_REQUEST:  
168 - case POST_ATTRIBUTES_REQUEST:  
169 - case POST_TELEMETRY_REQUEST:  
170 - case TO_DEVICE_RPC_RESPONSE:  
171 - case TO_SERVER_RPC_REQUEST:  
172 - ctx.setSessionType(SessionType.SYNC);  
173 - msg = adaptor.convertToActorMsg(ctx, type, request);  
174 - break;  
175 - case SUBSCRIBE_ATTRIBUTES_REQUEST:  
176 - case SUBSCRIBE_RPC_COMMANDS_REQUEST:  
177 - ExchangeObserver systemObserver = (ExchangeObserver) observerField.get(advanced);  
178 - advanced.setObserver(new CoapExchangeObserverProxy(systemObserver, ctx));  
179 - ctx.setSessionType(SessionType.ASYNC);  
180 - msg = adaptor.convertToActorMsg(ctx, type, request);  
181 - break;  
182 - case UNSUBSCRIBE_ATTRIBUTES_REQUEST:  
183 - case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:  
184 - ctx.setSessionType(SessionType.ASYNC);  
185 - msg = adaptor.convertToActorMsg(ctx, type, request);  
186 - break;  
187 - default:  
188 - log.trace("[{}] Unsupported msg type: {}", ctx.getSessionId(), type);  
189 - throw new IllegalArgumentException("Unsupported msg type: " + type);  
190 - }  
191 - log.trace("Processing msg: {}", msg);  
192 -// processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg));  
193 - } catch (AdaptorException e) {  
194 - log.debug("Failed to decode payload {}", e);  
195 - exchange.respond(ResponseCode.BAD_REQUEST, e.getMessage());  
196 - return Optional.empty();  
197 - } catch (IllegalArgumentException | IllegalAccessException e) {  
198 - log.debug("Failed to process payload {}", e);  
199 - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR, e.getMessage());  
200 - }  
201 - return Optional.of(ctx.getSessionId());  
202 - }  
203 -  
204 - private Optional<DeviceCredentialsFilter> decodeCredentials(Request request) {  
205 - List<String> uriPath = request.getOptions().getUriPath();  
206 - DeviceCredentialsFilter credentials = null;  
207 - if (uriPath.size() >= ACCESS_TOKEN_POSITION) {  
208 - credentials = new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1));  
209 - }  
210 - return Optional.ofNullable(credentials);  
211 - }  
212 -  
213 - private Optional<FeatureType> getFeatureType(Request request) {  
214 - List<String> uriPath = request.getOptions().getUriPath();  
215 - try {  
216 - if (uriPath.size() >= FEATURE_TYPE_POSITION) {  
217 - return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION - 1).toUpperCase()));  
218 - }  
219 - } catch (RuntimeException e) {  
220 - log.warn("Failed to decode feature type: {}", uriPath);  
221 - }  
222 - return Optional.empty();  
223 - }  
224 -  
225 - public static Optional<Integer> getRequestId(Request request) {  
226 - List<String> uriPath = request.getOptions().getUriPath();  
227 - try {  
228 - if (uriPath.size() >= REQUEST_ID_POSITION) {  
229 - return Optional.of(Integer.valueOf(uriPath.get(REQUEST_ID_POSITION - 1)));  
230 - }  
231 - } catch (RuntimeException e) {  
232 - log.warn("Failed to decode feature type: {}", uriPath);  
233 - }  
234 - return Optional.empty();  
235 - }  
236 -  
237 - @Override  
238 - public Resource getChild(String name) {  
239 - return this;  
240 - }  
241 -  
242 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.transport.coap.adaptors;  
17 -  
18 -import java.util.*;  
19 -  
20 -import lombok.extern.slf4j.Slf4j;  
21 -import org.eclipse.californium.core.coap.CoAP.ResponseCode;  
22 -import org.eclipse.californium.core.coap.Request;  
23 -import org.eclipse.californium.core.coap.Response;  
24 -import org.springframework.util.StringUtils;  
25 -import org.thingsboard.server.common.msg.core.*;  
26 -import org.thingsboard.server.common.msg.kv.AttributesKVMsg;  
27 -import org.thingsboard.server.common.msg.session.SessionMsgType;  
28 -import org.thingsboard.server.common.msg.session.SessionContext;  
29 -import org.thingsboard.server.common.msg.session.ex.ProcessingTimeoutException;  
30 -import org.thingsboard.server.common.transport.adaptor.AdaptorException;  
31 -import org.thingsboard.server.common.transport.adaptor.JsonConverter;  
32 -import org.springframework.stereotype.Component;  
33 -  
34 -import com.google.gson.JsonObject;  
35 -import com.google.gson.JsonParser;  
36 -import com.google.gson.JsonSyntaxException;  
37 -import org.thingsboard.server.transport.coap.CoapTransportResource;  
38 -import org.thingsboard.server.transport.coap.session.CoapSessionCtx;  
39 -  
40 -@Component("JsonCoapAdaptor")  
41 -@Slf4j  
42 -public class JsonCoapAdaptor implements CoapTransportAdaptor {  
43 -  
44 - @Override  
45 - public AdaptorToSessionActorMsg convertToActorMsg(CoapSessionCtx ctx, SessionMsgType type, Request inbound) throws AdaptorException {  
46 - FromDeviceMsg msg = null;  
47 - switch (type) {  
48 - case POST_TELEMETRY_REQUEST:  
49 - msg = convertToTelemetryUploadRequest(ctx, inbound);  
50 - break;  
51 - case POST_ATTRIBUTES_REQUEST:  
52 - msg = convertToUpdateAttributesRequest(ctx, inbound);  
53 - break;  
54 - case GET_ATTRIBUTES_REQUEST:  
55 - msg = convertToGetAttributesRequest(ctx, inbound);  
56 - break;  
57 - case SUBSCRIBE_RPC_COMMANDS_REQUEST:  
58 - msg = new RpcSubscribeMsg();  
59 - break;  
60 - case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:  
61 - msg = new RpcUnsubscribeMsg();  
62 - break;  
63 - case TO_DEVICE_RPC_RESPONSE:  
64 - msg = convertToDeviceRpcResponse(ctx, inbound);  
65 - break;  
66 - case TO_SERVER_RPC_REQUEST:  
67 - msg = convertToServerRpcRequest(ctx, inbound);  
68 - break;  
69 - case SUBSCRIBE_ATTRIBUTES_REQUEST:  
70 - msg = new AttributesSubscribeMsg();  
71 - break;  
72 - case UNSUBSCRIBE_ATTRIBUTES_REQUEST:  
73 - msg = new AttributesUnsubscribeMsg();  
74 - break;  
75 - default:  
76 - log.warn("[{}] Unsupported msg type: {}!", ctx.getSessionId(), type);  
77 - throw new AdaptorException(new IllegalArgumentException("Unsupported msg type: " + type + "!"));  
78 - }  
79 - return new BasicAdaptorToSessionActorMsg(ctx, msg);  
80 - }  
81 -  
82 - private FromDeviceMsg convertToDeviceRpcResponse(CoapSessionCtx ctx, Request inbound) throws AdaptorException {  
83 - Optional<Integer> requestId = CoapTransportResource.getRequestId(inbound);  
84 - String payload = validatePayload(ctx, inbound);  
85 - JsonObject response = new JsonParser().parse(payload).getAsJsonObject();  
86 - return new ToDeviceRpcResponseMsg(  
87 - requestId.orElseThrow(() -> new AdaptorException("Request id is missing!")),  
88 - response.get("response").toString());  
89 - }  
90 -  
91 - private FromDeviceMsg convertToServerRpcRequest(CoapSessionCtx ctx, Request inbound) throws AdaptorException {  
92 -  
93 - String payload = validatePayload(ctx, inbound);  
94 -  
95 -// return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), 0);  
96 - return null;  
97 - }  
98 -  
99 - @Override  
100 - public Optional<Response> convertToAdaptorMsg(CoapSessionCtx ctx, SessionActorToAdaptorMsg source) throws AdaptorException {  
101 - ToDeviceMsg msg = source.getMsg();  
102 - switch (msg.getSessionMsgType()) {  
103 - case STATUS_CODE_RESPONSE:  
104 - case TO_DEVICE_RPC_RESPONSE_ACK:  
105 - return Optional.of(convertStatusCodeResponse((StatusCodeResponse) msg));  
106 - case GET_ATTRIBUTES_RESPONSE:  
107 - return Optional.of(convertGetAttributesResponse((GetAttributesResponse) msg));  
108 - case ATTRIBUTES_UPDATE_NOTIFICATION:  
109 - return Optional.of(convertNotificationResponse(ctx, (AttributesUpdateNotification) msg));  
110 - case TO_DEVICE_RPC_REQUEST:  
111 - return Optional.of(convertToDeviceRpcRequest(ctx, (ToDeviceRpcRequestMsg) msg));  
112 - case TO_SERVER_RPC_RESPONSE:  
113 - return Optional.of(convertToServerRpcResponse(ctx, (ToServerRpcResponseMsg) msg));  
114 - case RULE_ENGINE_ERROR:  
115 - return Optional.of(convertToRuleEngineErrorResponse(ctx, (RuleEngineErrorMsg) msg));  
116 - default:  
117 - log.warn("[{}] Unsupported msg type: {}!", source.getSessionId(), msg.getSessionMsgType());  
118 - throw new AdaptorException(new IllegalArgumentException("Unsupported msg type: " + msg.getSessionMsgType() + "!"));  
119 - }  
120 - }  
121 -  
122 - private Response convertToRuleEngineErrorResponse(CoapSessionCtx ctx, RuleEngineErrorMsg msg) {  
123 - ResponseCode status = ResponseCode.INTERNAL_SERVER_ERROR;  
124 - switch (msg.getError()) {  
125 - case QUEUE_PUT_TIMEOUT:  
126 - status = ResponseCode.GATEWAY_TIMEOUT;  
127 - break;  
128 - default:  
129 - if (msg.getInSessionMsgType() == SessionMsgType.TO_SERVER_RPC_REQUEST) {  
130 - status = ResponseCode.BAD_REQUEST;  
131 - }  
132 - break;  
133 - }  
134 - Response response = new Response(status);  
135 - response.setPayload(JsonConverter.toErrorJson(msg.getErrorMsg()).toString());  
136 - return response;  
137 - }  
138 -  
139 - private Response convertNotificationResponse(CoapSessionCtx ctx, AttributesUpdateNotification msg) {  
140 - return getObserveNotification(ctx, JsonConverter.toJson(msg.getData(), false));  
141 - }  
142 -  
143 - private Response convertToDeviceRpcRequest(CoapSessionCtx ctx, ToDeviceRpcRequestMsg msg) {  
144 - return getObserveNotification(ctx, JsonConverter.toJson(msg, true));  
145 - }  
146 -  
147 - private Response getObserveNotification(CoapSessionCtx ctx, JsonObject json) {  
148 - Response response = new Response(ResponseCode.CONTENT);  
149 - response.getOptions().setObserve(ctx.nextSeqNumber());  
150 - response.setPayload(json.toString());  
151 - return response;  
152 - }  
153 -  
154 - private AttributesUpdateRequest convertToUpdateAttributesRequest(SessionContext ctx, Request inbound) throws AdaptorException {  
155 - String payload = validatePayload(ctx, inbound);  
156 - try {  
157 - return JsonConverter.convertToAttributes(new JsonParser().parse(payload));  
158 - } catch (IllegalStateException | JsonSyntaxException ex) {  
159 - throw new AdaptorException(ex);  
160 - }  
161 - }  
162 -  
163 - private FromDeviceMsg convertToGetAttributesRequest(SessionContext ctx, Request inbound) throws AdaptorException {  
164 - List<String> queryElements = inbound.getOptions().getUriQuery();  
165 - if (queryElements != null && queryElements.size() > 0) {  
166 - Set<String> clientKeys = toKeys(ctx, queryElements, "clientKeys");  
167 - Set<String> sharedKeys = toKeys(ctx, queryElements, "sharedKeys");  
168 - return new BasicGetAttributesRequest(0, clientKeys, sharedKeys);  
169 - } else {  
170 - return new BasicGetAttributesRequest(0);  
171 - }  
172 - }  
173 -  
174 - private Set<String> toKeys(SessionContext ctx, List<String> queryElements, String attributeName) throws AdaptorException {  
175 - String keys = null;  
176 - for (String queryElement : queryElements) {  
177 - String[] queryItem = queryElement.split("=");  
178 - if (queryItem.length == 2 && queryItem[0].equals(attributeName)) {  
179 - keys = queryItem[1];  
180 - }  
181 - }  
182 - if (keys != null && !StringUtils.isEmpty(keys)) {  
183 - return new HashSet<>(Arrays.asList(keys.split(",")));  
184 - } else {  
185 - return null;  
186 - }  
187 - }  
188 -  
189 - private TelemetryUploadRequest convertToTelemetryUploadRequest(SessionContext ctx, Request inbound) throws AdaptorException {  
190 - String payload = validatePayload(ctx, inbound);  
191 - try {  
192 - return JsonConverter.convertToTelemetry(new JsonParser().parse(payload));  
193 - } catch (IllegalStateException | JsonSyntaxException ex) {  
194 - throw new AdaptorException(ex);  
195 - }  
196 - }  
197 -  
198 - private Response convertStatusCodeResponse(StatusCodeResponse msg) {  
199 - if (msg.isSuccess()) {  
200 - Optional<Integer> code = msg.getData();  
201 - if (code.isPresent() && code.get() == 200) {  
202 - return new Response(ResponseCode.VALID);  
203 - } else {  
204 - return new Response(ResponseCode.CREATED);  
205 - }  
206 - } else {  
207 - return convertError(msg.getError());  
208 - }  
209 - }  
210 -  
211 - private String validatePayload(SessionContext ctx, Request inbound) throws AdaptorException {  
212 - String payload = inbound.getPayloadString();  
213 - if (payload == null) {  
214 - log.warn("[{}] Payload is empty!", ctx.getSessionId());  
215 - throw new AdaptorException(new IllegalArgumentException("Payload is empty!"));  
216 - }  
217 - return payload;  
218 - }  
219 -  
220 - private Response convertToServerRpcResponse(SessionContext ctx, ToServerRpcResponseMsg msg) {  
221 - if (msg.isSuccess()) {  
222 - Response response = new Response(ResponseCode.CONTENT);  
223 -// JsonElement result = JsonConverter.toJson(msg);  
224 -// response.setPayload(result.toString());  
225 - return response;  
226 - } else {  
227 - return convertError(Optional.of(new RuntimeException("Server RPC response is empty!")));  
228 - }  
229 - }  
230 -  
231 - private Response convertGetAttributesResponse(GetAttributesResponse msg) {  
232 - if (msg.isSuccess()) {  
233 - Optional<AttributesKVMsg> payload = msg.getData();  
234 - if (!payload.isPresent() || (payload.get().getClientAttributes().isEmpty() && payload.get().getSharedAttributes().isEmpty())) {  
235 - return new Response(ResponseCode.NOT_FOUND);  
236 - } else {  
237 - Response response = new Response(ResponseCode.CONTENT);  
238 - JsonObject result = JsonConverter.toJson(payload.get(), false);  
239 - response.setPayload(result.toString());  
240 - return response;  
241 - }  
242 - } else {  
243 - return convertError(msg.getError());  
244 - }  
245 - }  
246 -  
247 - private Response convertError(Optional<Exception> exception) {  
248 - if (exception.isPresent()) {  
249 - log.warn("Converting exception: {}", exception.get().getMessage(), exception.get());  
250 - if (exception.get() instanceof ProcessingTimeoutException) {  
251 - return new Response(ResponseCode.SERVICE_UNAVAILABLE);  
252 - } else {  
253 - return new Response(ResponseCode.INTERNAL_SERVER_ERROR);  
254 - }  
255 - } else {  
256 - return new Response(ResponseCode.INTERNAL_SERVER_ERROR);  
257 - }  
258 - }  
259 -  
260 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.transport.coap.session;  
17 -  
18 -import org.eclipse.californium.core.network.Exchange;  
19 -import org.eclipse.californium.core.network.ExchangeObserver;  
20 -  
21 -public class CoapExchangeObserverProxy implements ExchangeObserver {  
22 -  
23 - private final ExchangeObserver proxy;  
24 - private final CoapSessionCtx ctx;  
25 -  
26 - public CoapExchangeObserverProxy(ExchangeObserver proxy, CoapSessionCtx ctx) {  
27 - super();  
28 - this.proxy = proxy;  
29 - this.ctx = ctx;  
30 - }  
31 -  
32 - @Override  
33 - public void completed(Exchange exchange) {  
34 - proxy.completed(exchange);  
35 - ctx.close();  
36 - }  
37 -  
38 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.transport.coap.session;  
17 -  
18 -import lombok.extern.slf4j.Slf4j;  
19 -import org.eclipse.californium.core.coap.CoAP.ResponseCode;  
20 -import org.eclipse.californium.core.coap.Request;  
21 -import org.eclipse.californium.core.coap.Response;  
22 -import org.eclipse.californium.core.server.resources.CoapExchange;  
23 -import org.thingsboard.server.common.msg.session.ex.SessionException;  
24 -import org.thingsboard.server.common.transport.SessionMsgProcessor;  
25 -import org.thingsboard.server.common.transport.adaptor.AdaptorException;  
26 -import org.thingsboard.server.common.transport.auth.DeviceAuthService;  
27 -import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;  
28 -import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;  
29 -  
30 -import java.util.concurrent.atomic.AtomicInteger;  
31 -  
32 -@Slf4j  
33 -public class CoapSessionCtx extends DeviceAwareSessionContext {  
34 -  
35 - private final SessionId sessionId;  
36 - private final CoapExchange exchange;  
37 - private final CoapTransportAdaptor adaptor;  
38 - private final String token;  
39 - private final long timeout;  
40 - private SessionType sessionType;  
41 - private final AtomicInteger seqNumber = new AtomicInteger(2);  
42 -  
43 - public CoapSessionCtx(CoapExchange exchange, CoapTransportAdaptor adaptor, SessionMsgProcessor processor, DeviceAuthService authService, long timeout) {  
44 - super();  
45 - Request request = exchange.advanced().getRequest();  
46 - this.token = request.getTokenString();  
47 - this.sessionId = new CoapSessionId(request.getSource().getHostAddress(), request.getSourcePort(), this.token);  
48 - this.exchange = exchange;  
49 - this.adaptor = adaptor;  
50 - this.timeout = timeout;  
51 - }  
52 -  
53 -  
54 - @Override  
55 - public void onMsg(SessionActorToAdaptorMsg msg) throws SessionException {  
56 - try {  
57 - adaptor.convertToAdaptorMsg(this, msg).ifPresent(this::pushToNetwork);  
58 - } catch (AdaptorException e) {  
59 - logAndWrap(e);  
60 - }  
61 - }  
62 -  
63 - private void pushToNetwork(Response response) {  
64 - exchange.respond(response);  
65 - }  
66 -  
67 - private void logAndWrap(AdaptorException e) throws SessionException {  
68 - log.warn("Failed to convert msg: {}", e.getMessage(), e);  
69 - throw new SessionException(e);  
70 - }  
71 -  
72 - @Override  
73 - public void onMsg(SessionCtrlMsg msg) throws SessionException {  
74 - log.debug("[{}] onCtrl: {}", sessionId, msg);  
75 - if (msg instanceof SessionCloseMsg) {  
76 - onSessionClose((SessionCloseMsg) msg);  
77 - }  
78 - }  
79 -  
80 - private void onSessionClose(SessionCloseMsg msg) {  
81 - if (msg.isTimeout()) {  
82 - exchange.respond(ResponseCode.SERVICE_UNAVAILABLE);  
83 - } else if (msg.isCredentialsRevoked()) {  
84 - exchange.respond(ResponseCode.UNAUTHORIZED);  
85 - } else {  
86 - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);  
87 - }  
88 - }  
89 -  
90 - @Override  
91 - public SessionId getSessionId() {  
92 - return sessionId;  
93 - }  
94 -  
95 - @Override  
96 - public String toString() {  
97 - return "CoapSessionCtx [sessionId=" + sessionId + "]";  
98 - }  
99 -  
100 - @Override  
101 - public boolean isClosed() {  
102 - return exchange.advanced().isComplete() || exchange.advanced().isTimedOut();  
103 - }  
104 -  
105 - public void close() {  
106 - log.info("[{}] Closing processing context. Timeout: {}", sessionId, exchange.advanced().isTimedOut());  
107 -// processor.process(exchange.advanced().isTimedOut() ? SessionCloseMsg.onTimeout(sessionId) : SessionCloseMsg.onError(sessionId));  
108 - }  
109 -  
110 - @Override  
111 - public long getTimeout() {  
112 - return timeout;  
113 - }  
114 -  
115 - public void setSessionType(SessionType sessionType) {  
116 - this.sessionType = sessionType;  
117 - }  
118 -  
119 - @Override  
120 - public SessionType getSessionType() {  
121 - return sessionType;  
122 - }  
123 -  
124 - public int nextSeqNumber() {  
125 - return seqNumber.getAndIncrement();  
126 - }  
127 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.transport.coap.session;  
17 -  
18 -public final class CoapSessionId implements SessionId {  
19 -  
20 - private final String clientAddress;  
21 - private final int clientPort;  
22 - private final String token;  
23 -  
24 - public CoapSessionId(String host, int port, String token) {  
25 - super();  
26 - this.clientAddress = host;  
27 - this.clientPort = port;  
28 - this.token = token;  
29 - }  
30 -  
31 - @Override  
32 - public int hashCode() {  
33 - final int prime = 31;  
34 - int result = 1;  
35 - result = prime * result + ((clientAddress == null) ? 0 : clientAddress.hashCode());  
36 - result = prime * result + clientPort;  
37 - result = prime * result + ((token == null) ? 0 : token.hashCode());  
38 - return result;  
39 - }  
40 -  
41 - @Override  
42 - public boolean equals(Object obj) {  
43 - if (this == obj)  
44 - return true;  
45 - if (obj == null)  
46 - return false;  
47 - if (getClass() != obj.getClass())  
48 - return false;  
49 - CoapSessionId other = (CoapSessionId) obj;  
50 - if (clientAddress == null) {  
51 - if (other.clientAddress != null)  
52 - return false;  
53 - } else if (!clientAddress.equals(other.clientAddress))  
54 - return false;  
55 - if (clientPort != other.clientPort)  
56 - return false;  
57 - if (token == null) {  
58 - if (other.token != null)  
59 - return false;  
60 - } else if (!token.equals(other.token))  
61 - return false;  
62 - return true;  
63 - }  
64 -  
65 - @Override  
66 - public String toString() {  
67 - return "CoapSessionId [clientAddress=" + clientAddress + ", clientPort=" + clientPort + ", token=" + token + "]";  
68 - }  
69 -  
70 - @Override  
71 - public String toUidStr() {  
72 - return clientAddress + ":" + clientPort + ":" + token;  
73 - }  
74 -  
75 -}  
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!--
  3 +
  4 + Copyright © 2016-2018 The Thingsboard Authors
  5 +
  6 + Licensed under the Apache License, Version 2.0 (the "License");
  7 + you may not use this file except in compliance with the License.
  8 + You may obtain a copy of the License at
  9 +
  10 + http://www.apache.org/licenses/LICENSE-2.0
  11 +
  12 + Unless required by applicable law or agreed to in writing, software
  13 + distributed under the License is distributed on an "AS IS" BASIS,
  14 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 + See the License for the specific language governing permissions and
  16 + limitations under the License.
  17 +
  18 +-->
  19 +<!DOCTYPE configuration>
  20 +<configuration scan="true" scanPeriod="10 seconds">
  21 +
  22 + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  23 + <encoder>
  24 + <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
  25 + </encoder>
  26 + </appender>
  27 +
  28 + <logger name="org.thingsboard.server" level="TRACE" />
  29 +
  30 + <root level="INFO">
  31 + <appender-ref ref="STDOUT"/>
  32 + </root>
  33 +
  34 +</configuration>
  1 +#
  2 +# Copyright © 2016-2018 The Thingsboard Authors
  3 +#
  4 +# Licensed under the Apache License, Version 2.0 (the "License");
  5 +# you may not use this file except in compliance with the License.
  6 +# You may obtain a copy of the License at
  7 +#
  8 +# http://www.apache.org/licenses/LICENSE-2.0
  9 +#
  10 +# Unless required by applicable law or agreed to in writing, software
  11 +# distributed under the License is distributed on an "AS IS" BASIS,
  12 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +# See the License for the specific language governing permissions and
  14 +# limitations under the License.
  15 +#
  16 +
  17 +spring.main.web-environment: false
  18 +spring.main.web-application-type: none
  19 +
  20 +# MQTT server parameters
  21 +transport:
  22 + coap:
  23 + bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
  24 + bind_port: "${COAP_BIND_PORT:5683}"
  25 + timeout: "${COAP_TIMEOUT:10000}"
  26 +
  27 +#Quota parameters
  28 +quota:
  29 + host:
  30 + # Max allowed number of API requests in interval for single host
  31 + limit: "${QUOTA_HOST_LIMIT:10000}"
  32 + # Interval duration
  33 + intervalMs: "${QUOTA_HOST_INTERVAL_MS:60000}"
  34 + # Maximum silence duration for host after which Host removed from QuotaService. Must be bigger than intervalMs
  35 + ttlMs: "${QUOTA_HOST_TTL_MS:60000}"
  36 + # Interval for scheduled task that cleans expired records. TTL is used for expiring
  37 + cleanPeriodMs: "${QUOTA_HOST_CLEAN_PERIOD_MS:300000}"
  38 + # Enable Host API Limits
  39 + enabled: "${QUOTA_HOST_ENABLED:true}"
  40 + # Array of whitelist hosts
  41 + whitelist: "${QUOTA_HOST_WHITELIST:localhost,127.0.0.1}"
  42 + # Array of blacklist hosts
  43 + blacklist: "${QUOTA_HOST_BLACKLIST:}"
  44 + log:
  45 + topSize: 10
  46 + intervalMin: 2
  47 +
  48 +kafka:
  49 + enabled: true
  50 + bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
  51 + acks: "${TB_KAFKA_ACKS:all}"
  52 + retries: "${TB_KAFKA_RETRIES:1}"
  53 + batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
  54 + linger.ms: "${TB_KAFKA_LINGER_MS:1}"
  55 + buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
  56 + transport_api:
  57 + requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}"
  58 + responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}"
  59 + max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
  60 + max_requests_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
  61 + response_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
  62 + response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
  63 + rule_engine:
  64 + topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
  65 + notifications:
  66 + topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
  67 + poll_interval: "${TB_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}"
  68 + auto_commit_interval: "${TB_TRANSPORT_NOTIFICATIONS_AUTO_COMMIT_INTERVAL_MS:100}"
  1 +#!/bin/sh
  2 +
  3 +chown -R ${pkg.name}: ${pkg.logFolder}
  4 +chown -R ${pkg.name}: ${pkg.installFolder}
  5 +update-rc.d ${pkg.name} defaults
  6 +
  1 +#!/bin/sh
  2 +
  3 +update-rc.d -f ${pkg.name} remove
  1 +#!/bin/sh
  2 +
  3 +if ! getent group ${pkg.name} >/dev/null; then
  4 + addgroup --system ${pkg.name}
  5 +fi
  6 +
  7 +if ! getent passwd ${pkg.name} >/dev/null; then
  8 + adduser --quiet \
  9 + --system \
  10 + --ingroup ${pkg.name} \
  11 + --quiet \
  12 + --disabled-login \
  13 + --disabled-password \
  14 + --home ${pkg.installFolder} \
  15 + --no-create-home \
  16 + -gecos "Thingsboard application" \
  17 + ${pkg.name}
  18 +fi
  1 +#!/bin/sh
  2 +
  3 +if [ -e /var/run/${pkg.name}/${pkg.name}.pid ]; then
  4 + service ${pkg.name} stop
  5 +fi
  1 +#!/bin/sh
  2 +
  3 +chown -R ${pkg.name}: ${pkg.logFolder}
  4 +chown -R ${pkg.name}: ${pkg.installFolder}
  5 +
  6 +if [ $1 -eq 1 ] ; then
  7 + # Initial installation
  8 + systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || :
  9 +fi
  1 +#!/bin/sh
  2 +
  3 +if [ $1 -ge 1 ] ; then
  4 + # Package upgrade, not uninstall
  5 + systemctl try-restart ${pkg.name}.service >/dev/null 2>&1 || :
  6 +fi
  1 +#!/bin/sh
  2 +
  3 +getent group ${pkg.name} >/dev/null || groupadd -r ${pkg.name}
  4 +getent passwd ${pkg.name} >/dev/null || \
  5 +useradd -d ${pkg.installFolder} -g ${pkg.name} -M -r ${pkg.name} -s /sbin/nologin \
  6 +-c "Thingsboard application"
  1 +#!/bin/sh
  2 +
  3 +if [ $1 -eq 0 ] ; then
  4 + # Package removal, not upgrade
  5 + systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
  6 +fi
  1 +[Unit]
  2 +Description=${pkg.name}
  3 +After=syslog.target
  4 +
  5 +[Service]
  6 +User=${pkg.name}
  7 +ExecStart=${pkg.installFolder}/bin/${pkg.name}.jar
  8 +SuccessExitStatus=143
  9 +
  10 +[Install]
  11 +WantedBy=multi-user.target
  1 +@ECHO OFF
  2 +
  3 +setlocal ENABLEEXTENSIONS
  4 +
  5 +@ECHO Detecting Java version installed.
  6 +:CHECK_JAVA_64
  7 +@ECHO Detecting if it is 64 bit machine
  8 +set KEY_NAME="HKEY_LOCAL_MACHINE\Software\Wow6432Node\JavaSoft\Java Runtime Environment"
  9 +set VALUE_NAME=CurrentVersion
  10 +
  11 +FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
  12 + set ValueName=%%A
  13 + set ValueType=%%B
  14 + set ValueValue=%%C
  15 +)
  16 +@ECHO CurrentVersion %ValueValue%
  17 +
  18 +SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
  19 +SET VALUE_NAME=JavaHome
  20 +
  21 +if defined ValueName (
  22 + FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
  23 + set ValueName2=%%A
  24 + set ValueType2=%%B
  25 + set JRE_PATH2=%%C
  26 +
  27 + if defined ValueName2 (
  28 + set ValueName = %ValueName2%
  29 + set ValueType = %ValueType2%
  30 + set ValueValue = %JRE_PATH2%
  31 + )
  32 + )
  33 +)
  34 +
  35 +IF NOT "%JRE_PATH2%" == "" GOTO JAVA_INSTALLED
  36 +
  37 +:CHECK_JAVA_32
  38 +@ECHO Detecting if it is 32 bit machine
  39 +set KEY_NAME="HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment"
  40 +set VALUE_NAME=CurrentVersion
  41 +
  42 +FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
  43 + set ValueName=%%A
  44 + set ValueType=%%B
  45 + set ValueValue=%%C
  46 +)
  47 +@ECHO CurrentVersion %ValueValue%
  48 +
  49 +SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
  50 +SET VALUE_NAME=JavaHome
  51 +
  52 +if defined ValueName (
  53 + FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
  54 + set ValueName2=%%A
  55 + set ValueType2=%%B
  56 + set JRE_PATH2=%%C
  57 +
  58 + if defined ValueName2 (
  59 + set ValueName = %ValueName2%
  60 + set ValueType = %ValueType2%
  61 + set ValueValue = %JRE_PATH2%
  62 + )
  63 + )
  64 +)
  65 +
  66 +IF "%JRE_PATH2%" == "" GOTO JAVA_NOT_INSTALLED
  67 +
  68 +:JAVA_INSTALLED
  69 +
  70 +@ECHO Java 1.8 found!
  71 +@ECHO Installing ${pkg.name} ...
  72 +
  73 +%BASE%${pkg.name}.exe install
  74 +
  75 +@ECHO ${pkg.name} installed successfully!
  76 +
  77 +GOTO END
  78 +
  79 +:JAVA_NOT_INSTALLED
  80 +@ECHO Java 1.8 or above is not installed
  81 +@ECHO Please go to https://java.com/ and install Java. Then retry installation.
  82 +PAUSE
  83 +GOTO END
  84 +
  85 +:END
  86 +
  87 +
  1 +<service>
  2 + <id>${pkg.name}</id>
  3 + <name>${project.name}</name>
  4 + <description>${project.description}</description>
  5 + <workingdirectory>%BASE%\conf</workingdirectory>
  6 + <logpath>${pkg.winWrapperLogFolder}</logpath>
  7 + <logmode>rotate</logmode>
  8 + <env name="LOADER_PATH" value="%BASE%\conf" />
  9 + <executable>java</executable>
  10 + <startargument>-Xloggc:%BASE%\logs\gc.log</startargument>
  11 + <startargument>-XX:+HeapDumpOnOutOfMemoryError</startargument>
  12 + <startargument>-XX:+PrintGCDetails</startargument>
  13 + <startargument>-XX:+PrintGCDateStamps</startargument>
  14 + <startargument>-XX:+PrintHeapAtGC</startargument>
  15 + <startargument>-XX:+PrintTenuringDistribution</startargument>
  16 + <startargument>-XX:+PrintGCApplicationStoppedTime</startargument>
  17 + <startargument>-XX:+UseGCLogFileRotation</startargument>
  18 + <startargument>-XX:NumberOfGCLogFiles=10</startargument>
  19 + <startargument>-XX:GCLogFileSize=10M</startargument>
  20 + <startargument>-XX:-UseBiasedLocking</startargument>
  21 + <startargument>-XX:+UseTLAB</startargument>
  22 + <startargument>-XX:+ResizeTLAB</startargument>
  23 + <startargument>-XX:+PerfDisableSharedMem</startargument>
  24 + <startargument>-XX:+UseCondCardMark</startargument>
  25 + <startargument>-XX:CMSWaitDuration=10000</startargument>
  26 + <startargument>-XX:+UseParNewGC</startargument>
  27 + <startargument>-XX:+UseConcMarkSweepGC</startargument>
  28 + <startargument>-XX:+CMSParallelRemarkEnabled</startargument>
  29 + <startargument>-XX:+CMSParallelInitialMarkEnabled</startargument>
  30 + <startargument>-XX:+CMSEdenChunksRecordAlways</startargument>
  31 + <startargument>-XX:CMSInitiatingOccupancyFraction=75</startargument>
  32 + <startargument>-XX:+UseCMSInitiatingOccupancyOnly</startargument>
  33 + <startargument>-jar</startargument>
  34 + <startargument>%BASE%\lib\${pkg.name}.jar</startargument>
  35 +
  36 +</service>
  1 +@ECHO OFF
  2 +
  3 +@ECHO Stopping ${pkg.name} ...
  4 +net stop ${pkg.name}
  5 +
  6 +@ECHO Uninstalling ${pkg.name} ...
  7 +%~dp0${pkg.name}.exe uninstall
  8 +
  9 +@ECHO DONE.
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.transport.coap;  
17 -  
18 -import lombok.extern.slf4j.Slf4j;  
19 -import org.eclipse.californium.core.CoapClient;  
20 -import org.eclipse.californium.core.CoapResponse;  
21 -import org.eclipse.californium.core.coap.CoAP.ResponseCode;  
22 -import org.eclipse.californium.core.coap.MediaTypeRegistry;  
23 -import org.junit.Assert;  
24 -import org.junit.Before;  
25 -import org.junit.Test;  
26 -import org.junit.runner.RunWith;  
27 -import org.springframework.beans.factory.annotation.Autowired;  
28 -import org.springframework.context.annotation.Bean;  
29 -import org.springframework.context.annotation.Configuration;  
30 -import org.springframework.test.annotation.DirtiesContext;  
31 -import org.springframework.test.annotation.DirtiesContext.ClassMode;  
32 -import org.springframework.test.context.junit4.SpringRunner;  
33 -import org.thingsboard.server.common.data.Device;  
34 -import org.thingsboard.server.common.data.id.CustomerId;  
35 -import org.thingsboard.server.common.data.id.DeviceId;  
36 -import org.thingsboard.server.common.data.id.TenantId;  
37 -import org.thingsboard.server.common.data.security.DeviceCredentialsFilter;  
38 -import org.thingsboard.server.common.data.security.DeviceCredentialsType;  
39 -import org.thingsboard.server.common.data.security.DeviceTokenCredentials;  
40 -import org.thingsboard.server.common.msg.session.*;  
41 -import org.thingsboard.server.common.transport.SessionMsgProcessor;  
42 -import org.thingsboard.server.common.transport.auth.DeviceAuthResult;  
43 -import org.thingsboard.server.common.transport.auth.DeviceAuthService;  
44 -import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService;  
45 -  
46 -import java.util.Optional;  
47 -import java.util.UUID;  
48 -  
49 -@RunWith(SpringRunner.class)  
50 -@DirtiesContext(classMode = ClassMode.BEFORE_CLASS)  
51 -@Slf4j  
52 -public class CoapServerTest {  
53 -  
54 - private static final int TEST_PORT = 5555;  
55 - private static final String TELEMETRY_POST_MESSAGE = "[{\"key1\":\"value1\"}]";  
56 - private static final String TEST_ATTRIBUTES_RESPONSE = "{\"key1\":\"value1\",\"key2\":42}";  
57 - private static final String DEVICE1_TOKEN = "Device1Token";  
58 -  
59 - @Configuration  
60 - public static class EchoCoapServerITConfiguration extends CoapServerTestConfiguration {  
61 -  
62 - @Bean  
63 - public static DeviceAuthService authService() {  
64 - return new DeviceAuthService() {  
65 -  
66 - private final DeviceId devId = new DeviceId(UUID.randomUUID());  
67 -  
68 - @Override  
69 - public DeviceAuthResult process(DeviceCredentialsFilter credentials) {  
70 - if (credentials != null && credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) {  
71 - DeviceTokenCredentials tokenCredentials = (DeviceTokenCredentials) credentials;  
72 - if (tokenCredentials.getCredentialsId().equals(DEVICE1_TOKEN)) {  
73 - return DeviceAuthResult.of(devId);  
74 - }  
75 - }  
76 - return DeviceAuthResult.of("Credentials are invalid!");  
77 - }  
78 -  
79 - @Override  
80 - public Optional<Device> findDeviceById(DeviceId deviceId) {  
81 - if (deviceId.equals(devId)) {  
82 - Device dev = new Device();  
83 - dev.setId(devId);  
84 - dev.setTenantId(new TenantId(UUID.randomUUID()));  
85 - dev.setCustomerId(new CustomerId(UUID.randomUUID()));  
86 - return Optional.of(dev);  
87 - } else {  
88 - return Optional.empty();  
89 - }  
90 - }  
91 - };  
92 - }  
93 -  
94 - @Bean  
95 - public static SessionMsgProcessor sessionMsgProcessor() {  
96 - return new SessionMsgProcessor() {  
97 -  
98 -// @Override  
99 -// public void process(SessionAwareMsg toActorMsg) {  
100 -// if (toActorMsg instanceof TransportToDeviceSessionActorMsg) {  
101 -// AdaptorToSessionActorMsg sessionMsg = ((TransportToDeviceSessionActorMsg) toActorMsg).getSessionMsg();  
102 -// try {  
103 -// FromDeviceMsg deviceMsg = sessionMsg.getMsg();  
104 -// ToDeviceMsg toDeviceMsg = null;  
105 -// if (deviceMsg.getMsgType() == SessionMsgType.POST_TELEMETRY_REQUEST) {  
106 -// toDeviceMsg = BasicStatusCodeResponse.onSuccess(deviceMsg.getMsgType(), BasicRequest.DEFAULT_REQUEST_ID);  
107 -// } else if (deviceMsg.getMsgType() == SessionMsgType.GET_ATTRIBUTES_REQUEST) {  
108 -// List<AttributeKvEntry> data = new ArrayList<>();  
109 -// data.add(new BaseAttributeKvEntry(new StringDataEntry("key1", "value1"), System.currentTimeMillis()));  
110 -// data.add(new BaseAttributeKvEntry(new LongDataEntry("key2", 42L), System.currentTimeMillis()));  
111 -// BasicAttributeKVMsg kv = BasicAttributeKVMsg.fromClient(data);  
112 -// toDeviceMsg = BasicGetAttributesResponse.onSuccess(deviceMsg.getMsgType(), BasicRequest.DEFAULT_REQUEST_ID, kv);  
113 -// }  
114 -// if (toDeviceMsg != null) {  
115 -// sessionMsg.getSessionContext().onMsg(new BasicSessionActorToAdaptorMsg(sessionMsg.getSessionContext(), toDeviceMsg));  
116 -// }  
117 -// } catch (Exception e) {  
118 -// e.printStackTrace();  
119 -// }  
120 -// }  
121 -// }  
122 -  
123 - @Override  
124 - public void onDeviceAdded(Device device) {  
125 -  
126 - }  
127 -  
128 -  
129 - };  
130 - }  
131 -  
132 - @Bean  
133 - public static HostRequestsQuotaService quotaService() {  
134 - return new HostRequestsQuotaService(null, null, null, null, false);  
135 - }  
136 - }  
137 -  
138 - @Autowired  
139 - private CoapTransportService service;  
140 -  
141 - @Before  
142 - public void beforeTest() {  
143 - log.info("Service info: {}", service.toString());  
144 - }  
145 -  
146 - @Test  
147 - public void testBadJsonTelemetryPostRequest() {  
148 - CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.TELEMETRY.name().toLowerCase());  
149 - CoapResponse response = client.setTimeout(6000).post("test", MediaTypeRegistry.APPLICATION_JSON);  
150 - Assert.assertEquals(ResponseCode.BAD_REQUEST, response.getCode());  
151 - log.info("Response: {}, {}", response.getCode(), response.getResponseText());  
152 - }  
153 -  
154 - @Test  
155 - public void testNoCredentialsPostRequest() {  
156 - CoapClient client = new CoapClient(getBaseTestUrl());  
157 - CoapResponse response = client.setTimeout(6000).post(TELEMETRY_POST_MESSAGE, MediaTypeRegistry.APPLICATION_JSON);  
158 - Assert.assertEquals(ResponseCode.BAD_REQUEST, response.getCode());  
159 - log.info("Response: {}, {}", response.getCode(), response.getResponseText());  
160 - }  
161 -  
162 - @Test  
163 - public void testValidJsonTelemetryPostRequest() {  
164 - CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.TELEMETRY.name().toLowerCase());  
165 - CoapResponse response = client.setTimeout(6000).post(TELEMETRY_POST_MESSAGE, MediaTypeRegistry.APPLICATION_JSON);  
166 - Assert.assertEquals(ResponseCode.CREATED, response.getCode());  
167 - log.info("Response: {}, {}", response.getCode(), response.getResponseText());  
168 - }  
169 -  
170 - @Test  
171 - public void testNoCredentialsAttributesGetRequest() {  
172 - CoapClient client = new CoapClient("coap://localhost:5555/api/v1?keys=key1,key2");  
173 - CoapResponse response = client.setTimeout(6000).get();  
174 - Assert.assertEquals(ResponseCode.BAD_REQUEST, response.getCode());  
175 - }  
176 -  
177 - @Test  
178 - public void testNoKeysAttributesGetRequest() {  
179 - CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.ATTRIBUTES.name().toLowerCase() + "?data=key1,key2");  
180 - CoapResponse response = client.setTimeout(6000).get();  
181 - Assert.assertEquals(ResponseCode.CONTENT, response.getCode());  
182 - }  
183 -  
184 - @Test  
185 - public void testValidAttributesGetRequest() {  
186 - CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.ATTRIBUTES.name().toLowerCase() + "?clientKeys=key1,key2");  
187 - CoapResponse response = client.setTimeout(6000).get();  
188 - Assert.assertEquals(ResponseCode.CONTENT, response.getCode());  
189 - Assert.assertEquals(TEST_ATTRIBUTES_RESPONSE, response.getResponseText());  
190 - log.info("Response: {}, {}", response.getCode(), response.getResponseText());  
191 - }  
192 -  
193 - private String getBaseTestUrl() {  
194 - return "coap://localhost:" + TEST_PORT + "/api/v1/";  
195 - }  
196 -  
197 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.transport.coap;  
17 -  
18 -import org.springframework.context.annotation.Bean;  
19 -import org.springframework.context.annotation.ComponentScan;  
20 -import org.springframework.context.annotation.Configuration;  
21 -import org.springframework.context.annotation.PropertySource;  
22 -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;  
23 -import org.springframework.test.context.TestPropertySource;  
24 -  
25 -@Configuration  
26 -@ComponentScan({ "org.thingsboard.server.transport.coap" })  
27 -@PropertySource("classpath:coap-transport-test.properties")  
28 -public class CoapServerTestConfiguration {  
29 -  
30 - @Bean  
31 - public static PropertySourcesPlaceholderConfigurer propertyConfig() {  
32 - return new PropertySourcesPlaceholderConfigurer();  
33 - }  
34 -  
35 -}  
1 -coap.bind_address=0.0.0.0  
2 -coap.bind_port=5555  
3 -coap.adaptor=JsonCoapAdaptor  
4 -coap.timeout=10000  
@@ -145,7 +145,7 @@ @@ -145,7 +145,7 @@
145 <resource> 145 <resource>
146 <directory>src/main/conf</directory> 146 <directory>src/main/conf</directory>
147 <excludes> 147 <excludes>
148 - <exclude>tb-mqtt-transport.conf</exclude> 148 + <exclude>tb-http-transport.conf</exclude>
149 </excludes> 149 </excludes>
150 <filtering>true</filtering> 150 <filtering>true</filtering>
151 </resource> 151 </resource>
@@ -229,7 +229,7 @@ @@ -229,7 +229,7 @@
229 </excludes> 229 </excludes>
230 <archive> 230 <archive>
231 <manifestEntries> 231 <manifestEntries>
232 - <Implementation-Title>ThingsBoard MQTT Transport Service</Implementation-Title> 232 + <Implementation-Title>ThingsBoard HTTP Transport Service</Implementation-Title>
233 <Implementation-Version>${project.version}</Implementation-Version> 233 <Implementation-Version>${project.version}</Implementation-Version>
234 </manifestEntries> 234 </manifestEntries>
235 </archive> 235 </archive>
@@ -239,7 +239,7 @@ @@ -239,7 +239,7 @@
239 <groupId>org.springframework.boot</groupId> 239 <groupId>org.springframework.boot</groupId>
240 <artifactId>spring-boot-maven-plugin</artifactId> 240 <artifactId>spring-boot-maven-plugin</artifactId>
241 <configuration> 241 <configuration>
242 - <mainClass>org.thingsboard.server.mqtt.ThingsboardHttpTransportApplication</mainClass> 242 + <mainClass>org.thingsboard.server.http.ThingsboardHttpTransportApplication</mainClass>
243 <classifier>boot</classifier> 243 <classifier>boot</classifier>
244 <layout>ZIP</layout> 244 <layout>ZIP</layout>
245 <executable>true</executable> 245 <executable>true</executable>
@@ -49,6 +49,10 @@ @@ -49,6 +49,10 @@
49 <artifactId>queue</artifactId> 49 <artifactId>queue</artifactId>
50 </dependency> 50 </dependency>
51 <dependency> 51 <dependency>
  52 + <groupId>org.springframework.boot</groupId>
  53 + <artifactId>spring-boot-starter-web</artifactId>
  54 + </dependency>
  55 + <dependency>
52 <groupId>com.sun.winsw</groupId> 56 <groupId>com.sun.winsw</groupId>
53 <artifactId>winsw</artifactId> 57 <artifactId>winsw</artifactId>
54 <classifier>bin</classifier> 58 <classifier>bin</classifier>
@@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
14 # limitations under the License. 14 # limitations under the License.
15 # 15 #
16 16
  17 +spring.main.web-environment: false
17 spring.main.web-application-type: none 18 spring.main.web-application-type: none
18 19
19 # MQTT server parameters 20 # MQTT server parameters
@@ -36,6 +36,7 @@ @@ -36,6 +36,7 @@
36 <modules> 36 <modules>
37 <module>http</module> 37 <module>http</module>
38 <module>mqtt</module> 38 <module>mqtt</module>
  39 + <module>coap</module>
39 </modules> 40 </modules>
40 41
41 </project> 42 </project>