thingsboard-memoizeit
Changes
application/pom.xml 8(+4 -4)
common/transport/coap/pom.xml 88(+88 -0)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java 28(+25 -3)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java 155(+155 -0)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java 0(+0 -0)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java 51(+51 -0)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java 445(+445 -0)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java 38(+8 -30)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java 2(+0 -2)
common/transport/pom.xml 2(+1 -1)
pom.xml 2(+1 -1)
transport/coap/build.gradle 140(+140 -0)
transport/coap/pom.xml 299(+278 -21)
transport/coap/src/main/assembly/windows.xml 71(+71 -0)
transport/coap/src/main/conf/logback.xml 43(+43 -0)
transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java 48(+48 -0)
transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java 260(+0 -260)
transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java 242(+0 -242)
transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapExchangeObserverProxy.java 38(+0 -38)
transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java 127(+0 -127)
transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java 75(+0 -75)
transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTestConfiguration.java 35(+0 -35)
transport/http/pom.xml 6(+3 -3)
transport/mqtt/pom.xml 4(+4 -0)
transport/pom.xml 1(+1 -0)
Details
application/pom.xml 8(+4 -4)
diff --git a/application/pom.xml b/application/pom.xml
index 309f7de..d99f607 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -60,10 +60,6 @@
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>transport-api</artifactId>
</dependency>
- <!--<dependency>-->
- <!--<groupId>org.thingsboard.transport</groupId>-->
- <!--<artifactId>coap</artifactId>-->
- <!--</dependency>-->
<dependency>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>mqtt</artifactId>
@@ -73,6 +69,10 @@
<artifactId>http</artifactId>
</dependency>
<dependency>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>coap</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.thingsboard</groupId>
<artifactId>dao</artifactId>
</dependency>
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 6238a3e..90d1500 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -455,7 +455,6 @@ transport:
enabled: "${MQTT_ENABLED:true}"
bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
bind_port: "${MQTT_BIND_PORT:1883}"
- adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}"
timeout: "${MQTT_TIMEOUT:10000}"
netty:
leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}"
@@ -482,5 +481,4 @@ transport:
enabled: "${COAP_ENABLED:true}"
bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
bind_port: "${COAP_BIND_PORT:5683}"
- adaptor: "${COAP_ADAPTOR_NAME:JsonCoapAdaptor}"
timeout: "${COAP_TIMEOUT:10000}"
common/transport/coap/pom.xml 88(+88 -0)
diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml
new file mode 100644
index 0000000..d68a6db
--- /dev/null
+++ b/common/transport/coap/pom.xml
@@ -0,0 +1,88 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.thingsboard.common</groupId>
+ <version>2.2.0-SNAPSHOT</version>
+ <artifactId>transport</artifactId>
+ </parent>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>coap</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Thingsboard CoAP Transport Common</name>
+ <url>https://thingsboard.io</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <main.dir>${basedir}/../../..</main.dir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>transport-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.californium</groupId>
+ <artifactId>californium-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>log4j-over-slf4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java
new file mode 100644
index 0000000..f5bfb49
--- /dev/null
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java
@@ -0,0 +1,155 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.coap.adaptors;
+
+import java.util.*;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.californium.core.coap.CoAP;
+import org.eclipse.californium.core.coap.Request;
+import org.eclipse.californium.core.coap.Response;
+import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
+import org.thingsboard.server.common.msg.session.SessionContext;
+import org.thingsboard.server.common.transport.adaptor.AdaptorException;
+import org.thingsboard.server.common.transport.adaptor.JsonConverter;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.transport.coap.CoapTransportResource;
+
+@Component("JsonCoapAdaptor")
+@Slf4j
+public class JsonCoapAdaptor implements CoapTransportAdaptor {
+
+ @Override
+ public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound) throws AdaptorException {
+ String payload = validatePayload(sessionId, inbound);
+ try {
+ return JsonConverter.convertToTelemetryProto(new JsonParser().parse(payload));
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ @Override
+ public TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound) throws AdaptorException {
+ String payload = validatePayload(sessionId, inbound);
+ try {
+ return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload));
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ @Override
+ public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException {
+ List<String> queryElements = inbound.getOptions().getUriQuery();
+ TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
+ if (queryElements != null && queryElements.size() > 0) {
+ Set<String> clientKeys = toKeys(queryElements, "clientKeys");
+ Set<String> sharedKeys = toKeys(queryElements, "sharedKeys");
+ if (clientKeys != null) {
+ result.addAllClientAttributeNames(clientKeys);
+ }
+ if (sharedKeys != null) {
+ result.addAllSharedAttributeNames(sharedKeys);
+ }
+ }
+ return result.build();
+ }
+
+ @Override
+ public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException {
+ Optional<Integer> requestId = CoapTransportResource.getRequestId(inbound);
+ String payload = validatePayload(sessionId, inbound);
+ JsonObject response = new JsonParser().parse(payload).getAsJsonObject();
+ return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId.orElseThrow(() -> new AdaptorException("Request id is missing!")))
+ .setPayload(response.toString()).build();
+ }
+
+ @Override
+ public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException {
+ String payload = validatePayload(sessionId, inbound);
+ return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), 0);
+ }
+
+ @Override
+ public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg msg) throws AdaptorException {
+ return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg));
+ }
+
+ @Override
+ public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.ToDeviceRpcRequestMsg msg) throws AdaptorException {
+ return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg, true));
+ }
+
+ @Override
+ public Response convertToPublish(CoapTransportResource.CoapSessionListener coapSessionListener, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException {
+ Response response = new Response(CoAP.ResponseCode.CONTENT);
+ JsonElement result = JsonConverter.toJson(msg);
+ response.setPayload(result.toString());
+ return response;
+ }
+
+ @Override
+ public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException {
+ if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0 && msg.getDeletedAttributeKeysCount() == 0) {
+ return new Response(CoAP.ResponseCode.NOT_FOUND);
+ } else {
+ Response response = new Response(CoAP.ResponseCode.CONTENT);
+ JsonObject result = JsonConverter.toJson(msg);
+ response.setPayload(result.toString());
+ return response;
+ }
+ }
+
+ private Response getObserveNotification(int seqNumber, JsonElement json) {
+ Response response = new Response(CoAP.ResponseCode.CONTENT);
+ response.getOptions().setObserve(seqNumber);
+ response.setPayload(json.toString());
+ return response;
+ }
+
+ private String validatePayload(UUID sessionId, Request inbound) throws AdaptorException {
+ String payload = inbound.getPayloadString();
+ if (payload == null) {
+ log.warn("[{}] Payload is empty!", sessionId);
+ throw new AdaptorException(new IllegalArgumentException("Payload is empty!"));
+ }
+ return payload;
+ }
+
+ private Set<String> toKeys(List<String> queryElements, String attributeName) throws AdaptorException {
+ String keys = null;
+ for (String queryElement : queryElements) {
+ String[] queryItem = queryElement.split("=");
+ if (queryItem.length == 2 && queryItem[0].equals(attributeName)) {
+ keys = queryItem[1];
+ }
+ }
+ if (keys != null && !StringUtils.isEmpty(keys)) {
+ return new HashSet<>(Arrays.asList(keys.split(",")));
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java
new file mode 100644
index 0000000..13662cf
--- /dev/null
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.coap;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.transport.TransportContext;
+import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;
+
+/**
+ * Created by ashvayka on 18.10.18.
+ */
+@Slf4j
+@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.coap.enabled}'=='true')")
+@Component
+public class CoapTransportContext extends TransportContext {
+
+ @Getter
+ @Value("${transport.coap.bind_address}")
+ private String host;
+
+ @Getter
+ @Value("${transport.coap.bind_port}")
+ private Integer port;
+
+ @Getter
+ @Value("${transport.coap.timeout}")
+ private Long timeout;
+
+ @Getter
+ @Autowired
+ private CoapTransportAdaptor adaptor;
+
+}
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
new file mode 100644
index 0000000..0e2a794
--- /dev/null
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
@@ -0,0 +1,445 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.coap;
+
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.californium.core.CoapResource;
+import org.eclipse.californium.core.coap.CoAP.ResponseCode;
+import org.eclipse.californium.core.coap.Request;
+import org.eclipse.californium.core.coap.Response;
+import org.eclipse.californium.core.network.Exchange;
+import org.eclipse.californium.core.network.ExchangeObserver;
+import org.eclipse.californium.core.server.resources.CoapExchange;
+import org.eclipse.californium.core.server.resources.Resource;
+import org.springframework.util.ReflectionUtils;
+import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
+import org.thingsboard.server.common.msg.session.FeatureType;
+import org.thingsboard.server.common.msg.session.SessionMsgType;
+import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.common.transport.TransportContext;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.TransportServiceCallback;
+import org.thingsboard.server.common.transport.adaptor.AdaptorException;
+import org.thingsboard.server.gen.transport.TransportProtos;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+@Slf4j
+public class CoapTransportResource extends CoapResource {
+ // coap://localhost:port/api/v1/DEVICE_TOKEN/[attributes|telemetry|rpc[/requestId]]
+ private static final int ACCESS_TOKEN_POSITION = 3;
+ private static final int FEATURE_TYPE_POSITION = 4;
+ private static final int REQUEST_ID_POSITION = 5;
+
+ private final CoapTransportContext transportContext;
+ private final TransportService transportService;
+ private final Field observerField;
+ private final long timeout;
+ private final ConcurrentMap<String, TransportProtos.SessionInfoProto> tokenToSessionIdMap = new ConcurrentHashMap<>();
+
+ public CoapTransportResource(CoapTransportContext context, String name) {
+ super(name);
+ this.transportContext = context;
+ this.transportService = context.getTransportService();
+ this.timeout = context.getTimeout();
+ // This is important to turn off existing observable logic in
+ // CoapResource. We will have our own observe monitoring due to 1:1
+ // observe relationship.
+ this.setObservable(false);
+ observerField = ReflectionUtils.findField(Exchange.class, "observer");
+ observerField.setAccessible(true);
+ }
+
+ @Override
+ public void handleGET(CoapExchange exchange) {
+ if (transportContext.getQuotaService().isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) {
+ log.warn("CoAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort());
+ exchange.respond(ResponseCode.BAD_REQUEST);
+ return;
+ }
+
+ Optional<FeatureType> featureType = getFeatureType(exchange.advanced().getRequest());
+ if (!featureType.isPresent()) {
+ log.trace("Missing feature type parameter");
+ exchange.respond(ResponseCode.BAD_REQUEST);
+ } else if (featureType.get() == FeatureType.TELEMETRY) {
+ log.trace("Can't fetch/subscribe to timeseries updates");
+ exchange.respond(ResponseCode.BAD_REQUEST);
+ } else if (exchange.getRequestOptions().hasObserve()) {
+ processExchangeGetRequest(exchange, featureType.get());
+ } else if (featureType.get() == FeatureType.ATTRIBUTES) {
+ processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);
+ } else {
+ log.trace("Invalid feature type parameter");
+ exchange.respond(ResponseCode.BAD_REQUEST);
+ }
+ }
+
+ private void processExchangeGetRequest(CoapExchange exchange, FeatureType featureType) {
+ boolean unsubscribe = exchange.getRequestOptions().getObserve() == 1;
+ SessionMsgType sessionMsgType;
+ if (featureType == FeatureType.RPC) {
+ sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST : SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST;
+ } else {
+ sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST : SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST;
+ }
+ processRequest(exchange, sessionMsgType);
+ }
+
+ @Override
+ public void handlePOST(CoapExchange exchange) {
+ if (transportContext.getQuotaService().isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) {
+ log.warn("CoAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort());
+ exchange.respond(ResponseCode.BAD_REQUEST);
+ return;
+ }
+
+ Optional<FeatureType> featureType = getFeatureType(exchange.advanced().getRequest());
+ if (!featureType.isPresent()) {
+ log.trace("Missing feature type parameter");
+ exchange.respond(ResponseCode.BAD_REQUEST);
+ } else {
+ switch (featureType.get()) {
+ case ATTRIBUTES:
+ processRequest(exchange, SessionMsgType.POST_ATTRIBUTES_REQUEST);
+ break;
+ case TELEMETRY:
+ processRequest(exchange, SessionMsgType.POST_TELEMETRY_REQUEST);
+ break;
+ case RPC:
+ Optional<Integer> requestId = getRequestId(exchange.advanced().getRequest());
+ if (requestId.isPresent()) {
+ processRequest(exchange, SessionMsgType.TO_DEVICE_RPC_RESPONSE);
+ } else {
+ processRequest(exchange, SessionMsgType.TO_SERVER_RPC_REQUEST);
+ }
+ break;
+ }
+ }
+ }
+
+ private void processRequest(CoapExchange exchange, SessionMsgType type) {
+ log.trace("Processing {}", exchange.advanced().getRequest());
+ exchange.accept();
+ Exchange advanced = exchange.advanced();
+ Request request = advanced.getRequest();
+
+ Optional<DeviceTokenCredentials> credentials = decodeCredentials(request);
+ if (!credentials.isPresent()) {
+ exchange.respond(ResponseCode.BAD_REQUEST);
+ return;
+ }
+
+ transportService.process(TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(),
+ new DeviceAuthCallback(transportContext, exchange, sessionInfo -> {
+ UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
+ try {
+ switch (type) {
+ case POST_ATTRIBUTES_REQUEST:
+ transportService.process(sessionInfo,
+ transportContext.getAdaptor().convertToPostAttributes(sessionId, request),
+ new CoapOkCallback(exchange));
+ break;
+ case POST_TELEMETRY_REQUEST:
+ transportService.process(sessionInfo,
+ transportContext.getAdaptor().convertToPostTelemetry(sessionId, request),
+ new CoapOkCallback(exchange));
+ break;
+ case SUBSCRIBE_ATTRIBUTES_REQUEST:
+ advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced),
+ registerAsyncCoapSession(exchange, request, sessionInfo, sessionId)));
+ transportService.process(sessionInfo,
+ TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(),
+ new CoapNoOpCallback(exchange));
+ break;
+ case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
+ TransportProtos.SessionInfoProto attrSession = lookupAsyncSessionInfo(request);
+ if (attrSession != null) {
+ transportService.process(attrSession,
+ TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(),
+ new CoapOkCallback(exchange));
+ closeAndDeregister(sessionInfo);
+ }
+ break;
+ case SUBSCRIBE_RPC_COMMANDS_REQUEST:
+ advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced),
+ registerAsyncCoapSession(exchange, request, sessionInfo, sessionId)));
+ transportService.process(sessionInfo,
+ TransportProtos.SubscribeToRPCMsg.getDefaultInstance(),
+ new CoapNoOpCallback(exchange));
+ break;
+ case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
+ TransportProtos.SessionInfoProto rpcSession = lookupAsyncSessionInfo(request);
+ if (rpcSession != null) {
+ transportService.process(rpcSession,
+ TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(),
+ new CoapOkCallback(exchange));
+ transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);
+ transportService.deregisterSession(rpcSession);
+ }
+ break;
+ case TO_DEVICE_RPC_RESPONSE:
+ transportService.process(sessionInfo,
+ transportContext.getAdaptor().convertToDeviceRpcResponse(sessionId, request),
+ new CoapOkCallback(exchange));
+ break;
+ case TO_SERVER_RPC_REQUEST:
+ transportService.process(sessionInfo,
+ transportContext.getAdaptor().convertToServerRpcRequest(sessionId, request),
+ new CoapNoOpCallback(exchange));
+ break;
+ case GET_ATTRIBUTES_REQUEST:
+ transportService.registerSyncSession(sessionInfo, new CoapSessionListener(sessionId, exchange), transportContext.getTimeout());
+ transportService.process(sessionInfo,
+ transportContext.getAdaptor().convertToGetAttributes(sessionId, request),
+ new CoapNoOpCallback(exchange));
+ break;
+ }
+ } catch (AdaptorException e) {
+ log.trace("[{}] Failed to decode message: ", sessionId, e);
+ exchange.respond(ResponseCode.BAD_REQUEST);
+ } catch (IllegalAccessException e) {
+ log.trace("[{}] Failed to process message: ", sessionId, e);
+ exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
+ }
+ }));
+ }
+
+ private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(Request request) {
+ String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString();
+ return tokenToSessionIdMap.remove(token);
+ }
+
+ private String registerAsyncCoapSession(CoapExchange exchange, Request request, TransportProtos.SessionInfoProto sessionInfo, UUID sessionId) {
+ String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString();
+ tokenToSessionIdMap.putIfAbsent(token, sessionInfo);
+ CoapSessionListener attrListener = new CoapSessionListener(sessionId, exchange);
+ transportService.registerAsyncSession(sessionInfo, attrListener);
+ transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null);
+ return token;
+ }
+
+ private static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) {
+ return TransportProtos.SessionEventMsg.newBuilder()
+ .setSessionType(TransportProtos.SessionType.ASYNC)
+ .setEvent(event).build();
+ }
+
+ private Optional<DeviceTokenCredentials> decodeCredentials(Request request) {
+ List<String> uriPath = request.getOptions().getUriPath();
+ if (uriPath.size() >= ACCESS_TOKEN_POSITION) {
+ return Optional.of(new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1)));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ private Optional<FeatureType> getFeatureType(Request request) {
+ List<String> uriPath = request.getOptions().getUriPath();
+ try {
+ if (uriPath.size() >= FEATURE_TYPE_POSITION) {
+ return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION - 1).toUpperCase()));
+ }
+ } catch (RuntimeException e) {
+ log.warn("Failed to decode feature type: {}", uriPath);
+ }
+ return Optional.empty();
+ }
+
+ public static Optional<Integer> getRequestId(Request request) {
+ List<String> uriPath = request.getOptions().getUriPath();
+ try {
+ if (uriPath.size() >= REQUEST_ID_POSITION) {
+ return Optional.of(Integer.valueOf(uriPath.get(REQUEST_ID_POSITION - 1)));
+ }
+ } catch (RuntimeException e) {
+ log.warn("Failed to decode feature type: {}", uriPath);
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Resource getChild(String name) {
+ return this;
+ }
+
+ private static class DeviceAuthCallback implements TransportServiceCallback<TransportProtos.ValidateDeviceCredentialsResponseMsg> {
+ private final TransportContext transportContext;
+ private final CoapExchange exchange;
+ private final Consumer<TransportProtos.SessionInfoProto> onSuccess;
+
+ DeviceAuthCallback(TransportContext transportContext, CoapExchange exchange, Consumer<TransportProtos.SessionInfoProto> onSuccess) {
+ this.transportContext = transportContext;
+ this.exchange = exchange;
+ this.onSuccess = onSuccess;
+ }
+
+ @Override
+ public void onSuccess(TransportProtos.ValidateDeviceCredentialsResponseMsg msg) {
+ if (msg.hasDeviceInfo()) {
+ UUID sessionId = UUID.randomUUID();
+ TransportProtos.DeviceInfoProto deviceInfoProto = msg.getDeviceInfo();
+ TransportProtos.SessionInfoProto sessionInfo = TransportProtos.SessionInfoProto.newBuilder()
+ .setNodeId(transportContext.getNodeId())
+ .setTenantIdMSB(deviceInfoProto.getTenantIdMSB())
+ .setTenantIdLSB(deviceInfoProto.getTenantIdLSB())
+ .setDeviceIdMSB(deviceInfoProto.getDeviceIdMSB())
+ .setDeviceIdLSB(deviceInfoProto.getDeviceIdLSB())
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .build();
+ onSuccess.accept(sessionInfo);
+ } else {
+ exchange.respond(ResponseCode.UNAUTHORIZED);
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.warn("Failed to process request", e);
+ exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ private static class CoapOkCallback implements TransportServiceCallback<Void> {
+ private final CoapExchange exchange;
+
+ CoapOkCallback(CoapExchange exchange) {
+ this.exchange = exchange;
+ }
+
+ @Override
+ public void onSuccess(Void msg) {
+ exchange.respond(ResponseCode.VALID);
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ private static class CoapNoOpCallback implements TransportServiceCallback<Void> {
+ private final CoapExchange exchange;
+
+ CoapNoOpCallback(CoapExchange exchange) {
+ this.exchange = exchange;
+ }
+
+ @Override
+ public void onSuccess(Void msg) {
+
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ public class CoapSessionListener implements SessionMsgListener {
+
+ private final CoapExchange exchange;
+ private final AtomicInteger seqNumber = new AtomicInteger(2);
+
+ CoapSessionListener(UUID sessionId, CoapExchange exchange) {
+ this.exchange = exchange;
+ }
+
+ @Override
+ public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg msg) {
+ try {
+ exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
+ } catch (AdaptorException e) {
+ log.trace("Failed to reply due to error", e);
+ exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg) {
+ try {
+ exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
+ } catch (AdaptorException e) {
+ log.trace("Failed to reply due to error", e);
+ exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {
+ exchange.respond(ResponseCode.SERVICE_UNAVAILABLE);
+ }
+
+ @Override
+ public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg msg) {
+ try {
+ exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
+ } catch (AdaptorException e) {
+ log.trace("Failed to reply due to error", e);
+ exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg msg) {
+ try {
+ exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
+ } catch (AdaptorException e) {
+ log.trace("Failed to reply due to error", e);
+ exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ public int getNextSeqNumber() {
+ return seqNumber.getAndIncrement();
+ }
+ }
+
+ public class CoapExchangeObserverProxy implements ExchangeObserver {
+
+ private final ExchangeObserver proxy;
+ private final String token;
+
+ CoapExchangeObserverProxy(ExchangeObserver proxy, String token) {
+ super();
+ this.proxy = proxy;
+ this.token = token;
+ }
+
+ @Override
+ public void completed(Exchange exchange) {
+ proxy.completed(exchange);
+ TransportProtos.SessionInfoProto session = tokenToSessionIdMap.remove(token);
+ if (session != null) {
+ closeAndDeregister(session);
+ }
+ }
+ }
+
+ private void closeAndDeregister(TransportProtos.SessionInfoProto session) {
+ transportService.process(session, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);
+ transportService.deregisterSession(session);
+ }
+
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
index c1639ce..55df31a 100644
--- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
@@ -43,8 +43,6 @@ public class MqttTransportService {
private String host;
@Value("${transport.mqtt.bind_port}")
private Integer port;
- @Value("${transport.mqtt.adaptor}")
- private String adaptorName;
@Value("${transport.mqtt.netty.leak_detector_level}")
private String leakDetectorLevel;
common/transport/pom.xml 2(+1 -1)
diff --git a/common/transport/pom.xml b/common/transport/pom.xml
index c9b47e8..83e7ba8 100644
--- a/common/transport/pom.xml
+++ b/common/transport/pom.xml
@@ -38,7 +38,7 @@
<module>transport-api</module>
<module>mqtt</module>
<module>http</module>
- <!--module>coap</module-->
+ <module>coap</module>
</modules>
</project>
pom.xml 2(+1 -1)
diff --git a/pom.xml b/pom.xml
index f4c2957..684c3d0 100755
--- a/pom.xml
+++ b/pom.xml
@@ -370,7 +370,7 @@
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.thingsboard.transport</groupId>
+ <groupId>org.thingsboard.common.transport</groupId>
<artifactId>coap</artifactId>
<version>${project.version}</version>
</dependency>
transport/coap/build.gradle 140(+140 -0)
diff --git a/transport/coap/build.gradle b/transport/coap/build.gradle
new file mode 100644
index 0000000..6d54cb4
--- /dev/null
+++ b/transport/coap/build.gradle
@@ -0,0 +1,140 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import org.apache.tools.ant.filters.ReplaceTokens
+
+buildscript {
+ ext {
+ osPackageVersion = "3.8.0"
+ }
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath("com.netflix.nebula:gradle-ospackage-plugin:${osPackageVersion}")
+ }
+}
+
+apply plugin: "nebula.ospackage"
+
+buildDir = projectBuildDir
+version = projectVersion
+distsDirName = "./"
+
+// OS Package plugin configuration
+ospackage {
+ packageName = pkgName
+ version = "${project.version}"
+ release = 1
+ os = LINUX
+ type = BINARY
+
+ into pkgInstallFolder
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the actual .jar file
+ from(mainJar) {
+ // Strip the version from the jar filename
+ rename { String fileName ->
+ "${pkgName}.jar"
+ }
+ fileMode 0500
+ into "bin"
+ }
+
+ // Copy the config files
+ from("target/conf") {
+ exclude "${pkgName}.conf"
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "conf"
+ }
+
+}
+
+// Configure our RPM build task
+buildRpm {
+
+ arch = NOARCH
+
+ version = projectVersion.replace('-', '')
+ archiveName = "${pkgName}.rpm"
+
+ requires("java-1.8.0")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'rpm'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ preInstall file("${buildDir}/control/rpm/preinst")
+ postInstall file("${buildDir}/control/rpm/postinst")
+ preUninstall file("${buildDir}/control/rpm/prerm")
+ postUninstall file("${buildDir}/control/rpm/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the system unit files
+ from("${buildDir}/control/${pkgName}.service") {
+ addParentDirs = false
+ fileMode 0644
+ into "/usr/lib/systemd/system"
+ }
+
+ directory(pkgLogFolder, 0755)
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
+
+// Same as the buildRpm task
+buildDeb {
+
+ arch = "all"
+
+ archiveName = "${pkgName}.deb"
+
+ requires("openjdk-8-jre").or("java8-runtime").or("oracle-java8-installer").or("openjdk-8-jre-headless")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'deb'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.conf")
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.yml")
+ configurationFile("${pkgInstallFolder}/conf/logback.xml")
+
+ preInstall file("${buildDir}/control/deb/preinst")
+ postInstall file("${buildDir}/control/deb/postinst")
+ preUninstall file("${buildDir}/control/deb/prerm")
+ postUninstall file("${buildDir}/control/deb/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ directory(pkgLogFolder, 0755)
+ link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/bin/${pkgName}.jar")
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
transport/coap/pom.xml 299(+278 -21)
diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml
index a7015f0..a5e8107 100644
--- a/transport/coap/pom.xml
+++ b/transport/coap/pom.xml
@@ -27,42 +27,37 @@
<artifactId>coap</artifactId>
<packaging>jar</packaging>
- <name>Thingsboard COAP Transport</name>
+ <name>Thingsboard CoAP Transport Service</name>
<url>https://thingsboard.io</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../..</main.dir>
+ <pkg.name>tb-coap-transport</pkg.name>
+ <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
+ <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
+ <pkg.win.dist>${project.build.directory}/windows</pkg.win.dist>
</properties>
<dependencies>
<dependency>
- <groupId>org.thingsboard.common</groupId>
- <artifactId>transport</artifactId>
- </dependency>
- <dependency>
- <groupId>org.eclipse.californium</groupId>
- <artifactId>californium-core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>coap</artifactId>
</dependency>
<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
+ <groupId>org.thingsboard.common</groupId>
+ <artifactId>queue</artifactId>
</dependency>
<dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
+ <groupId>com.sun.winsw</groupId>
+ <artifactId>winsw</artifactId>
+ <classifier>bin</classifier>
+ <type>exe</type>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -81,4 +76,266 @@
</dependency>
</dependencies>
+ <build>
+ <finalName>${pkg.name}-${project.version}</finalName>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <excludes>
+ <exclude>logback.xml</exclude>
+ </excludes>
+ <filtering>false</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-service-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/conf</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/unix.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-win-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${pkg.win.dist}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <excludes>
+ <exclude>logback.xml</exclude>
+ </excludes>
+ <filtering>false</filtering>
+ </resource>
+ <resource>
+ <directory>src/main/conf</directory>
+ <excludes>
+ <exclude>tb-coap-transport.conf</exclude>
+ </excludes>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/windows.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-control</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/control</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/scripts/control</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/unix.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-windows-control</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${pkg.win.dist}</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/scripts/windows</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/windows.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-winsw-service</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>com.sun.winsw</groupId>
+ <artifactId>winsw</artifactId>
+ <classifier>bin</classifier>
+ <type>exe</type>
+ <destFileName>service.exe</destFileName>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${pkg.win.dist}</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/logback.xml</exclude>
+ </excludes>
+ <archive>
+ <manifestEntries>
+ <Implementation-Title>ThingsBoard CoAP Transport Service</Implementation-Title>
+ <Implementation-Version>${project.version}</Implementation-Version>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>org.thingsboard.server.coap.ThingsboardCoapTransportApplication</mainClass>
+ <classifier>boot</classifier>
+ <layout>ZIP</layout>
+ <executable>true</executable>
+ <excludeDevtools>true</excludeDevtools>
+ <embeddedLaunchScriptProperties>
+ <confFolder>${pkg.installFolder}/conf</confFolder>
+ <logFolder>${pkg.unixLogFolder}</logFolder>
+ <logFilename>${pkg.name}.out</logFilename>
+ <initInfoProvides>${pkg.name}</initInfoProvides>
+ </embeddedLaunchScriptProperties>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.fortasoft</groupId>
+ <artifactId>gradle-maven-plugin</artifactId>
+ <configuration>
+ <tasks>
+ <task>build</task>
+ <task>buildDeb</task>
+ <task>buildRpm</task>
+ </tasks>
+ <args>
+ <arg>-PprojectBuildDir=${project.build.directory}</arg>
+ <arg>-PprojectVersion=${project.version}</arg>
+ <arg>-PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</arg>
+ <arg>-PpkgName=${pkg.name}</arg>
+ <arg>-PpkgInstallFolder=${pkg.installFolder}</arg>
+ <arg>-PpkgLogFolder=${pkg.unixLogFolder}</arg>
+ </args>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>invoke</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <finalName>${pkg.name}</finalName>
+ <descriptors>
+ <descriptor>src/main/assembly/windows.xml</descriptor>
+ </descriptors>
+ </configuration>
+ <executions>
+ <execution>
+ <id>assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <configuration>
+ <file>${project.build.directory}/${pkg.name}.deb</file>
+ <artifactId>${project.artifactId}</artifactId>
+ <groupId>${project.groupId}</groupId>
+ <version>${project.version}</version>
+ <classifier>deb</classifier>
+ <packaging>deb</packaging>
+ </configuration>
+ <executions>
+ <execution>
+ <id>install-deb</id>
+ <phase>package</phase>
+ <goals>
+ <goal>install-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <repositories>
+ <repository>
+ <id>jenkins</id>
+ <name>Jenkins Repository</name>
+ <url>http://repo.jenkins-ci.org/releases</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
</project>
transport/coap/src/main/assembly/windows.xml 71(+71 -0)
diff --git a/transport/coap/src/main/assembly/windows.xml b/transport/coap/src/main/assembly/windows.xml
new file mode 100644
index 0000000..82da34e
--- /dev/null
+++ b/transport/coap/src/main/assembly/windows.xml
@@ -0,0 +1,71 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
+ <id>windows</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <!-- Workaround to create logs directory -->
+ <fileSets>
+ <fileSet>
+ <directory>${pkg.win.dist}</directory>
+ <outputDirectory>logs</outputDirectory>
+ <excludes>
+ <exclude>*/**</exclude>
+ </excludes>
+ </fileSet>
+ <fileSet>
+ <directory>${pkg.win.dist}/conf</directory>
+ <outputDirectory>conf</outputDirectory>
+ <lineEnding>windows</lineEnding>
+ </fileSet>
+ </fileSets>
+
+ <files>
+ <file>
+ <source>${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</source>
+ <outputDirectory>lib</outputDirectory>
+ <destName>${pkg.name}.jar</destName>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/service.exe</source>
+ <outputDirectory/>
+ <destName>${pkg.name}.exe</destName>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/service.xml</source>
+ <outputDirectory/>
+ <destName>${pkg.name}.xml</destName>
+ <lineEnding>windows</lineEnding>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/install.bat</source>
+ <outputDirectory/>
+ <lineEnding>windows</lineEnding>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/uninstall.bat</source>
+ <outputDirectory/>
+ <lineEnding>windows</lineEnding>
+ </file>
+ </files>
+</assembly>
transport/coap/src/main/conf/logback.xml 43(+43 -0)
diff --git a/transport/coap/src/main/conf/logback.xml b/transport/coap/src/main/conf/logback.xml
new file mode 100644
index 0000000..f36469d
--- /dev/null
+++ b/transport/coap/src/main/conf/logback.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE configuration>
+<configuration>
+
+ <appender name="fileLogAppender"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${pkg.logFolder}/${pkg.name}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <maxFileSize>100MB</maxFileSize>
+ <maxHistory>30</maxHistory>
+ <totalSizeCap>3GB</totalSizeCap>
+ </rollingPolicy>
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.thingsboard.server" level="INFO" />
+
+ <root level="INFO">
+ <appender-ref ref="fileLogAppender"/>
+ </root>
+
+</configuration>
diff --git a/transport/coap/src/main/conf/tb-coap-transport.conf b/transport/coap/src/main/conf/tb-coap-transport.conf
new file mode 100644
index 0000000..0afa91c
--- /dev/null
+++ b/transport/coap/src/main/conf/tb-coap-transport.conf
@@ -0,0 +1,23 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
diff --git a/transport/coap/src/main/filters/unix.properties b/transport/coap/src/main/filters/unix.properties
new file mode 100644
index 0000000..8967278
--- /dev/null
+++ b/transport/coap/src/main/filters/unix.properties
@@ -0,0 +1 @@
+pkg.logFolder=${pkg.unixLogFolder}
\ No newline at end of file
diff --git a/transport/coap/src/main/filters/windows.properties b/transport/coap/src/main/filters/windows.properties
new file mode 100644
index 0000000..a6e48d9
--- /dev/null
+++ b/transport/coap/src/main/filters/windows.properties
@@ -0,0 +1,2 @@
+pkg.logFolder=${BASE}\\logs
+pkg.winWrapperLogFolder=%BASE%\\logs
diff --git a/transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java b/transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java
new file mode 100644
index 0000000..1ad86dd
--- /dev/null
+++ b/transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.coap;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import java.util.Arrays;
+
+@SpringBootConfiguration
+@EnableAsync
+@EnableScheduling
+@ComponentScan({"org.thingsboard.server.coap", "org.thingsboard.server.common", "org.thingsboard.server.transport.coap", "org.thingsboard.server.kafka"})
+public class ThingsboardCoapTransportApplication {
+
+ private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
+ private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "tb-coap-transport";
+
+ public static void main(String[] args) {
+ SpringApplication.run(ThingsboardCoapTransportApplication.class, updateArguments(args));
+ }
+
+ private static String[] updateArguments(String[] args) {
+ if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) {
+ String[] modifiedArgs = new String[args.length + 1];
+ System.arraycopy(args, 0, modifiedArgs, 0, args.length);
+ modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM;
+ return modifiedArgs;
+ }
+ return args;
+ }
+}
diff --git a/transport/coap/src/main/resources/logback.xml b/transport/coap/src/main/resources/logback.xml
new file mode 100644
index 0000000..18864a9
--- /dev/null
+++ b/transport/coap/src/main/resources/logback.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE configuration>
+<configuration scan="true" scanPeriod="10 seconds">
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.thingsboard.server" level="TRACE" />
+
+ <root level="INFO">
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>
\ No newline at end of file
diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml
new file mode 100644
index 0000000..1790d82
--- /dev/null
+++ b/transport/coap/src/main/resources/tb-coap-transport.yml
@@ -0,0 +1,68 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+spring.main.web-environment: false
+spring.main.web-application-type: none
+
+# MQTT server parameters
+transport:
+ coap:
+ bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
+ bind_port: "${COAP_BIND_PORT:5683}"
+ timeout: "${COAP_TIMEOUT:10000}"
+
+#Quota parameters
+quota:
+ host:
+ # Max allowed number of API requests in interval for single host
+ limit: "${QUOTA_HOST_LIMIT:10000}"
+ # Interval duration
+ intervalMs: "${QUOTA_HOST_INTERVAL_MS:60000}"
+ # Maximum silence duration for host after which Host removed from QuotaService. Must be bigger than intervalMs
+ ttlMs: "${QUOTA_HOST_TTL_MS:60000}"
+ # Interval for scheduled task that cleans expired records. TTL is used for expiring
+ cleanPeriodMs: "${QUOTA_HOST_CLEAN_PERIOD_MS:300000}"
+ # Enable Host API Limits
+ enabled: "${QUOTA_HOST_ENABLED:true}"
+ # Array of whitelist hosts
+ whitelist: "${QUOTA_HOST_WHITELIST:localhost,127.0.0.1}"
+ # Array of blacklist hosts
+ blacklist: "${QUOTA_HOST_BLACKLIST:}"
+ log:
+ topSize: 10
+ intervalMin: 2
+
+kafka:
+ enabled: true
+ bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
+ acks: "${TB_KAFKA_ACKS:all}"
+ retries: "${TB_KAFKA_RETRIES:1}"
+ batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
+ linger.ms: "${TB_KAFKA_LINGER_MS:1}"
+ buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
+ transport_api:
+ requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}"
+ responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}"
+ max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
+ max_requests_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
+ response_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
+ response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
+ rule_engine:
+ topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
+ notifications:
+ topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
+ poll_interval: "${TB_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}"
+ auto_commit_interval: "${TB_TRANSPORT_NOTIFICATIONS_AUTO_COMMIT_INTERVAL_MS:100}"
diff --git a/transport/coap/src/main/scripts/control/deb/postinst b/transport/coap/src/main/scripts/control/deb/postinst
new file mode 100644
index 0000000..d4066c0
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/postinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+update-rc.d ${pkg.name} defaults
+
diff --git a/transport/coap/src/main/scripts/control/deb/postrm b/transport/coap/src/main/scripts/control/deb/postrm
new file mode 100644
index 0000000..6186580
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/postrm
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+update-rc.d -f ${pkg.name} remove
diff --git a/transport/coap/src/main/scripts/control/deb/preinst b/transport/coap/src/main/scripts/control/deb/preinst
new file mode 100644
index 0000000..6be5959
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/preinst
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if ! getent group ${pkg.name} >/dev/null; then
+ addgroup --system ${pkg.name}
+fi
+
+if ! getent passwd ${pkg.name} >/dev/null; then
+ adduser --quiet \
+ --system \
+ --ingroup ${pkg.name} \
+ --quiet \
+ --disabled-login \
+ --disabled-password \
+ --home ${pkg.installFolder} \
+ --no-create-home \
+ -gecos "Thingsboard application" \
+ ${pkg.name}
+fi
diff --git a/transport/coap/src/main/scripts/control/deb/prerm b/transport/coap/src/main/scripts/control/deb/prerm
new file mode 100644
index 0000000..898d3ef
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/prerm
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ -e /var/run/${pkg.name}/${pkg.name}.pid ]; then
+ service ${pkg.name} stop
+fi
diff --git a/transport/coap/src/main/scripts/control/rpm/postinst b/transport/coap/src/main/scripts/control/rpm/postinst
new file mode 100644
index 0000000..8a7a88f
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/postinst
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+
+if [ $1 -eq 1 ] ; then
+ # Initial installation
+ systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/coap/src/main/scripts/control/rpm/postrm b/transport/coap/src/main/scripts/control/rpm/postrm
new file mode 100644
index 0000000..8e1f8a2
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/postrm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -ge 1 ] ; then
+ # Package upgrade, not uninstall
+ systemctl try-restart ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/coap/src/main/scripts/control/rpm/preinst b/transport/coap/src/main/scripts/control/rpm/preinst
new file mode 100644
index 0000000..e19fc88
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/preinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+getent group ${pkg.name} >/dev/null || groupadd -r ${pkg.name}
+getent passwd ${pkg.name} >/dev/null || \
+useradd -d ${pkg.installFolder} -g ${pkg.name} -M -r ${pkg.name} -s /sbin/nologin \
+-c "Thingsboard application"
diff --git a/transport/coap/src/main/scripts/control/rpm/prerm b/transport/coap/src/main/scripts/control/rpm/prerm
new file mode 100644
index 0000000..accb487
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/prerm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -eq 0 ] ; then
+ # Package removal, not upgrade
+ systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
+fi
diff --git a/transport/coap/src/main/scripts/control/tb-coap-transport.service b/transport/coap/src/main/scripts/control/tb-coap-transport.service
new file mode 100644
index 0000000..d456fc0
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/tb-coap-transport.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=${pkg.name}
+After=syslog.target
+
+[Service]
+User=${pkg.name}
+ExecStart=${pkg.installFolder}/bin/${pkg.name}.jar
+SuccessExitStatus=143
+
+[Install]
+WantedBy=multi-user.target
diff --git a/transport/coap/src/main/scripts/windows/install.bat b/transport/coap/src/main/scripts/windows/install.bat
new file mode 100644
index 0000000..dba7736
--- /dev/null
+++ b/transport/coap/src/main/scripts/windows/install.bat
@@ -0,0 +1,87 @@
+@ECHO OFF
+
+setlocal ENABLEEXTENSIONS
+
+@ECHO Detecting Java version installed.
+:CHECK_JAVA_64
+@ECHO Detecting if it is 64 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\Wow6432Node\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF NOT "%JRE_PATH2%" == "" GOTO JAVA_INSTALLED
+
+:CHECK_JAVA_32
+@ECHO Detecting if it is 32 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF "%JRE_PATH2%" == "" GOTO JAVA_NOT_INSTALLED
+
+:JAVA_INSTALLED
+
+@ECHO Java 1.8 found!
+@ECHO Installing ${pkg.name} ...
+
+%BASE%${pkg.name}.exe install
+
+@ECHO ${pkg.name} installed successfully!
+
+GOTO END
+
+:JAVA_NOT_INSTALLED
+@ECHO Java 1.8 or above is not installed
+@ECHO Please go to https://java.com/ and install Java. Then retry installation.
+PAUSE
+GOTO END
+
+:END
+
+
diff --git a/transport/coap/src/main/scripts/windows/service.xml b/transport/coap/src/main/scripts/windows/service.xml
new file mode 100644
index 0000000..f7b9d30
--- /dev/null
+++ b/transport/coap/src/main/scripts/windows/service.xml
@@ -0,0 +1,36 @@
+<service>
+ <id>${pkg.name}</id>
+ <name>${project.name}</name>
+ <description>${project.description}</description>
+ <workingdirectory>%BASE%\conf</workingdirectory>
+ <logpath>${pkg.winWrapperLogFolder}</logpath>
+ <logmode>rotate</logmode>
+ <env name="LOADER_PATH" value="%BASE%\conf" />
+ <executable>java</executable>
+ <startargument>-Xloggc:%BASE%\logs\gc.log</startargument>
+ <startargument>-XX:+HeapDumpOnOutOfMemoryError</startargument>
+ <startargument>-XX:+PrintGCDetails</startargument>
+ <startargument>-XX:+PrintGCDateStamps</startargument>
+ <startargument>-XX:+PrintHeapAtGC</startargument>
+ <startargument>-XX:+PrintTenuringDistribution</startargument>
+ <startargument>-XX:+PrintGCApplicationStoppedTime</startargument>
+ <startargument>-XX:+UseGCLogFileRotation</startargument>
+ <startargument>-XX:NumberOfGCLogFiles=10</startargument>
+ <startargument>-XX:GCLogFileSize=10M</startargument>
+ <startargument>-XX:-UseBiasedLocking</startargument>
+ <startargument>-XX:+UseTLAB</startargument>
+ <startargument>-XX:+ResizeTLAB</startargument>
+ <startargument>-XX:+PerfDisableSharedMem</startargument>
+ <startargument>-XX:+UseCondCardMark</startargument>
+ <startargument>-XX:CMSWaitDuration=10000</startargument>
+ <startargument>-XX:+UseParNewGC</startargument>
+ <startargument>-XX:+UseConcMarkSweepGC</startargument>
+ <startargument>-XX:+CMSParallelRemarkEnabled</startargument>
+ <startargument>-XX:+CMSParallelInitialMarkEnabled</startargument>
+ <startargument>-XX:+CMSEdenChunksRecordAlways</startargument>
+ <startargument>-XX:CMSInitiatingOccupancyFraction=75</startargument>
+ <startargument>-XX:+UseCMSInitiatingOccupancyOnly</startargument>
+ <startargument>-jar</startargument>
+ <startargument>%BASE%\lib\${pkg.name}.jar</startargument>
+
+</service>
diff --git a/transport/coap/src/main/scripts/windows/uninstall.bat b/transport/coap/src/main/scripts/windows/uninstall.bat
new file mode 100644
index 0000000..921e4c8
--- /dev/null
+++ b/transport/coap/src/main/scripts/windows/uninstall.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+
+@ECHO Stopping ${pkg.name} ...
+net stop ${pkg.name}
+
+@ECHO Uninstalling ${pkg.name} ...
+%~dp0${pkg.name}.exe uninstall
+
+@ECHO DONE.
\ No newline at end of file
transport/http/pom.xml 6(+3 -3)
diff --git a/transport/http/pom.xml b/transport/http/pom.xml
index 225f064..407d995 100644
--- a/transport/http/pom.xml
+++ b/transport/http/pom.xml
@@ -145,7 +145,7 @@
<resource>
<directory>src/main/conf</directory>
<excludes>
- <exclude>tb-mqtt-transport.conf</exclude>
+ <exclude>tb-http-transport.conf</exclude>
</excludes>
<filtering>true</filtering>
</resource>
@@ -229,7 +229,7 @@
</excludes>
<archive>
<manifestEntries>
- <Implementation-Title>ThingsBoard MQTT Transport Service</Implementation-Title>
+ <Implementation-Title>ThingsBoard HTTP Transport Service</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
</manifestEntries>
</archive>
@@ -239,7 +239,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
- <mainClass>org.thingsboard.server.mqtt.ThingsboardHttpTransportApplication</mainClass>
+ <mainClass>org.thingsboard.server.http.ThingsboardHttpTransportApplication</mainClass>
<classifier>boot</classifier>
<layout>ZIP</layout>
<executable>true</executable>
transport/mqtt/pom.xml 4(+4 -0)
diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml
index 2750e1e..6395b40 100644
--- a/transport/mqtt/pom.xml
+++ b/transport/mqtt/pom.xml
@@ -49,6 +49,10 @@
<artifactId>queue</artifactId>
</dependency>
<dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.sun.winsw</groupId>
<artifactId>winsw</artifactId>
<classifier>bin</classifier>
diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml
index e46de8e..1d425e4 100644
--- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml
+++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml
@@ -14,6 +14,7 @@
# limitations under the License.
#
+spring.main.web-environment: false
spring.main.web-application-type: none
# MQTT server parameters
transport/pom.xml 1(+1 -0)
diff --git a/transport/pom.xml b/transport/pom.xml
index 281f511..0bb9300 100644
--- a/transport/pom.xml
+++ b/transport/pom.xml
@@ -36,6 +36,7 @@
<modules>
<module>http</module>
<module>mqtt</module>
+ <module>coap</module>
</modules>
</project>