Browse Source

Handling Connect/Connack with new architecture

pull/1166/head
Andrew Shvayka 8 years ago
parent
commit
1ac0ed40ba
  1. 8
      application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java
  2. 4
      application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java
  3. 4
      application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
  4. 153
      application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java
  5. 31
      application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.java
  6. 30
      application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java
  7. 20
      application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java
  8. 17
      application/src/main/resources/thingsboard.yml
  9. 8
      common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java
  10. 66
      common/queue/src/main/java/org/thingsboard/server/kafka/AsyncCallbackTemplate.java
  11. 12
      common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java
  12. 19
      common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java
  13. 66
      common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java
  14. 12
      common/transport/src/main/proto/transport.proto
  15. 10
      transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
  16. 4
      transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java
  17. 134
      transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
  18. 2
      transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java
  19. 15
      transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java
  20. 10
      transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
  21. 45
      transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/AsyncCallbackTemplate.java
  22. 30
      transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java
  23. 15
      transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java
  24. 15
      transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java
  25. 9
      transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml

8
application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

4
application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java

@ -49,7 +49,7 @@ public class DefaultMsgQueueService implements MsgQueueService {
@Autowired @Autowired
private MsgQueue msgQueue; private MsgQueue msgQueue;
@Autowired @Autowired(required = false)
private TenantQuotaService quotaService; private TenantQuotaService quotaService;
private ScheduledExecutorService cleanupExecutor; private ScheduledExecutorService cleanupExecutor;
@ -74,7 +74,7 @@ public class DefaultMsgQueueService implements MsgQueueService {
@Override @Override
public ListenableFuture<Void> put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) { public ListenableFuture<Void> put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) {
if(quotaService.isQuotaExceeded(tenantId.getId().toString())) { if(quotaService != null && quotaService.isQuotaExceeded(tenantId.getId().toString())) {
log.warn("Tenant TbMsg Quota exceeded for [{}:{}] . Reject", tenantId.getId()); log.warn("Tenant TbMsg Quota exceeded for [{}:{}] . Reject", tenantId.getId());
return Futures.immediateFailedFuture(new RuntimeException("Tenant TbMsg Quota exceeded")); return Futures.immediateFailedFuture(new RuntimeException("Tenant TbMsg Quota exceeded"));
} }

4
application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java

@ -103,9 +103,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
responseBuilder.autoCommit(true); responseBuilder.autoCommit(true);
responseBuilder.autoCommitIntervalMs(autoCommitInterval); responseBuilder.autoCommitIntervalMs(autoCommitInterval);
responseBuilder.decoder(new RemoteJsResponseDecoder()); responseBuilder.decoder(new RemoteJsResponseDecoder());
responseBuilder.requestIdExtractor((response) -> { responseBuilder.requestIdExtractor((response) -> new UUID(response.getRequestIdMSB(), response.getRequestIdLSB()));
return new UUID(response.getRequestIdMSB(), response.getRequestIdLSB());
});
TbKafkaRequestTemplate.TbKafkaRequestTemplateBuilder TbKafkaRequestTemplate.TbKafkaRequestTemplateBuilder
<JsInvokeProtos.RemoteJsRequest, JsInvokeProtos.RemoteJsResponse> builder = TbKafkaRequestTemplate.builder(); <JsInvokeProtos.RemoteJsRequest, JsInvokeProtos.RemoteJsResponse> builder = TbKafkaRequestTemplate.builder();

153
application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java

@ -1,14 +1,165 @@
/**
* 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.service.transport; package org.thingsboard.server.service.transport;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; 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.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenResponseMsg;
import org.thingsboard.server.kafka.TBKafkaConsumerTemplate;
import org.thingsboard.server.kafka.TBKafkaProducerTemplate;
import org.thingsboard.server.kafka.TbKafkaResponseTemplate;
import org.thingsboard.server.kafka.TbKafkaSettings;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/** /**
* Created by ashvayka on 05.10.18. * Created by ashvayka on 05.10.18.
*/ */
@Slf4j @Slf4j
@Service @Service
@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) @ConditionalOnProperty(prefix = "transport.remote", value = "enabled", havingValue = "true")
public class RemoteTransportApiService implements TransportApiService { public class RemoteTransportApiService implements TransportApiService {
private static final ObjectMapper mapper = new ObjectMapper();
@Value("${transport.remote.transport_api.requests_topic}")
private String transportApiRequestsTopic;
@Value("${transport.remote.transport_api.responses_topic}")
private String transportApiResponsesTopic;
@Value("${transport.remote.transport_api.max_pending_requests}")
private int maxPendingRequests;
@Value("${transport.remote.transport_api.request_timeout}")
private long requestTimeout;
@Value("${transport.remote.transport_api.request_poll_interval}")
private int responsePollDuration;
@Value("${transport.remote.transport_api.request_auto_commit_interval}")
private int autoCommitInterval;
@Autowired
private TbKafkaSettings kafkaSettings;
@Autowired
private DiscoveryService discoveryService;
@Autowired
private DeviceService deviceService;
@Autowired
private DeviceCredentialsService deviceCredentialsService;
private ExecutorService transportCallbackExecutor;
private TbKafkaResponseTemplate<TransportApiRequestMsg, TransportApiResponseMsg> transportApiTemplate;
@PostConstruct
public void init() {
this.transportCallbackExecutor = Executors.newCachedThreadPool();
TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder<TransportApiResponseMsg> responseBuilder = TBKafkaProducerTemplate.builder();
responseBuilder.settings(kafkaSettings);
responseBuilder.defaultTopic(transportApiResponsesTopic);
responseBuilder.encoder(new TransportApiResponseEncoder());
TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder<TransportApiRequestMsg> requestBuilder = TBKafkaConsumerTemplate.builder();
requestBuilder.settings(kafkaSettings);
requestBuilder.topic(transportApiRequestsTopic);
requestBuilder.clientId(discoveryService.getNodeId());
requestBuilder.groupId("tb-node");
requestBuilder.autoCommit(true);
requestBuilder.autoCommitIntervalMs(autoCommitInterval);
requestBuilder.decoder(new TransportApiRequestDecoder());
TbKafkaResponseTemplate.TbKafkaResponseTemplateBuilder
<TransportApiRequestMsg, TransportApiResponseMsg> builder = TbKafkaResponseTemplate.builder();
builder.requestTemplate(requestBuilder.build());
builder.responseTemplate(responseBuilder.build());
builder.maxPendingRequests(maxPendingRequests);
builder.requestTimeout(requestTimeout);
builder.pollInterval(responsePollDuration);
builder.executor(transportCallbackExecutor);
builder.handler(this);
transportApiTemplate = builder.build();
transportApiTemplate.init();
}
@Override
public ListenableFuture<TransportApiResponseMsg> handle(TransportApiRequestMsg transportApiRequestMsg) throws Exception {
if (transportApiRequestMsg.hasValidateTokenRequestMsg()) {
ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg();
//TODO: Make async and enable caching
DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(msg.getToken());
if (credentials != null && credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) {
return getDeviceInfo(credentials.getDeviceId());
} else {
return getEmptyTransportApiResponseFuture();
}
}
return getEmptyTransportApiResponseFuture();
}
private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId) {
return Futures.transform(deviceService.findDeviceByIdAsync(deviceId), device -> {
if (device == null) {
log.trace("[{}] Failed to lookup device by id", deviceId);
return getEmptyTransportApiResponse();
}
try {
DeviceInfoProto deviceInfoProto = DeviceInfoProto.newBuilder()
.setTenantIdMSB(device.getTenantId().getId().getMostSignificantBits())
.setTenantIdLSB(device.getTenantId().getId().getLeastSignificantBits())
.setDeviceIdMSB(device.getId().getId().getMostSignificantBits())
.setDeviceIdLSB(device.getId().getId().getLeastSignificantBits())
.setDeviceName(device.getName())
.setDeviceType(device.getType())
.setAdditionalInfo(mapper.writeValueAsString(device.getAdditionalInfo()))
.build();
return TransportApiResponseMsg.newBuilder()
.setValidateTokenResponseMsg(ValidateDeviceTokenResponseMsg.newBuilder().setDeviceInfo(deviceInfoProto).build()).build();
} catch (JsonProcessingException e) {
log.warn("[{}] Failed to lookup device by id", deviceId, e);
return getEmptyTransportApiResponse();
}
});
}
private ListenableFuture<TransportApiResponseMsg> getEmptyTransportApiResponseFuture() {
return Futures.immediateFuture(getEmptyTransportApiResponse());
}
private TransportApiResponseMsg getEmptyTransportApiResponse() {
return TransportApiResponseMsg.newBuilder()
.setValidateTokenResponseMsg(ValidateDeviceTokenResponseMsg.getDefaultInstance()).build();
}
} }

