27 changed files with 786 additions and 11 deletions
@ -0,0 +1,267 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.device; |
|||
|
|||
import com.datastax.oss.driver.api.core.uuid.Uuids; |
|||
import com.fasterxml.jackson.core.JsonProcessingException; |
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import com.google.common.util.concurrent.Futures; |
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import com.google.common.util.concurrent.MoreExecutors; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.DataConstants; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.DeviceProfileType; |
|||
import org.thingsboard.server.common.data.audit.ActionType; |
|||
import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileConfiguration; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.id.UserId; |
|||
import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
|||
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
|||
import org.thingsboard.server.common.data.kv.StringDataEntry; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
import org.thingsboard.server.common.msg.TbMsgMetaData; |
|||
import org.thingsboard.server.common.msg.queue.ServiceType; |
|||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
|||
import org.thingsboard.server.dao.attributes.AttributesService; |
|||
import org.thingsboard.server.dao.audit.AuditLogService; |
|||
import org.thingsboard.server.dao.device.DeviceCredentialsService; |
|||
import org.thingsboard.server.dao.device.DeviceProfileDao; |
|||
import org.thingsboard.server.dao.device.DeviceProvisionService; |
|||
import org.thingsboard.server.dao.device.DeviceService; |
|||
import org.thingsboard.server.dao.device.provision.ProvisionRequest; |
|||
import org.thingsboard.server.dao.device.provision.ProvisionResponse; |
|||
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; |
|||
import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
|||
import org.thingsboard.server.queue.TbQueueCallback; |
|||
import org.thingsboard.server.queue.TbQueueProducer; |
|||
import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
|||
import org.thingsboard.server.queue.discovery.PartitionService; |
|||
import org.thingsboard.server.service.state.DeviceStateService; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
import java.util.concurrent.locks.ReentrantLock; |
|||
|
|||
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
|||
|
|||
|
|||
@Service |
|||
@Slf4j |
|||
public class DeviceProvisionServiceImpl implements DeviceProvisionService { |
|||
|
|||
protected TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> ruleEngineMsgProducer; |
|||
|
|||
private static final String DEVICE_PROVISION_STATE = "provisionState"; |
|||
private static final String PROVISIONED_STATE = "provisioned"; |
|||
|
|||
private static final UserId PROVISION_USER_ID = UserId.fromString(NULL_UUID.toString()); |
|||
|
|||
private final ReentrantLock deviceCreationLock = new ReentrantLock(); |
|||
|
|||
@Autowired |
|||
DeviceProfileDao deviceProfileDao; |
|||
|
|||
@Autowired |
|||
DeviceService deviceService; |
|||
|
|||
@Autowired |
|||
DeviceCredentialsService deviceCredentialsService; |
|||
|
|||
@Autowired |
|||
AttributesService attributesService; |
|||
|
|||
@Autowired |
|||
DeviceStateService deviceStateService; |
|||
|
|||
@Autowired |
|||
AuditLogService auditLogService; |
|||
|
|||
@Autowired |
|||
PartitionService partitionService; |
|||
|
|||
|
|||
@Override |
|||
public ListenableFuture<ProvisionResponse> provisionDevice(ProvisionRequest provisionRequest) { |
|||
DeviceProfile targetProfile = deviceProfileDao.findProfileByTenantIdAndProfileDataProvisionConfigurationPair( |
|||
TenantId.SYS_TENANT_ID, |
|||
provisionRequest.getCredentials().getProvisionDeviceKey(), |
|||
provisionRequest.getCredentials().getProvisionDeviceSecret()); |
|||
|
|||
if (targetProfile.getProfileData().getConfiguration().getType() != DeviceProfileType.PROVISION) { |
|||
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.NOT_FOUND)); |
|||
} |
|||
|
|||
ProvisionDeviceProfileConfiguration currentProfileConfiguration = (ProvisionDeviceProfileConfiguration) targetProfile.getProfileData().getConfiguration(); |
|||
if (!new ProvisionDeviceProfileConfiguration(provisionRequest.getCredentials().getProvisionDeviceKey(), provisionRequest.getCredentials().getProvisionDeviceSecret()).equals(currentProfileConfiguration)) { |
|||
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.NOT_FOUND)); |
|||
} |
|||
|
|||
Device device = deviceService.findDeviceByTenantIdAndName(targetProfile.getTenantId(), provisionRequest.getDeviceName()); |
|||
switch (currentProfileConfiguration.getStrategy()) { |
|||
case CHECK_NEW_DEVICE: |
|||
if (device == null) { |
|||
return createDevice(provisionRequest, targetProfile); |
|||
} else { |
|||
log.warn("[{}] The device is present and could not be provisioned once more!", device.getName()); |
|||
notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
|||
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.FAILURE)); |
|||
} |
|||
case CHECK_PRE_PROVISIONED_DEVICE: |
|||
if (device == null) { |
|||
log.warn("[{}] Failed to find pre provisioned device!", provisionRequest.getDeviceName()); |
|||
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.FAILURE)); |
|||
} else { |
|||
return processProvision(device, provisionRequest); |
|||
} |
|||
default: |
|||
throw new RuntimeException("Strategy is not supported - " + currentProfileConfiguration.getStrategy().name()); |
|||
} |
|||
} |
|||
|
|||
private ListenableFuture<ProvisionResponse> processProvision(Device device, ProvisionRequest provisionRequest) { |
|||
ListenableFuture<Optional<AttributeKvEntry>> provisionStateFuture = attributesService.find(device.getTenantId(), device.getId(), |
|||
DataConstants.SERVER_SCOPE, DEVICE_PROVISION_STATE); |
|||
ListenableFuture<Boolean> provisionedFuture = Futures.transformAsync(provisionStateFuture, optionalAtr -> { |
|||
if (optionalAtr.isPresent()) { |
|||
String state = optionalAtr.get().getValueAsString(); |
|||
if (state.equals(PROVISIONED_STATE)) { |
|||
return Futures.immediateFuture(true); |
|||
} else { |
|||
log.error("[{}][{}] Unknown provision state: {}!", device.getName(), DEVICE_PROVISION_STATE, state); |
|||
return Futures.immediateCancelledFuture(); |
|||
} |
|||
} |
|||
return Futures.transform(saveProvisionStateAttribute(device), input -> false, MoreExecutors.directExecutor()); |
|||
}, MoreExecutors.directExecutor()); |
|||
if (provisionedFuture.isCancelled()) { |
|||
throw new RuntimeException("Unknown provision state!"); |
|||
} |
|||
return Futures.transform(provisionedFuture, provisioned -> { |
|||
if (provisioned) { |
|||
notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
|||
return new ProvisionResponse(null, ProvisionResponseStatus.FAILURE); |
|||
} |
|||
notify(device, provisionRequest, DataConstants.PROVISION_SUCCESS, true); |
|||
return new ProvisionResponse(deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()), ProvisionResponseStatus.SUCCESS); |
|||
}, MoreExecutors.directExecutor()); |
|||
} |
|||
|
|||
private ListenableFuture<ProvisionResponse> createDevice(ProvisionRequest provisionRequest, DeviceProfile profile) { |
|||
deviceCreationLock.lock(); |
|||
try { |
|||
return processCreateDevice(provisionRequest, profile); |
|||
} finally { |
|||
deviceCreationLock.unlock(); |
|||
} |
|||
} |
|||
|
|||
private ListenableFuture<ProvisionResponse> processCreateDevice(ProvisionRequest provisionRequest, DeviceProfile profile) { |
|||
Device device = deviceService.findDeviceByTenantIdAndName(profile.getTenantId(), provisionRequest.getDeviceName()); |
|||
if (device == null) { |
|||
Device savedDevice = saveDevice(provisionRequest, profile); |
|||
|
|||
deviceStateService.onDeviceAdded(savedDevice); |
|||
pushDeviceCreatedEventToRuleEngine(savedDevice); |
|||
notify(savedDevice, provisionRequest, DataConstants.PROVISION_SUCCESS, true); |
|||
|
|||
return Futures.transform(saveProvisionStateAttribute(savedDevice), input -> |
|||
new ProvisionResponse( |
|||
getDeviceCredentials(savedDevice, provisionRequest.getX509CertPubKey()), |
|||
ProvisionResponseStatus.SUCCESS), MoreExecutors.directExecutor()); |
|||
} |
|||
log.warn("[{}] The device is already provisioned!", device.getName()); |
|||
notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
|||
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.FAILURE)); |
|||
} |
|||
|
|||
private ListenableFuture<List<Void>> saveProvisionStateAttribute(Device device) { |
|||
return attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE, |
|||
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(DEVICE_PROVISION_STATE, PROVISIONED_STATE), |
|||
System.currentTimeMillis()))); |
|||
} |
|||
|
|||
private Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile) { |
|||
Device device = new Device(); |
|||
device.setName(provisionRequest.getDeviceName()); |
|||
device.setType(provisionRequest.getDeviceType()); |
|||
device.setTenantId(profile.getTenantId()); |
|||
return deviceService.saveDevice(device); |
|||
} |
|||
|
|||
private DeviceCredentials getDeviceCredentials(Device device, String x509CertPubKey) { |
|||
DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); |
|||
if (!StringUtils.isEmpty(x509CertPubKey)) { |
|||
credentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE); |
|||
credentials.setCredentialsValue(x509CertPubKey); |
|||
return deviceCredentialsService.updateDeviceCredentials(device.getTenantId(), credentials); |
|||
} |
|||
return credentials; |
|||
} |
|||
|
|||
private void notify(Device device, ProvisionRequest provisionRequest, String type, boolean success) { |
|||
pushProvisionEventToRuleEngine(provisionRequest, device, type); |
|||
logAction(device.getTenantId(), device, success, provisionRequest); |
|||
} |
|||
|
|||
private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) { |
|||
try { |
|||
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(request); |
|||
TbMsg msg = new TbMsg(Uuids.timeBased(), type, device.getId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode), null, null, 0L); |
|||
sendToRuleEngine(device.getTenantId(), msg, null); |
|||
} catch (JsonProcessingException | IllegalArgumentException e) { |
|||
log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e); |
|||
} |
|||
} |
|||
|
|||
private void pushDeviceCreatedEventToRuleEngine(Device device) { |
|||
try { |
|||
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); |
|||
TbMsg msg = new TbMsg(Uuids.timeBased(), DataConstants.ENTITY_CREATED, device.getId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode), null, null, 0L); |
|||
sendToRuleEngine(device.getTenantId(), msg, null); |
|||
} catch (JsonProcessingException | IllegalArgumentException e) { |
|||
log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e); |
|||
} |
|||
} |
|||
|
|||
protected void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) { |
|||
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, tbMsg.getOriginator()); |
|||
TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder().setTbMsg(TbMsg.toByteString(tbMsg)) |
|||
.setTenantIdMSB(tenantId.getId().getMostSignificantBits()) |
|||
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()).build(); |
|||
ruleEngineMsgProducer.send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), callback); |
|||
} |
|||
|
|||
private TbMsgMetaData createTbMsgMetaData(Device device) { |
|||
TbMsgMetaData metaData = new TbMsgMetaData(); |
|||
metaData.putValue("tenantId", device.getTenantId().toString()); |
|||
return metaData; |
|||
} |
|||
|
|||
private void logAction(TenantId tenantId, Device device, boolean success, ProvisionRequest provisionRequest) { |
|||
ActionType actionType = success ? ActionType.PROVISION_SUCCESS : ActionType.PROVISION_FAILURE; |
|||
auditLogService.logEntityAction(tenantId, null, null, device.getName(), device.getId(), device, actionType, null, provisionRequest); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.dao.device; |
|||
|
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import org.thingsboard.server.dao.device.provision.ProvisionRequest; |
|||
import org.thingsboard.server.dao.device.provision.ProvisionResponse; |
|||
|
|||
public interface DeviceProvisionService { |
|||
ListenableFuture<ProvisionResponse> provisionDevice(ProvisionRequest provisionRequest); |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.dao.device.provision; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileConfiguration; |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
public class ProvisionRequest { |
|||
private String deviceName; |
|||
private String deviceType; |
|||
private String x509CertPubKey; |
|||
private ProvisionDeviceProfileConfiguration credentials; |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.dao.device.provision; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
public class ProvisionResponse { |
|||
private DeviceCredentials deviceCredentials; |
|||
private ProvisionResponseStatus responseStatus; |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.dao.device.provision; |
|||
|
|||
public enum ProvisionResponseStatus { |
|||
|
|||
SUCCESS, |
|||
NOT_FOUND, |
|||
FAILURE |
|||
|
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.common.data.device.profile; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonCreator; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.DeviceProfileType; |
|||
|
|||
import java.util.Objects; |
|||
|
|||
@Data |
|||
public class ProvisionDeviceProfileConfiguration implements DeviceProfileConfiguration { |
|||
|
|||
private String provisionDeviceKey; |
|||
private String provisionDeviceSecret; |
|||
|
|||
private ProvisionRequestValidationStrategyType strategy; |
|||
|
|||
@Override |
|||
public DeviceProfileType getType() { |
|||
return DeviceProfileType.PROVISION; |
|||
} |
|||
|
|||
@JsonCreator |
|||
public ProvisionDeviceProfileConfiguration(@JsonProperty("provisionDeviceKey") String provisionProfileKey, @JsonProperty("provisionDeviceSecret") String provisionProfileSecret) { |
|||
this.provisionDeviceKey = provisionProfileKey; |
|||
this.provisionDeviceSecret = provisionProfileSecret; |
|||
} |
|||
|
|||
@Override |
|||
public boolean equals(Object o) { |
|||
if (this == o) return true; |
|||
if (o == null || getClass() != o.getClass()) return false; |
|||
ProvisionDeviceProfileConfiguration that = (ProvisionDeviceProfileConfiguration) o; |
|||
return provisionDeviceKey.equals(that.provisionDeviceKey) && |
|||
provisionDeviceSecret.equals(that.provisionDeviceSecret); |
|||
} |
|||
|
|||
@Override |
|||
public int hashCode() { |
|||
return Objects.hash(provisionDeviceKey, provisionDeviceSecret); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.common.data.device.profile; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class ProvisionRequestValidationStrategy { |
|||
private final ProvisionRequestValidationStrategyType validationStrategyType; |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.common.data.device.profile; |
|||
|
|||
public enum ProvisionRequestValidationStrategyType { |
|||
CHECK_NEW_DEVICE, CHECK_PRE_PROVISIONED_DEVICE |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.dao.device; |
|||
|
|||
public class DeviceProvisionServiceImpl { |
|||
} |
|||
Loading…
Reference in new issue