Java tutorial
/** * 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 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 org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; 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.dao.relation.RelationService; import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; 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 org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.state.DeviceStateService; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.ReentrantLock; /** * Created by ashvayka on 05.10.18. */ @Slf4j @Service public class LocalTransportApiService implements TransportApiService { private static final ObjectMapper mapper = new ObjectMapper(); @Autowired private DeviceService deviceService; @Autowired private RelationService relationService; @Autowired private DeviceCredentialsService deviceCredentialsService; @Autowired private DeviceStateService deviceStateService; @Autowired private DbCallbackExecutorService dbCallbackExecutorService; private ReentrantLock deviceCreationLock = new ReentrantLock(); @Override public ListenableFuture<TransportApiResponseMsg> handle(TransportApiRequestMsg transportApiRequestMsg) { if (transportApiRequestMsg.hasValidateTokenRequestMsg()) { ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg(); return validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN); } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) { ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg(); return validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE); } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) { return handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()); } return getEmptyTransportApiResponseFuture(); } private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) { //TODO: Make async and enable caching DeviceCredentials credentials = deviceCredentialsService .findDeviceCredentialsByCredentialsId(credentialsId); if (credentials != null && credentials.getCredentialsType() == credentialsType) { return getDeviceInfo(credentials.getDeviceId()); } else { return getEmptyTransportApiResponseFuture(); } } private ListenableFuture<TransportApiResponseMsg> handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) { DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId); return Futures.transform(gatewayFuture, gateway -> { deviceCreationLock.lock(); try { Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName()); if (device == null) { device = new Device(); device.setTenantId(gateway.getTenantId()); device.setName(requestMsg.getDeviceName()); device.setType(requestMsg.getDeviceType()); device.setCustomerId(gateway.getCustomerId()); device = deviceService.saveDevice(device); relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created")); deviceStateService.onDeviceAdded(device); } return TransportApiResponseMsg.newBuilder() .setGetOrCreateDeviceResponseMsg(GetOrCreateDeviceFromGatewayResponseMsg.newBuilder() .setDeviceInfo(getDeviceInfoProto(device)).build()) .build(); } catch (JsonProcessingException e) { log.warn("[{}] Failed to lookup device by gateway id and name", gatewayId, requestMsg.getDeviceName(), e); throw new RuntimeException(e); } finally { deviceCreationLock.unlock(); } }, dbCallbackExecutorService); } private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId) { return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, deviceId), device -> { if (device == null) { log.trace("[{}] Failed to lookup device by id", deviceId); return getEmptyTransportApiResponse(); } try { return TransportApiResponseMsg.newBuilder() .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.newBuilder() .setDeviceInfo(getDeviceInfoProto(device)).build()) .build(); } catch (JsonProcessingException e) { log.warn("[{}] Failed to lookup device by id", deviceId, e); return getEmptyTransportApiResponse(); } }); } private DeviceInfoProto getDeviceInfoProto(Device device) throws JsonProcessingException { return 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(); } private ListenableFuture<TransportApiResponseMsg> getEmptyTransportApiResponseFuture() { return Futures.immediateFuture(getEmptyTransportApiResponse()); } private TransportApiResponseMsg getEmptyTransportApiResponse() { return TransportApiResponseMsg.newBuilder() .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.getDefaultInstance()).build(); } }