31
application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.java

@ -0,0 +1,31 @@
/**
* 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.service.transport;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
import org.thingsboard.server.kafka.TbKafkaDecoder;
import java.io.IOException;
/**
* Created by ashvayka on 05.10.18.
*/
public class TransportApiRequestDecoder implements TbKafkaDecoder<TransportApiRequestMsg> {
@Override
public TransportApiRequestMsg decode(byte[] data) throws IOException {
return TransportApiRequestMsg.parseFrom(data);
}
}

30
application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java

@ -0,0 +1,30 @@
/**
* 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.service.transport;
import org.thingsboard.server.kafka.TbKafkaEncoder;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
/**
* Created by ashvayka on 05.10.18.
*/
public class TransportApiResponseEncoder implements TbKafkaEncoder<TransportApiResponseMsg> {
@Override
public byte[] encode(TransportApiResponseMsg value) {
return value.toByteArray();
}
}

20
application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java

@ -1,7 +1,25 @@
/**
* 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.service.transport; package org.thingsboard.server.service.transport;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.kafka.TbKafkaHandler;
/** /**
* Created by ashvayka on 05.10.18. * Created by ashvayka on 05.10.18.
*/ */
public interface TransportApiService { public interface TransportApiService extends TbKafkaHandler<TransportProtos.TransportApiRequestMsg, TransportProtos.TransportApiResponseMsg> {
} }

