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 60 <groupId>org.thingsboard.common.transport</groupId>
61 61 <artifactId>transport-api</artifactId>
62 62 </dependency>
63   - <!--<dependency>-->
64   - <!--<groupId>org.thingsboard.transport</groupId>-->
65   - <!--<artifactId>coap</artifactId>-->
66   - <!--</dependency>-->
67 63 <dependency>
68 64 <groupId>org.thingsboard.common.transport</groupId>
69 65 <artifactId>mqtt</artifactId>
... ... @@ -73,6 +69,10 @@
73 69 <artifactId>http</artifactId>
74 70 </dependency>
75 71 <dependency>
  72 + <groupId>org.thingsboard.common.transport</groupId>
  73 + <artifactId>coap</artifactId>
  74 + </dependency>
  75 + <dependency>
76 76 <groupId>org.thingsboard</groupId>
77 77 <artifactId>dao</artifactId>
78 78 </dependency>
... ...
... ... @@ -455,7 +455,6 @@ transport:
455 455 enabled: "${MQTT_ENABLED:true}"
456 456 bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
457 457 bind_port: "${MQTT_BIND_PORT:1883}"
458   - adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}"
459 458 timeout: "${MQTT_TIMEOUT:10000}"
460 459 netty:
461 460 leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}"
... ... @@ -482,5 +481,4 @@ transport:
482 481 enabled: "${COAP_ENABLED:true}"
483 482 bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
484 483 bind_port: "${COAP_BIND_PORT:5683}"
485   - adaptor: "${COAP_ADAPTOR_NAME:JsonCoapAdaptor}"
486 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 21 import org.eclipse.californium.core.network.CoapEndpoint;
22 22 import org.springframework.beans.factory.annotation.Autowired;
23 23 import org.springframework.beans.factory.annotation.Value;
  24 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
24 25 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
25 26 import org.springframework.context.ApplicationContext;
26 27 import org.springframework.stereotype.Service;
... ... @@ -37,49 +38,26 @@ import java.net.InetSocketAddress;
37 38 import java.net.UnknownHostException;
38 39
39 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 42 @Slf4j
42 43 public class CoapTransportService {
43 44
44 45 private static final String V1 = "v1";
45 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 53 @PostConstruct
74 54 public void init() throws UnknownHostException {
75 55 log.info("Starting CoAP transport...");
76   - log.info("Lookup CoAP transport adaptor {}", adaptorName);
77   - this.adaptor = (CoapTransportAdaptor) appContext.getBean(adaptorName);
78 56 log.info("Starting CoAP transport server");
79 57 this.server = new CoapServer();
80 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 61 server.addEndpoint(new CoapEndpoint(sockAddr));
84 62 server.start();
85 63 log.info("CoAP transport started!");
... ... @@ -87,7 +65,7 @@ public class CoapTransportService {
87 65
88 66 private void createResources() {
89 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 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 17
18 18 import org.eclipse.californium.core.coap.Request;
19 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 43 private String host;
44 44 @Value("${transport.mqtt.bind_port}")
45 45 private Integer port;
46   - @Value("${transport.mqtt.adaptor}")
47   - private String adaptorName;
48 46
49 47 @Value("${transport.mqtt.netty.leak_detector_level}")
50 48 private String leakDetectorLevel;
... ...
... ... @@ -38,7 +38,7 @@
38 38 <module>transport-api</module>
39 39 <module>mqtt</module>
40 40 <module>http</module>
41   - <!--module>coap</module-->
  41 + <module>coap</module>
42 42 </modules>
43 43
44 44 </project>
... ...
... ... @@ -370,7 +370,7 @@
370 370 <version>${project.version}</version>
371 371 </dependency>
372 372 <dependency>
373   - <groupId>org.thingsboard.transport</groupId>
  373 + <groupId>org.thingsboard.common.transport</groupId>
374 374 <artifactId>coap</artifactId>
375 375 <version>${project.version}</version>
376 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 27 <artifactId>coap</artifactId>
28 28 <packaging>jar</packaging>
29 29
30   - <name>Thingsboard COAP Transport</name>
  30 + <name>Thingsboard CoAP Transport Service</name>
31 31 <url>https://thingsboard.io</url>
32 32
33 33 <properties>
34 34 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
35 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 40 </properties>
37 41
38 42 <dependencies>
39 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 46 </dependency>
51 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 50 </dependency>
59 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 54 </dependency>
63 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 61 </dependency>
67 62 <dependency>
68 63 <groupId>org.springframework.boot</groupId>
... ... @@ -81,4 +76,266 @@
81 76 </dependency>
82 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 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}
\ No newline at end of file
... ...
  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>
\ No newline at end of file
... ...
  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.
\ No newline at end of file
... ...
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
\ No newline at end of file
... ... @@ -145,7 +145,7 @@
145 145 <resource>
146 146 <directory>src/main/conf</directory>
147 147 <excludes>
148   - <exclude>tb-mqtt-transport.conf</exclude>
  148 + <exclude>tb-http-transport.conf</exclude>
149 149 </excludes>
150 150 <filtering>true</filtering>
151 151 </resource>
... ... @@ -229,7 +229,7 @@
229 229 </excludes>
230 230 <archive>
231 231 <manifestEntries>
232   - <Implementation-Title>ThingsBoard MQTT Transport Service</Implementation-Title>
  232 + <Implementation-Title>ThingsBoard HTTP Transport Service</Implementation-Title>
233 233 <Implementation-Version>${project.version}</Implementation-Version>
234 234 </manifestEntries>
235 235 </archive>
... ... @@ -239,7 +239,7 @@
239 239 <groupId>org.springframework.boot</groupId>
240 240 <artifactId>spring-boot-maven-plugin</artifactId>
241 241 <configuration>
242   - <mainClass>org.thingsboard.server.mqtt.ThingsboardHttpTransportApplication</mainClass>
  242 + <mainClass>org.thingsboard.server.http.ThingsboardHttpTransportApplication</mainClass>
243 243 <classifier>boot</classifier>
244 244 <layout>ZIP</layout>
245 245 <executable>true</executable>
... ...
... ... @@ -49,6 +49,10 @@
49 49 <artifactId>queue</artifactId>
50 50 </dependency>
51 51 <dependency>
  52 + <groupId>org.springframework.boot</groupId>
  53 + <artifactId>spring-boot-starter-web</artifactId>
  54 + </dependency>
  55 + <dependency>
52 56 <groupId>com.sun.winsw</groupId>
53 57 <artifactId>winsw</artifactId>
54 58 <classifier>bin</classifier>
... ...
... ... @@ -14,6 +14,7 @@
14 14 # limitations under the License.
15 15 #
16 16
  17 +spring.main.web-environment: false
17 18 spring.main.web-application-type: none
18 19
19 20 # MQTT server parameters
... ...
... ... @@ -36,6 +36,7 @@
36 36 <modules>
37 37 <module>http</module>
38 38 <module>mqtt</module>
  39 + <module>coap</module>
39 40 </modules>
40 41
41 42 </project>
... ...