17
application/src/main/resources/thingsboard.yml

@ -84,7 +84,7 @@ http:
# MQTT server parameters # MQTT server parameters
mqtt: mqtt:
# Enable/disable mqtt transport protocol. # Enable/disable mqtt transport protocol.
enabled: "${MQTT_ENABLED:true}" enabled: "${MQTT_ENABLED:false}"
bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}" bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
bind_port: "${MQTT_BIND_PORT:1883}" bind_port: "${MQTT_BIND_PORT:1883}"
adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}" adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}"
@ -149,7 +149,7 @@ quota:
# Interval for scheduled task that cleans expired records. TTL is used for expiring # Interval for scheduled task that cleans expired records. TTL is used for expiring
cleanPeriodMs: "${QUOTA_TENANT_CLEAN_PERIOD_MS:300000}" cleanPeriodMs: "${QUOTA_TENANT_CLEAN_PERIOD_MS:300000}"
# Enable Host API Limits # Enable Host API Limits
enabled: "${QUOTA_TENANT_ENABLED:false}" enabled: "${QUOTA_TENANT_ENABLED:true}"
# Array of whitelist tenants # Array of whitelist tenants
whitelist: "${QUOTA_TENANT_WHITELIST:}" whitelist: "${QUOTA_TENANT_WHITELIST:}"
# Array of blacklist tenants # Array of blacklist tenants
@ -450,3 +450,16 @@ js:
response_auto_commit_interval: "${REMOTE_JS_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}" response_auto_commit_interval: "${REMOTE_JS_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
# Maximum allowed JavaScript execution errors before JavaScript will be blacklisted # Maximum allowed JavaScript execution errors before JavaScript will be blacklisted
max_errors: "${REMOTE_JS_SANDBOX_MAX_ERRORS:3}" max_errors: "${REMOTE_JS_SANDBOX_MAX_ERRORS:3}"
transport:
remote:
enabled: "${REMOTE_TRANSPORT_ENABLED:true}"
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}"
request_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
request_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
request_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:1000}"
rule_engine:
topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"

8
common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

66
common/queue/src/main/java/org/thingsboard/server/kafka/AsyncCallbackTemplate.java

@ -0,0 +1,66 @@
/**
* 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.kafka;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* Created by ashvayka on 05.10.18.
*/
public class AsyncCallbackTemplate {
public static <T> void withCallbackAndTimeout(ListenableFuture<T> future,
Consumer<T> onSuccess,
Consumer<Throwable> onFailure,
long timeoutInMs,
ScheduledExecutorService timeoutExecutor,
Executor callbackExecutor) {
future = Futures.withTimeout(future, timeoutInMs, TimeUnit.MILLISECONDS, timeoutExecutor);
withCallback(future, onSuccess, onFailure, callbackExecutor);
}
public static <T> void withCallback(ListenableFuture<T> future, Consumer<T> onSuccess,
Consumer<Throwable> onFailure, Executor executor) {
FutureCallback<T> callback = new FutureCallback<T>() {
@Override
public void onSuccess(T result) {
try {
onSuccess.accept(result);
} catch (Throwable th) {
onFailure(th);
}
}
@Override
public void onFailure(Throwable t) {
onFailure.accept(t);
}
};
if (executor != null) {
Futures.addCallback(future, callback, executor);
} else {
Futures.addCallback(future, callback);
}
}
}

12
common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -101,6 +101,10 @@ public class TBKafkaProducerTemplate<T> {
return send(this.defaultTopic, key, value, timestamp, headers); return send(this.defaultTopic, key, value, timestamp, headers);
} }
public Future<RecordMetadata> send(String topic, String key, T value, Iterable<Header> headers) {
return send(topic, key, value, null, headers);
}
public Future<RecordMetadata> send(String topic, String key, T value, Long timestamp, Iterable<Header> headers) { public Future<RecordMetadata> send(String topic, String key, T value, Long timestamp, Iterable<Header> headers) {
byte[] data = encoder.encode(value); byte[] data = encoder.encode(value);
ProducerRecord<String, byte[]> record; ProducerRecord<String, byte[]> record;

19
common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java

@ -1,12 +1,27 @@
/**
* 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.kafka; package org.thingsboard.server.kafka;
import java.util.function.Consumer; import com.google.common.util.concurrent.ListenableFuture;
/** /**
* Created by ashvayka on 05.10.18. * Created by ashvayka on 05.10.18.
*/ */
public interface TbKafkaHandler<Request, Response> { public interface TbKafkaHandler<Request, Response> {
void handle(Request request, Consumer<Response> onSuccess, Consumer<Throwable> onFailure); ListenableFuture<Response> handle(Request request) throws Exception;
} }

66
common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -21,13 +21,15 @@ import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.common.header.Header; import org.apache.kafka.common.header.Header;
import org.apache.kafka.common.header.internals.RecordHeader; import org.apache.kafka.common.header.internals.RecordHeader;
import java.io.IOException;
import java.time.Duration; import java.time.Duration;
import java.util.Collections; import java.util.Collections;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
@ -40,12 +42,14 @@ public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaT
private final TBKafkaProducerTemplate<Response> responseTemplate; private final TBKafkaProducerTemplate<Response> responseTemplate;
private final TbKafkaHandler<Request, Response> handler; private final TbKafkaHandler<Request, Response> handler;
private final ConcurrentMap<UUID, String> pendingRequests; private final ConcurrentMap<UUID, String> pendingRequests;
private final ExecutorService executor; private final ExecutorService loopExecutor;
private final ScheduledExecutorService timeoutExecutor;
private final ExecutorService callbackExecutor;
private final int maxPendingRequests; private final int maxPendingRequests;
private final long requestTimeout;
private final long pollInterval; private final long pollInterval;
private volatile boolean stopped = false; private volatile boolean stopped = false;
//TODO:
private final AtomicInteger pendingRequestCount = new AtomicInteger(); private final AtomicInteger pendingRequestCount = new AtomicInteger();
@Builder @Builder
@ -53,6 +57,7 @@ public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaT
TBKafkaProducerTemplate<Response> responseTemplate, TBKafkaProducerTemplate<Response> responseTemplate,
TbKafkaHandler<Request, Response> handler, TbKafkaHandler<Request, Response> handler,
long pollInterval, long pollInterval,
long requestTimeout,
int maxPendingRequests, int maxPendingRequests,
ExecutorService executor) { ExecutorService executor) {
this.requestTemplate = requestTemplate; this.requestTemplate = requestTemplate;
@ -61,18 +66,24 @@ public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaT
this.pendingRequests = new ConcurrentHashMap<>(); this.pendingRequests = new ConcurrentHashMap<>();
this.maxPendingRequests = maxPendingRequests; this.maxPendingRequests = maxPendingRequests;
this.pollInterval = pollInterval; this.pollInterval = pollInterval;
this.executor = executor; this.requestTimeout = requestTimeout;
this.callbackExecutor = executor;
this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor();
this.loopExecutor = Executors.newSingleThreadExecutor();
} }
public void init() { public void init() {
this.responseTemplate.init(); this.responseTemplate.init();
requestTemplate.subscribe(); requestTemplate.subscribe();
executor.submit(() -> { loopExecutor.submit(() -> {
while (!stopped) { while (!stopped) {
if(pendingRequestCount.get() > maxPendingRequests){ while (pendingRequestCount.get() >= maxPendingRequests) {
try {
Thread.sleep(pollInterval);
} catch (InterruptedException e) {
log.trace("Failed to wait until the server has capacity to handle new requests", e);
}
} }
//TODO: we need to protect from reading too much requests.
ConsumerRecords<String, byte[]> requests = requestTemplate.poll(Duration.ofMillis(pollInterval)); ConsumerRecords<String, byte[]> requests = requestTemplate.poll(Duration.ofMillis(pollInterval));
requests.forEach(request -> { requests.forEach(request -> {
Header requestIdHeader = request.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER); Header requestIdHeader = request.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER);
@ -92,12 +103,27 @@ public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaT
} }
String responseTopic = bytesToString(responseTopicHeader.value()); String responseTopic = bytesToString(responseTopicHeader.value());
try { try {
pendingRequestCount.getAndIncrement();
Request decodedRequest = requestTemplate.decode(request); Request decodedRequest = requestTemplate.decode(request);
executor.submit(() -> handler.handle(decodedRequest, AsyncCallbackTemplate.withCallbackAndTimeout(handler.handle(decodedRequest),
response -> reply(requestId, responseTopic, response), response -> {
e -> log.error("[{}] Failed to process the request: {}", requestId, request, e))); pendingRequestCount.decrementAndGet();
reply(requestId, responseTopic, response);
},
e -> {
pendingRequestCount.decrementAndGet();
if (e.getCause() != null && e.getCause() instanceof TimeoutException) {
log.warn("[{}] Timedout to process the request: {}", requestId, request, e);
} else {
log.trace("[{}] Failed to process the request: {}", requestId, request, e);
}
},
requestTimeout,
timeoutExecutor,
callbackExecutor);
} catch (Throwable e) { } catch (Throwable e) {
log.error("[{}] Failed to process the request: {}", requestId, request, e); pendingRequestCount.decrementAndGet();
log.warn("[{}] Failed to process the request: {}", requestId, request, e);
} }
}); });
} }
@ -106,10 +132,16 @@ public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaT
public void stop() { public void stop() {
stopped = true; stopped = true;
if (timeoutExecutor != null) {
timeoutExecutor.shutdownNow();
}
if (loopExecutor != null) {
loopExecutor.shutdownNow();
}
} }
private void reply(UUID requestId, String topic, Response response) { private void reply(UUID requestId, String topic, Response response) {
responseTemplate.send(topic, response, Collections.singletonList(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId)))); responseTemplate.send(topic, requestId.toString(), response, Collections.singletonList(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId))));
} }
} }

12
common/transport/src/main/proto/transport.proto

@ -47,11 +47,13 @@ message TsKvListProto {
} }
message DeviceInfoProto { message DeviceInfoProto {
int64 deviceIdMSB = 1; int64 tenantIdMSB = 1;
int64 deviceIdLSB = 2; int64 tenantIdLSB = 2;
string deviceName = 3; int64 deviceIdMSB = 3;
string deviceType = 4; int64 deviceIdLSB = 4;
string additionalInfo = 5; string deviceName = 5;
string deviceType = 6;
string additionalInfo = 7;
} }
/** /**

10
transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java

@ -157,10 +157,10 @@ public class CoapTransportResource extends CoapResource {
CoapSessionCtx ctx = new CoapSessionCtx(exchange, adaptor, processor, authService, timeout); CoapSessionCtx ctx = new CoapSessionCtx(exchange, adaptor, processor, authService, timeout);
if (!ctx.login(credentials.get())) { // if (!ctx.login(credentials.get())) {
exchange.respond(ResponseCode.UNAUTHORIZED); // exchange.respond(ResponseCode.UNAUTHORIZED);
return Optional.empty(); // return Optional.empty();
} // }
AdaptorToSessionActorMsg msg; AdaptorToSessionActorMsg msg;
try { try {
@ -190,7 +190,7 @@ public class CoapTransportResource extends CoapResource {
throw new IllegalArgumentException("Unsupported msg type: " + type); throw new IllegalArgumentException("Unsupported msg type: " + type);
} }
log.trace("Processing msg: {}", msg); log.trace("Processing msg: {}", msg);
processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg)); // processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg));
} catch (AdaptorException e) { } catch (AdaptorException e) {
log.debug("Failed to decode payload {}", e); log.debug("Failed to decode payload {}", e);
exchange.respond(ResponseCode.BAD_REQUEST, e.getMessage()); exchange.respond(ResponseCode.BAD_REQUEST, e.getMessage());

4
transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java

@ -49,7 +49,7 @@ public class CoapSessionCtx extends DeviceAwareSessionContext {
private final AtomicInteger seqNumber = new AtomicInteger(2); private final AtomicInteger seqNumber = new AtomicInteger(2);
public CoapSessionCtx(CoapExchange exchange, CoapTransportAdaptor adaptor, SessionMsgProcessor processor, DeviceAuthService authService, long timeout) { public CoapSessionCtx(CoapExchange exchange, CoapTransportAdaptor adaptor, SessionMsgProcessor processor, DeviceAuthService authService, long timeout) {
super(processor, authService); super();
Request request = exchange.advanced().getRequest(); Request request = exchange.advanced().getRequest();
this.token = request.getTokenString(); this.token = request.getTokenString();
this.sessionId = new CoapSessionId(request.getSource().getHostAddress(), request.getSourcePort(), this.token); this.sessionId = new CoapSessionId(request.getSource().getHostAddress(), request.getSourcePort(), this.token);
@ -112,7 +112,7 @@ public class CoapSessionCtx extends DeviceAwareSessionContext {
public void close() { public void close() {
log.info("[{}] Closing processing context. Timeout: {}", sessionId, exchange.advanced().isTimedOut()); log.info("[{}] Closing processing context. Timeout: {}", sessionId, exchange.advanced().isTimedOut());
processor.process(exchange.advanced().isTimedOut() ? SessionCloseMsg.onTimeout(sessionId) : SessionCloseMsg.onError(sessionId)); // processor.process(exchange.advanced().isTimedOut() ? SessionCloseMsg.onTimeout(sessionId) : SessionCloseMsg.onError(sessionId));
} }
@Override @Override

134
transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java

@ -73,20 +73,20 @@ public class DeviceApiController {
if (quotaExceeded(httpRequest, responseWriter)) { if (quotaExceeded(httpRequest, responseWriter)) {
return responseWriter; return responseWriter;
} }
HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); // HttpSessionCtx ctx = getHttpSessionCtx(responseWriter);
if (ctx.login(new DeviceTokenCredentials(deviceToken))) { // if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
GetAttributesRequest request; // GetAttributesRequest request;
if (StringUtils.isEmpty(clientKeys) && StringUtils.isEmpty(sharedKeys)) { // if (StringUtils.isEmpty(clientKeys) && StringUtils.isEmpty(sharedKeys)) {
request = new BasicGetAttributesRequest(0); // request = new BasicGetAttributesRequest(0);
} else { // } else {
Set<String> clientKeySet = !StringUtils.isEmpty(clientKeys) ? new HashSet<>(Arrays.asList(clientKeys.split(","))) : null; // Set<String> clientKeySet = !StringUtils.isEmpty(clientKeys) ? new HashSet<>(Arrays.asList(clientKeys.split(","))) : null;
Set<String> sharedKeySet = !StringUtils.isEmpty(sharedKeys) ? new HashSet<>(Arrays.asList(sharedKeys.split(","))) : null; // Set<String> sharedKeySet = !StringUtils.isEmpty(sharedKeys) ? new HashSet<>(Arrays.asList(sharedKeys.split(","))) : null;
request = new BasicGetAttributesRequest(0, clientKeySet, sharedKeySet); // request = new BasicGetAttributesRequest(0, clientKeySet, sharedKeySet);
} // }
process(ctx, request); // process(ctx, request);
} else { // } else {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); // responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
} // }
return responseWriter; return responseWriter;
} }
@ -98,16 +98,16 @@ public class DeviceApiController {
if (quotaExceeded(request, responseWriter)) { if (quotaExceeded(request, responseWriter)) {
return responseWriter; return responseWriter;
} }
HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); // HttpSessionCtx ctx = getHttpSessionCtx(responseWriter);
if (ctx.login(new DeviceTokenCredentials(deviceToken))) { // if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
try { // try {
process(ctx, JsonConverter.convertToAttributes(new JsonParser().parse(json))); // process(ctx, JsonConverter.convertToAttributes(new JsonParser().parse(json)));
} catch (IllegalStateException | JsonSyntaxException ex) { // } catch (IllegalStateException | JsonSyntaxException ex) {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); // responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
} // }
} else { // } else {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); // responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
} // }
return responseWriter; return responseWriter;
} }
@ -119,15 +119,15 @@ public class DeviceApiController {
return responseWriter; return responseWriter;
} }
HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); HttpSessionCtx ctx = getHttpSessionCtx(responseWriter);
if (ctx.login(new DeviceTokenCredentials(deviceToken))) { // if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
try { // try {
process(ctx, JsonConverter.convertToTelemetry(new JsonParser().parse(json))); // process(ctx, JsonConverter.convertToTelemetry(new JsonParser().parse(json)));
} catch (IllegalStateException | JsonSyntaxException ex) { // } catch (IllegalStateException | JsonSyntaxException ex) {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); // responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
} // }
} else { // } else {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); // responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
} // }
return responseWriter; return responseWriter;
} }
@ -147,17 +147,17 @@ public class DeviceApiController {
if (quotaExceeded(request, responseWriter)) { if (quotaExceeded(request, responseWriter)) {
return responseWriter; return responseWriter;
} }
HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); // HttpSessionCtx ctx = getHttpSessionCtx(responseWriter);
if (ctx.login(new DeviceTokenCredentials(deviceToken))) { // if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
try { // try {
JsonObject response = new JsonParser().parse(json).getAsJsonObject(); // JsonObject response = new JsonParser().parse(json).getAsJsonObject();
process(ctx, new ToDeviceRpcResponseMsg(requestId, response.toString())); // process(ctx, new ToDeviceRpcResponseMsg(requestId, response.toString()));
} catch (IllegalStateException | JsonSyntaxException ex) { // } catch (IllegalStateException | JsonSyntaxException ex) {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); // responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
} // }
} else { // } else {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); // responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
} // }
return responseWriter; return responseWriter;
} }
@ -169,18 +169,18 @@ public class DeviceApiController {
return responseWriter; return responseWriter;
} }
HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); HttpSessionCtx ctx = getHttpSessionCtx(responseWriter);
if (ctx.login(new DeviceTokenCredentials(deviceToken))) { // if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
try { // try {
JsonObject request = new JsonParser().parse(json).getAsJsonObject(); // JsonObject request = new JsonParser().parse(json).getAsJsonObject();
process(ctx, new ToServerRpcRequestMsg(0, // process(ctx, new ToServerRpcRequestMsg(0,
request.get("method").getAsString(), // request.get("method").getAsString(),
request.get("params").toString())); // request.get("params").toString()));
} catch (IllegalStateException | JsonSyntaxException ex) { // } catch (IllegalStateException | JsonSyntaxException ex) {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); // responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
} // }
} else { // } else {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); // responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
} // }
return responseWriter; return responseWriter;
} }
@ -197,16 +197,16 @@ public class DeviceApiController {
if (quotaExceeded(httpRequest, responseWriter)) { if (quotaExceeded(httpRequest, responseWriter)) {
return responseWriter; return responseWriter;
} }
HttpSessionCtx ctx = getHttpSessionCtx(responseWriter, timeout); // HttpSessionCtx ctx = getHttpSessionCtx(responseWriter, timeout);
if (ctx.login(new DeviceTokenCredentials(deviceToken))) { // if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
try { // try {
process(ctx, msg); // process(ctx, msg);
} catch (IllegalStateException | JsonSyntaxException ex) { // } catch (IllegalStateException | JsonSyntaxException ex) {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); // responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
} // }
} else { // } else {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); // responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
} // }
return responseWriter; return responseWriter;
} }
@ -220,7 +220,7 @@ public class DeviceApiController {
private void process(HttpSessionCtx ctx, FromDeviceMsg request) { private void process(HttpSessionCtx ctx, FromDeviceMsg request) {
AdaptorToSessionActorMsg msg = new BasicAdaptorToSessionActorMsg(ctx, request); AdaptorToSessionActorMsg msg = new BasicAdaptorToSessionActorMsg(ctx, request);
processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg)); // processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg));
} }
private boolean quotaExceeded(HttpServletRequest request, DeferredResult<ResponseEntity> responseWriter) { private boolean quotaExceeded(HttpServletRequest request, DeferredResult<ResponseEntity> responseWriter) {

2
transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java

@ -43,7 +43,7 @@ public class HttpSessionCtx extends DeviceAwareSessionContext {
private final DeferredResult<ResponseEntity> responseWriter; private final DeferredResult<ResponseEntity> responseWriter;
public HttpSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, DeferredResult<ResponseEntity> responseWriter, long timeout) { public HttpSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, DeferredResult<ResponseEntity> responseWriter, long timeout) {
super(processor, authService); super();
this.sessionId = new HttpSessionId(); this.sessionId = new HttpSessionId();
this.responseWriter = responseWriter; this.responseWriter = responseWriter;
this.timeout = timeout; this.timeout = timeout;

15
transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java

@ -1,3 +1,18 @@
/**
* 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.mqtt; package org.thingsboard.server.transport.mqtt;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;

10
transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -388,7 +388,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
ctx.close(); ctx.close();
} else { } else {
ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED));
deviceSessionCtx.setDeviceInfo(deviceSessionCtx.getDeviceInfo()); deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo());
transportService.process(getSessionEventMsg(SessionEvent.OPEN), null); transportService.process(getSessionEventMsg(SessionEvent.OPEN), null);
checkGatewaySession(); checkGatewaySession();
} }

45
transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/AsyncCallbackTemplate.java

@ -1,45 +0,0 @@
package org.thingsboard.server.mqtt.service;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import javax.annotation.Nullable;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* Created by ashvayka on 05.10.18.
*/
public class AsyncCallbackTemplate {
public static <T> void withCallback(ListenableFuture<T> future, Consumer<T> onSuccess,
Consumer<Throwable> onFailure) {
withCallback(future, onSuccess, onFailure, null);
}
public static <T> void withCallback(ListenableFuture<T> future, Consumer<T> onSuccess,
Consumer<Throwable> onFailure, Executor executor) {
FutureCallback<T> callback = new FutureCallback<T>() {
@Override
public void onSuccess(@Nullable T result) {
try {
onSuccess.accept(result);
} catch (Throwable th) {
onFailure(th);
}
}
@Override
public void onFailure(Throwable t) {
onFailure.accept(t);
}
};
if (executor != null) {
Futures.addCallback(future, callback, executor);
} else {
Futures.addCallback(future, callback);
}
}
}

30
transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java

@ -1,3 +1,18 @@
/**
* 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.mqtt.service; package org.thingsboard.server.mqtt.service;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -5,6 +20,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.kafka.AsyncCallbackTemplate;
import org.thingsboard.server.kafka.TBKafkaConsumerTemplate; import org.thingsboard.server.kafka.TBKafkaConsumerTemplate;
import org.thingsboard.server.kafka.TBKafkaProducerTemplate; import org.thingsboard.server.kafka.TBKafkaProducerTemplate;
import org.thingsboard.server.kafka.TbKafkaRequestTemplate; import org.thingsboard.server.kafka.TbKafkaRequestTemplate;
@ -23,19 +39,19 @@ import java.util.concurrent.Executors;
@Service @Service
public class MqttTransportService implements TransportService { public class MqttTransportService implements TransportService {
@Value("${kafka.rule-engine.topic}") @Value("${kafka.rule_engine.topic}")
private String ruleEngineTopic; private String ruleEngineTopic;
@Value("${kafka.transport-api.requests-topic}") @Value("${kafka.transport_api.requests_topic}")
private String transportApiRequestsTopic; private String transportApiRequestsTopic;
@Value("${kafka.transport-api.responses-topic}") @Value("${kafka.transport_api.responses_topic}")
private String transportApiResponsesTopic; private String transportApiResponsesTopic;
@Value("${kafka.transport-api.max_pending_requests}") @Value("${kafka.transport_api.max_pending_requests}")
private long maxPendingRequests; private long maxPendingRequests;
@Value("${kafka.transport-api.max_requests_timeout}") @Value("${kafka.transport_api.max_requests_timeout}")
private long maxRequestsTimeout; private long maxRequestsTimeout;
@Value("${kafka.transport-api.response_poll_interval}") @Value("${kafka.transport_api.response_poll_interval}")
private int responsePollDuration; private int responsePollDuration;
@Value("${kafka.transport-api.response_auto_commit_interval}") @Value("${kafka.transport_api.response_auto_commit_interval}")
private int autoCommitInterval; private int autoCommitInterval;
@Autowired @Autowired

15
transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java

@ -1,3 +1,18 @@
/**
* 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.mqtt.service; package org.thingsboard.server.mqtt.service;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;

15
transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java

@ -1,3 +1,18 @@
/**
* 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.mqtt.service; package org.thingsboard.server.mqtt.service;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;

9
transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml

@ -73,13 +73,12 @@ kafka:
batch.size: "${TB_KAFKA_BATCH_SIZE:16384}" batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
linger.ms: "${TB_KAFKA_LINGER_MS:1}" linger.ms: "${TB_KAFKA_LINGER_MS:1}"
buffer.memory: "${TB_BUFFER_MEMORY:33554432}" buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
transport-api: transport_api:
requests-topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}" requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}"
responses-topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}" responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}"
max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}" max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
max_requests_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}" max_requests_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
response_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" response_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}" response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
# Maximum allowed JavaScript execution errors before JavaScript will be blacklisted rule_engine:
rule-engine:
topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}" topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"

Loading…
Cancel
Save