185 changed files with 5130 additions and 2495 deletions
@ -0,0 +1,24 @@ |
|||
{ |
|||
"providerId": "Apple", |
|||
"additionalInfo": null, |
|||
"accessTokenUri": "https://appleid.apple.com/auth/token", |
|||
"authorizationUri": "https://appleid.apple.com/auth/authorize?response_mode=form_post", |
|||
"scope": ["email","openid","name"], |
|||
"jwkSetUri": "https://appleid.apple.com/auth/keys", |
|||
"userInfoUri": null, |
|||
"clientAuthenticationMethod": "POST", |
|||
"userNameAttributeName": "email", |
|||
"mapperConfig": { |
|||
"type": "APPLE", |
|||
"basic": { |
|||
"emailAttributeKey": "email", |
|||
"firstNameAttributeKey": "firstName", |
|||
"lastNameAttributeKey": "lastName", |
|||
"tenantNameStrategy": "DOMAIN" |
|||
} |
|||
}, |
|||
"comment": null, |
|||
"loginButtonIcon": "apple-logo", |
|||
"loginButtonLabel": "Apple", |
|||
"helpLink": "https://developer.apple.com/sign-in-with-apple/get-started/" |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.security.auth.oauth2; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.util.LinkedMultiValueMap; |
|||
import org.springframework.util.MultiValueMap; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2Registration; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2User; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
@Service(value = "appleOAuth2ClientMapper") |
|||
@Slf4j |
|||
public class AppleOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { |
|||
|
|||
private static final String USER = "user"; |
|||
private static final String NAME = "name"; |
|||
private static final String FIRST_NAME = "firstName"; |
|||
private static final String LAST_NAME = "lastName"; |
|||
private static final String EMAIL = "email"; |
|||
|
|||
@Override |
|||
public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { |
|||
OAuth2MapperConfig config = registration.getMapperConfig(); |
|||
Map<String, Object> attributes = updateAttributesFromRequestParams(request, token.getPrincipal().getAttributes()); |
|||
String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); |
|||
OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); |
|||
|
|||
return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration); |
|||
} |
|||
|
|||
private static Map<String, Object> updateAttributesFromRequestParams(HttpServletRequest request, Map<String, Object> attributes) { |
|||
Map<String, Object> updated = attributes; |
|||
MultiValueMap<String, String> params = toMultiMap(request.getParameterMap()); |
|||
String userValue = params.getFirst(USER); |
|||
if (StringUtils.hasText(userValue)) { |
|||
JsonNode user = null; |
|||
try { |
|||
user = JacksonUtil.toJsonNode(userValue); |
|||
} catch (Exception e) {} |
|||
if (user != null) { |
|||
updated = new HashMap<>(attributes); |
|||
if (user.has(NAME)) { |
|||
JsonNode name = user.get(NAME); |
|||
if (name.isObject()) { |
|||
JsonNode firstName = name.get(FIRST_NAME); |
|||
if (firstName != null && firstName.isTextual()) { |
|||
updated.put(FIRST_NAME, firstName.asText()); |
|||
} |
|||
JsonNode lastName = name.get(LAST_NAME); |
|||
if (lastName != null && lastName.isTextual()) { |
|||
updated.put(LAST_NAME, lastName.asText()); |
|||
} |
|||
} |
|||
} |
|||
if (user.has(EMAIL)) { |
|||
JsonNode email = user.get(EMAIL); |
|||
if (email != null && email.isTextual()) { |
|||
updated.put(EMAIL, email.asText()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return updated; |
|||
} |
|||
|
|||
private static MultiValueMap<String, String> toMultiMap(Map<String, String[]> map) { |
|||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(map.size()); |
|||
map.forEach((key, values) -> { |
|||
if (values.length > 0) { |
|||
for (String value : values) { |
|||
params.add(key, value); |
|||
} |
|||
} |
|||
}); |
|||
return params; |
|||
} |
|||
} |
|||
@ -0,0 +1,211 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.queue.common; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.junit.runner.RunWith; |
|||
import org.mockito.ArgumentCaptor; |
|||
import org.mockito.Mock; |
|||
import org.mockito.junit.MockitoJUnitRunner; |
|||
import org.thingsboard.server.queue.TbQueueAdmin; |
|||
import org.thingsboard.server.queue.TbQueueConsumer; |
|||
import org.thingsboard.server.queue.TbQueueMsg; |
|||
import org.thingsboard.server.queue.TbQueueProducer; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.CountDownLatch; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.concurrent.atomic.AtomicLong; |
|||
|
|||
import static org.hamcrest.Matchers.equalTo; |
|||
import static org.hamcrest.Matchers.greaterThanOrEqualTo; |
|||
import static org.hamcrest.Matchers.is; |
|||
import static org.hamcrest.Matchers.lessThan; |
|||
import static org.mockito.ArgumentMatchers.any; |
|||
import static org.mockito.ArgumentMatchers.anyLong; |
|||
import static org.mockito.BDDMockito.willAnswer; |
|||
import static org.mockito.BDDMockito.willDoNothing; |
|||
import static org.mockito.BDDMockito.willReturn; |
|||
import static org.mockito.Mockito.RETURNS_DEEP_STUBS; |
|||
import static org.mockito.Mockito.atLeastOnce; |
|||
import static org.mockito.Mockito.mock; |
|||
import static org.mockito.Mockito.never; |
|||
import static org.mockito.Mockito.spy; |
|||
import static org.mockito.Mockito.times; |
|||
import static org.mockito.Mockito.verify; |
|||
|
|||
import static org.hamcrest.MatcherAssert.assertThat; |
|||
import static org.mockito.hamcrest.MockitoHamcrest.longThat; |
|||
|
|||
@Slf4j |
|||
@RunWith(MockitoJUnitRunner.class) |
|||
public class DefaultTbQueueRequestTemplateTest { |
|||
|
|||
@Mock |
|||
TbQueueAdmin queueAdmin; |
|||
@Mock |
|||
TbQueueProducer<TbQueueMsg> requestTemplate; |
|||
@Mock |
|||
TbQueueConsumer<TbQueueMsg> responseTemplate; |
|||
@Mock |
|||
ExecutorService executorMock; |
|||
|
|||
ExecutorService executor; |
|||
String topic = "js-responses-tb-node-0"; |
|||
long maxRequestTimeout = 10; |
|||
long maxPendingRequests = 32; |
|||
long pollInterval = 5; |
|||
|
|||
DefaultTbQueueRequestTemplate inst; |
|||
|
|||
@Before |
|||
public void setUp() throws Exception { |
|||
willReturn(topic).given(responseTemplate).getTopic(); |
|||
inst = spy(new DefaultTbQueueRequestTemplate( |
|||
queueAdmin, requestTemplate, responseTemplate, |
|||
maxRequestTimeout, maxPendingRequests, pollInterval, executorMock)); |
|||
|
|||
} |
|||
|
|||
@After |
|||
public void tearDown() throws Exception { |
|||
if (executor != null) { |
|||
executor.shutdownNow(); |
|||
} |
|||
} |
|||
|
|||
@Test |
|||
public void givenInstance_whenVerifyInitialParameters_thenOK() { |
|||
assertThat(inst.maxPendingRequests, equalTo(maxPendingRequests)); |
|||
assertThat(inst.maxRequestTimeoutNs, equalTo(TimeUnit.MILLISECONDS.toNanos(maxRequestTimeout))); |
|||
assertThat(inst.pollInterval, equalTo(pollInterval)); |
|||
assertThat(inst.executor, is(executorMock)); |
|||
assertThat(inst.stopped, is(false)); |
|||
assertThat(inst.internalExecutor, is(false)); |
|||
} |
|||
|
|||
@Test |
|||
public void givenExternalExecutor_whenInitStop_thenOK() { |
|||
inst.init(); |
|||
assertThat(inst.nextCleanupNs, equalTo(0L)); |
|||
verify(queueAdmin, times(1)).createTopicIfNotExists(topic); |
|||
verify(requestTemplate, times(1)).init(); |
|||
verify(responseTemplate, times(1)).subscribe(); |
|||
verify(executorMock, times(1)).submit(any(Runnable.class)); |
|||
|
|||
inst.stop(); |
|||
assertThat(inst.stopped, is(true)); |
|||
verify(responseTemplate, times(1)).unsubscribe(); |
|||
verify(requestTemplate, times(1)).stop(); |
|||
verify(executorMock, never()).shutdownNow(); |
|||
} |
|||
|
|||
@Test |
|||
public void givenMainLoop_whenLoopFewTimes_thenVerifyInvocationCount() throws InterruptedException { |
|||
executor = inst.createExecutor(); |
|||
CountDownLatch latch = new CountDownLatch(5); |
|||
willDoNothing().given(inst).sleep(anyLong()); |
|||
willAnswer(invocation -> { |
|||
if (latch.getCount() == 1) { |
|||
inst.stop(); //stop the loop in natural way
|
|||
} |
|||
if (latch.getCount() == 3 || latch.getCount() == 4) { |
|||
latch.countDown(); |
|||
throw new RuntimeException("test catch block"); |
|||
} |
|||
latch.countDown(); |
|||
return null; |
|||
}).given(inst).fetchAndProcessResponses(); |
|||
|
|||
executor.submit(inst::mainLoop); |
|||
latch.await(10, TimeUnit.SECONDS); |
|||
|
|||
verify(inst, times(5)).fetchAndProcessResponses(); |
|||
verify(inst, times(2)).sleep(longThat(lessThan(TimeUnit.MILLISECONDS.toNanos(inst.pollInterval)))); |
|||
} |
|||
|
|||
@Test |
|||
public void givenMessages_whenSend_thenOK() { |
|||
willDoNothing().given(inst).sendToRequestTemplate(any(), any(), any(), any()); |
|||
inst.init(); |
|||
final int msgCount = 10; |
|||
for (int i = 0; i < msgCount; i++) { |
|||
inst.send(getRequestMsgMock()); |
|||
} |
|||
assertThat(inst.pendingRequests.mappingCount(), equalTo((long) msgCount)); |
|||
verify(inst, times(msgCount)).sendToRequestTemplate(any(), any(), any(), any()); |
|||
} |
|||
|
|||
@Test |
|||
public void givenMessagesOverMaxPendingRequests_whenSend_thenImmediateFailedFutureForTheOfRequests() { |
|||
willDoNothing().given(inst).sendToRequestTemplate(any(), any(), any(), any()); |
|||
inst.init(); |
|||
int msgOverflowCount = 10; |
|||
for (int i = 0; i < inst.maxPendingRequests; i++) { |
|||
assertThat(inst.send(getRequestMsgMock()).isDone(), is(false)); //SettableFuture future - pending only
|
|||
} |
|||
for (int i = 0; i < msgOverflowCount; i++) { |
|||
assertThat("max pending requests overflow", inst.send(getRequestMsgMock()).isDone(), is(true)); //overflow, immediate failed future
|
|||
} |
|||
assertThat(inst.pendingRequests.mappingCount(), equalTo(inst.maxPendingRequests)); |
|||
verify(inst, times((int) inst.maxPendingRequests)).sendToRequestTemplate(any(), any(), any(), any()); |
|||
} |
|||
|
|||
@Test |
|||
public void givenNothing_whenSendAndFetchAndProcessResponsesWithTimeout_thenFail() { |
|||
//given
|
|||
AtomicLong currentTime = new AtomicLong(); |
|||
willAnswer(x -> { |
|||
log.info("currentTime={}", currentTime.get()); |
|||
return currentTime.get(); |
|||
}).given(inst).getCurrentClockNs(); |
|||
inst.init(); |
|||
inst.setupNextCleanup(); |
|||
willReturn(Collections.emptyList()).given(inst).doPoll(); |
|||
|
|||
//when
|
|||
long stepNs = TimeUnit.MILLISECONDS.toNanos(1); |
|||
for (long i = 0; i <= inst.maxRequestTimeoutNs * 2; i = i + stepNs) { |
|||
currentTime.addAndGet(stepNs); |
|||
assertThat(inst.send(getRequestMsgMock()).isDone(), is(false)); //SettableFuture future - pending only
|
|||
if (i % (inst.maxRequestTimeoutNs * 3 / 2) == 0) { |
|||
inst.fetchAndProcessResponses(); |
|||
} |
|||
} |
|||
|
|||
//then
|
|||
ArgumentCaptor<DefaultTbQueueRequestTemplate.ResponseMetaData> argumentCaptorResp = ArgumentCaptor.forClass(DefaultTbQueueRequestTemplate.ResponseMetaData.class); |
|||
ArgumentCaptor<UUID> argumentCaptorUUID = ArgumentCaptor.forClass(UUID.class); |
|||
ArgumentCaptor<Long> argumentCaptorLong = ArgumentCaptor.forClass(Long.class); |
|||
verify(inst, atLeastOnce()).setTimeoutException(argumentCaptorUUID.capture(), argumentCaptorResp.capture(), argumentCaptorLong.capture()); |
|||
|
|||
List<DefaultTbQueueRequestTemplate.ResponseMetaData> responseMetaDataList = argumentCaptorResp.getAllValues(); |
|||
List<Long> tickTsList = argumentCaptorLong.getAllValues(); |
|||
for (int i = 0; i < responseMetaDataList.size(); i++) { |
|||
assertThat("tickTs >= calculatedExpTime", tickTsList.get(i), greaterThanOrEqualTo(responseMetaDataList.get(i).getSubmitTime() + responseMetaDataList.get(i).getTimeout())); |
|||
} |
|||
} |
|||
|
|||
TbQueueMsg getRequestMsgMock() { |
|||
return mock(TbQueueMsg.class, RETURNS_DEEP_STUBS); |
|||
} |
|||
} |
|||
@ -0,0 +1,150 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.coap.CoAP; |
|||
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.observe.ObserveRelation; |
|||
import org.eclipse.californium.core.server.resources.CoapExchange; |
|||
import org.eclipse.californium.core.server.resources.Resource; |
|||
import org.eclipse.californium.core.server.resources.ResourceObserver; |
|||
import org.thingsboard.common.util.ThingsBoardExecutors; |
|||
import org.thingsboard.server.common.data.DeviceTransportType; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
import org.thingsboard.server.common.data.ota.OtaPackageType; |
|||
import org.thingsboard.server.common.data.security.DeviceTokenCredentials; |
|||
import org.thingsboard.server.common.transport.TransportServiceCallback; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
|
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ExecutorService; |
|||
|
|||
@Slf4j |
|||
public class OtaPackageTransportResource extends AbstractCoapTransportResource { |
|||
private static final int ACCESS_TOKEN_POSITION = 2; |
|||
|
|||
private final OtaPackageType otaPackageType; |
|||
|
|||
public OtaPackageTransportResource(CoapTransportContext ctx, OtaPackageType otaPackageType) { |
|||
super(ctx, otaPackageType.getKeyPrefix()); |
|||
this.otaPackageType = otaPackageType; |
|||
|
|||
this.setObservable(true); |
|||
} |
|||
|
|||
@Override |
|||
protected void processHandleGet(CoapExchange exchange) { |
|||
log.trace("Processing {}", exchange.advanced().getRequest()); |
|||
exchange.accept(); |
|||
Exchange advanced = exchange.advanced(); |
|||
Request request = advanced.getRequest(); |
|||
processAccessTokenRequest(exchange, request); |
|||
} |
|||
|
|||
@Override |
|||
protected void processHandlePost(CoapExchange exchange) { |
|||
exchange.respond(CoAP.ResponseCode.METHOD_NOT_ALLOWED); |
|||
} |
|||
|
|||
private void processAccessTokenRequest(CoapExchange exchange, Request request) { |
|||
Optional<DeviceTokenCredentials> credentials = decodeCredentials(request); |
|||
if (credentials.isEmpty()) { |
|||
exchange.respond(CoAP.ResponseCode.UNAUTHORIZED); |
|||
return; |
|||
} |
|||
transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(), |
|||
new CoapDeviceAuthCallback(transportContext, exchange, (sessionInfo, deviceProfile) -> { |
|||
getOtaPackageCallback(sessionInfo, exchange, otaPackageType); |
|||
})); |
|||
} |
|||
|
|||
private void getOtaPackageCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, OtaPackageType firmwareType) { |
|||
TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder() |
|||
.setTenantIdMSB(sessionInfo.getTenantIdMSB()) |
|||
.setTenantIdLSB(sessionInfo.getTenantIdLSB()) |
|||
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) |
|||
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) |
|||
.setType(firmwareType.name()).build(); |
|||
transportContext.getTransportService().process(sessionInfo, requestMsg, new OtaPackageCallback(exchange)); |
|||
} |
|||
|
|||
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(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public Resource getChild(String name) { |
|||
return this; |
|||
} |
|||
|
|||
private class OtaPackageCallback implements TransportServiceCallback<TransportProtos.GetOtaPackageResponseMsg> { |
|||
private final CoapExchange exchange; |
|||
|
|||
OtaPackageCallback(CoapExchange exchange) { |
|||
this.exchange = exchange; |
|||
} |
|||
|
|||
@Override |
|||
public void onSuccess(TransportProtos.GetOtaPackageResponseMsg msg) { |
|||
String title = exchange.getQueryParameter("title"); |
|||
String version = exchange.getQueryParameter("version"); |
|||
if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) { |
|||
String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString(); |
|||
if ((title == null || msg.getTitle().equals(title)) && (version == null || msg.getVersion().equals(version))) { |
|||
String strChunkSize = exchange.getQueryParameter("size"); |
|||
String strChunk = exchange.getQueryParameter("chunk"); |
|||
int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize); |
|||
int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk); |
|||
respondOtaPackage(exchange, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk)); |
|||
} else { |
|||
exchange.respond(CoAP.ResponseCode.BAD_REQUEST); |
|||
} |
|||
} else { |
|||
exchange.respond(CoAP.ResponseCode.NOT_FOUND); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onError(Throwable e) { |
|||
log.warn("Failed to process request", e); |
|||
exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); |
|||
} |
|||
} |
|||
|
|||
private void respondOtaPackage(CoapExchange exchange, byte[] data) { |
|||
Response response = new Response(CoAP.ResponseCode.CONTENT); |
|||
if (data != null && data.length > 0) { |
|||
response.setPayload(data); |
|||
if (exchange.getRequestOptions().getBlock2() != null) { |
|||
int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); |
|||
boolean lastFlag = data.length <= chunkSize; |
|||
response.getOptions().setBlock2(chunkSize, lastFlag, 0); |
|||
} |
|||
transportContext.getExecutor().submit(() -> exchange.respond(response)); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -1,483 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.client; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.eclipse.leshan.server.registration.Registration; |
|||
import org.thingsboard.server.common.data.ota.OtaPackageType; |
|||
import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; |
|||
import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; |
|||
import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; |
|||
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; |
|||
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; |
|||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; |
|||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; |
|||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; |
|||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; |
|||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; |
|||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.CopyOnWriteArrayList; |
|||
|
|||
import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; |
|||
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; |
|||
import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; |
|||
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; |
|||
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; |
|||
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; |
|||
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; |
|||
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; |
|||
import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RESOURCE; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_19_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_5_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_URI_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.EXECUTE; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.WRITE_REPLACE; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_NAME_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_RESULT_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UN_INSTALL_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE_STATE_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_VER_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsFwSateToFirmwareUpdateStatus; |
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.splitCamelCaseString; |
|||
|
|||
@Slf4j |
|||
public class LwM2mFwSwUpdate { |
|||
// 5/0/6 PkgName
|
|||
// 9/0/0 PkgName
|
|||
@Getter |
|||
@Setter |
|||
private volatile String currentTitle; |
|||
// 5/0/7 PkgVersion
|
|||
// 9/0/1 PkgVersion
|
|||
@Getter |
|||
@Setter |
|||
private volatile String currentVersion; |
|||
@Getter |
|||
@Setter |
|||
private volatile UUID currentId; |
|||
@Getter |
|||
@Setter |
|||
private volatile String stateUpdate; |
|||
@Getter |
|||
private String pathPackageId; |
|||
@Getter |
|||
private String pathStateId; |
|||
@Getter |
|||
private String pathResultId; |
|||
@Getter |
|||
private String pathNameId; |
|||
@Getter |
|||
private String pathVerId; |
|||
@Getter |
|||
private String pathInstallId; |
|||
@Getter |
|||
private String pathUnInstallId; |
|||
@Getter |
|||
private String wUpdate; |
|||
@Getter |
|||
@Setter |
|||
private volatile boolean infoFwSwUpdate = false; |
|||
private final OtaPackageType type; |
|||
|
|||
private final LwM2mUplinkMsgHandler handler; |
|||
|
|||
@Getter |
|||
LwM2mClient lwM2MClient; |
|||
@Getter |
|||
private final List<String> pendingInfoRequestsStart; |
|||
@Getter |
|||
@Setter |
|||
private volatile int updateStrategy; |
|||
|
|||
public LwM2mFwSwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClient lwM2MClient, OtaPackageType type, int updateStrategy) { |
|||
this.handler = handler; |
|||
this.lwM2MClient = lwM2MClient; |
|||
this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>(); |
|||
this.type = type; |
|||
this.stateUpdate = null; |
|||
this.updateStrategy = updateStrategy; |
|||
this.initPathId(); |
|||
} |
|||
|
|||
private void initPathId() { |
|||
if (FIRMWARE.equals(this.type)) { |
|||
this.pathPackageId = LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ? |
|||
FW_PACKAGE_5_ID : LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy ? |
|||
FW_PACKAGE_URI_ID : FW_PACKAGE_19_ID; |
|||
this.pathStateId = FW_STATE_ID; |
|||
this.pathResultId = FW_RESULT_ID; |
|||
this.pathNameId = FW_NAME_ID; |
|||
this.pathVerId = FW_5_VER_ID; |
|||
this.pathInstallId = FW_UPDATE_ID; |
|||
this.wUpdate = FW_UPDATE; |
|||
} else if (SOFTWARE.equals(this.type)) { |
|||
this.pathPackageId = SW_PACKAGE_ID; |
|||
this.pathStateId = SW_UPDATE_STATE_ID; |
|||
this.pathResultId = SW_RESULT_ID; |
|||
this.pathNameId = SW_NAME_ID; |
|||
this.pathVerId = SW_VER_ID; |
|||
this.pathInstallId = SW_INSTALL_ID; |
|||
this.pathUnInstallId = SW_UN_INSTALL_ID; |
|||
this.wUpdate = SW_UPDATE; |
|||
} |
|||
} |
|||
|
|||
public void initReadValue(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request, String pathIdVer) { |
|||
if (pathIdVer != null) { |
|||
this.pendingInfoRequestsStart.remove(pathIdVer); |
|||
} |
|||
if (this.pendingInfoRequestsStart.size() == 0) { |
|||
this.infoFwSwUpdate = false; |
|||
// if (!FAILED.name().equals(this.stateUpdate)) {
|
|||
boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart(handler) : |
|||
this.conditionalSwUpdateStart(handler); |
|||
if (conditionalStart) { |
|||
this.writeFwSwWare(handler, request); |
|||
} |
|||
// }
|
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Send FsSw to Lwm2mClient: |
|||
* before operation Write: fw_state = DOWNLOADING |
|||
*/ |
|||
public void writeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) { |
|||
if (this.currentId != null) { |
|||
this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name(); |
|||
this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LWM2M_INFO, null); |
|||
String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); |
|||
String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LWM2M_INFO, |
|||
LwM2mOperationType.FW_UPDATE.name(), this.pathPackageId); |
|||
handler.logToTelemetry(fwMsg, lwM2MClient.getRegistration().getId()); |
|||
log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer); |
|||
if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy) { |
|||
int chunkSize = 0; |
|||
int chunk = 0; |
|||
byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); |
|||
|
|||
TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(firmwareChunk).timeout(handler.config.getTimeout()).build(); |
|||
request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); |
|||
} else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { |
|||
String apiFont = "coap://176.36.143.9:5685"; |
|||
String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RESOURCE + "/" + this.currentId.toString(); |
|||
log.warn("89) coapUri: [{}]", uri); |
|||
//TODO: user this.rpcRequest???
|
|||
TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(uri).timeout(handler.config.getTimeout()).build(); |
|||
request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); |
|||
} else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { |
|||
|
|||
} |
|||
} else { |
|||
String msgError = "FirmWareId is null."; |
|||
log.warn("6) [{}]", msgError); |
|||
// if (this.rpcRequest != null) {
|
|||
// TODO: refactor.
|
|||
// handler.sentRpcResponse(this.rpcRequest, CONTENT.name(), msgError, LOG_LW2M_ERROR);
|
|||
// }
|
|||
log.error(msgError); |
|||
this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LWM2M_ERROR, msgError); |
|||
} |
|||
} |
|||
|
|||
public void sendLogs(DefaultLwM2MUplinkMsgHandler handler, String typeOper, String typeInfo, String msgError) { |
|||
// this.sendSateOnThingsBoard(handler);
|
|||
String msg = String.format("%s: %s, %s, pkgVer: %s: pkgName - %s state - %s.", |
|||
typeInfo, this.wUpdate, typeOper, this.currentVersion, this.currentTitle, this.stateUpdate); |
|||
if (LOG_LWM2M_ERROR.equals(typeInfo)) { |
|||
msg = String.format("%s Error: %s", msg, msgError); |
|||
} |
|||
handler.logToTelemetry(lwM2MClient, msg); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* After inspection Update Result |
|||
* fw_state/sw_state = UPDATING |
|||
* send execute |
|||
*/ |
|||
public void executeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) { |
|||
this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_INFO, null); |
|||
//TODO: user this.rpcRequest???
|
|||
TbLwM2MExecuteRequest downlink = TbLwM2MExecuteRequest.builder().versionedId(pathInstallId).timeout(handler.config.getTimeout()).build(); |
|||
request.sendExecuteRequest(lwM2MClient, downlink, new TbLwM2MExecuteCallback(handler, lwM2MClient, pathInstallId)); |
|||
} |
|||
|
|||
/** |
|||
* Firmware start: Check if the version has changed and launch a new update. |
|||
* -ObjectId 5, Binary or ObjectId 5, URI |
|||
* -- If the result of the update - errors (more than 1) - This means that the previous. the update failed. |
|||
* - We launch the update regardless of the state of the firmware and its version. |
|||
* -- If the result of the update - errors (more than 1) - This means that the previous. the update failed. |
|||
* * ObjectId 5, Binary |
|||
* -- If the result of the update is not errors (equal to 1 or 0) and ver in Object 5 is not empty - it means that the previous update has passed. |
|||
* Compare current versions by equals. |
|||
* * ObjectId 5, URI |
|||
* -- If the result of the update is not errors (equal to 1 or 0) and ver in Object 5 is not empty - it means that the previous update has passed. |
|||
* Compare current versions by contains. |
|||
*/ |
|||
private boolean conditionalFwUpdateStart(DefaultLwM2MUplinkMsgHandler handler) { |
|||
Long updateResultFw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); |
|||
String ver5 = (String) this.lwM2MClient.getResourceValue(null, this.pathVerId); |
|||
String pathName = (String) this.lwM2MClient.getResourceValue(null, this.pathNameId); |
|||
String ver3 = (String) this.lwM2MClient.getResourceValue(null, FW_3_VER_ID); |
|||
// #1/#2
|
|||
String fwMsg = null; |
|||
if ((this.currentVersion != null && ( |
|||
ver5 != null && ver5.equals(this.currentVersion) || |
|||
ver3 != null && ver3.contains(this.currentVersion) |
|||
)) || |
|||
(this.currentTitle != null && pathName != null && this.currentTitle.equals(pathName))) { |
|||
fwMsg = String.format("%s: The update was interrupted. The device has the same version: %s.", LOG_LWM2M_ERROR, |
|||
this.currentVersion); |
|||
} else if (updateResultFw != null && updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { |
|||
fwMsg = String.format("%s: The update was interrupted. The device has the status UpdateResult: error (%d).", LOG_LWM2M_ERROR, |
|||
updateResultFw); |
|||
} |
|||
if (fwMsg != null) { |
|||
handler.logToTelemetry(fwMsg, lwM2MClient.getRegistration().getId()); |
|||
return false; |
|||
} else { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* Before operation Execute inspection Update Result : |
|||
* 0 - Initial value |
|||
*/ |
|||
public boolean conditionalFwExecuteStart() { |
|||
Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); |
|||
return LwM2mTransportUtil.UpdateResultFw.INITIAL.code == updateResult; |
|||
} |
|||
|
|||
/** |
|||
* After operation Execute success inspection Update Result : |
|||
* 1 - "Firmware updated successfully" |
|||
*/ |
|||
public boolean conditionalFwExecuteAfterSuccess() { |
|||
Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); |
|||
return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code == updateResult; |
|||
} |
|||
|
|||
/** |
|||
* After operation Execute success inspection Update Result : |
|||
* > 1 error: "Firmware updated successfully" |
|||
*/ |
|||
public boolean conditionalFwExecuteAfterError() { |
|||
Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); |
|||
return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code < updateResult; |
|||
} |
|||
|
|||
/** |
|||
* Software start |
|||
* - If Update Result -errors (equal or more than 50) - This means that the previous. the update failed. |
|||
* * - We launch the update regardless of the state of the firmware and its version. |
|||
* - If Update Result is not errors (less than 50) and ver is not empty - This means that before. the update has passed. |
|||
* - If Update Result is not errors and ver is empty - This means that there was no update yet or before. UnInstall update |
|||
* - If Update Result is not errors and ver is not empty - This means that before unInstall update |
|||
* * - Check if the version has changed and launch a new update. |
|||
*/ |
|||
private boolean conditionalSwUpdateStart(DefaultLwM2MUplinkMsgHandler handler) { |
|||
Long updateResultSw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); |
|||
// #1/#2
|
|||
return updateResultSw >= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code || |
|||
( |
|||
(updateResultSw <= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code |
|||
) && |
|||
( |
|||
(this.currentVersion != null && !this.currentVersion.equals(this.lwM2MClient.getResourceValue(null, this.pathVerId))) || |
|||
(this.currentTitle != null && !this.currentTitle.equals(this.lwM2MClient.getResourceValue(null, this.pathNameId))) |
|||
) |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Before operation Execute inspection Update Result : |
|||
* 3 - Successfully Downloaded and package integrity verified |
|||
*/ |
|||
public boolean conditionalSwUpdateExecute() { |
|||
Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); |
|||
return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_DOWNLOADED_VERIFIED.code == updateResult; |
|||
} |
|||
|
|||
/** |
|||
* After finish operation Execute (success): |
|||
* -- inspection Update Result: |
|||
* ---- FW если Update Result == 1 ("Firmware updated successfully") или SW если Update Result == 2 ("Software successfully installed.") |
|||
* -- fw_state/sw_state = UPDATED |
|||
* <p> |
|||
* After finish operation Execute (error): |
|||
* -- inspection updateResult and send to thingsboard info about error |
|||
* --- send to telemetry ( key - this is name Update Result in model) ( |
|||
* -- fw_state/sw_state = FAILED |
|||
*/ |
|||
public void finishFwSwUpdate(DefaultLwM2MUplinkMsgHandler handler, boolean success) { |
|||
Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); |
|||
String value = FIRMWARE.equals(this.type) ? LwM2mTransportUtil.UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type : |
|||
LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type; |
|||
String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId)); |
|||
if (success) { |
|||
this.stateUpdate = OtaPackageUpdateStatus.UPDATED.name(); |
|||
this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_INFO, null); |
|||
} else { |
|||
this.stateUpdate = OtaPackageUpdateStatus.FAILED.name(); |
|||
this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_ERROR, value); |
|||
} |
|||
handler.helper.sendParametersOnThingsboardTelemetry( |
|||
handler.helper.getKvStringtoThingsboard(key, value), this.lwM2MClient.getSession()); |
|||
} |
|||
|
|||
/** |
|||
* After operation Execute success inspection Update Result : |
|||
* 2 - "Software successfully installed." |
|||
*/ |
|||
public boolean conditionalSwExecuteAfterSuccess() { |
|||
Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); |
|||
return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_INSTALLED.code == updateResult; |
|||
} |
|||
|
|||
/** |
|||
* After operation Execute success inspection Update Result : |
|||
* >= 50 - error "NOT_ENOUGH_STORAGE" |
|||
*/ |
|||
public boolean conditionalSwExecuteAfterError() { |
|||
Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); |
|||
return LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code <= updateResult; |
|||
} |
|||
|
|||
// private void observeStateUpdate(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) {
|
|||
// request.sendAllRequest(lwM2MClient,
|
|||
// convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE,
|
|||
// null, null, 0, null);
|
|||
// request.sendAllRequest(lwM2MClient,
|
|||
// convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE,
|
|||
// null, null, 0, null);
|
|||
// }
|
|||
|
|||
public void sendSateOnThingsBoard(DefaultLwM2MUplinkMsgHandler handler) { |
|||
if (StringUtils.trimToNull(this.stateUpdate) != null) { |
|||
List<TransportProtos.KeyValueProto> result = new ArrayList<>(); |
|||
TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(getAttributeKey(this.type, STATE)); |
|||
kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(stateUpdate); |
|||
result.add(kvProto.build()); |
|||
handler.helper.sendParametersOnThingsboardTelemetry(result, |
|||
handler.getSessionInfoOrCloseSession(this.lwM2MClient.getRegistration())); |
|||
} |
|||
} |
|||
|
|||
public void sendReadObserveInfo(LwM2mDownlinkMsgHandler request) { |
|||
this.infoFwSwUpdate = true; |
|||
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( |
|||
this.pathStateId, this.lwM2MClient.getRegistration())); |
|||
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( |
|||
this.pathResultId, this.lwM2MClient.getRegistration())); |
|||
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( |
|||
FW_3_VER_ID, this.lwM2MClient.getRegistration())); |
|||
if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy || |
|||
LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy || |
|||
SOFTWARE.equals(this.type)) { |
|||
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( |
|||
this.pathVerId, this.lwM2MClient.getRegistration())); |
|||
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( |
|||
this.pathNameId, this.lwM2MClient.getRegistration())); |
|||
} |
|||
this.pendingInfoRequestsStart.forEach(versionedId -> { |
|||
TbLwM2MObserveRequest downlink = TbLwM2MObserveRequest.builder().versionedId(versionedId).build(); |
|||
request.sendObserveRequest(this.lwM2MClient, downlink, new TbLwM2MObserveCallback(handler, lwM2MClient, versionedId)); |
|||
}); |
|||
|
|||
} |
|||
|
|||
/** |
|||
* Before operation Execute (FwUpdate) inspection Update Result : |
|||
* - after finished operation Write result: success (FwUpdate): fw_state = DOWNLOADED |
|||
* - before start operation Execute (FwUpdate) Update Result = 0 - Initial value |
|||
* - start Execute (FwUpdate) |
|||
* After finished operation Execute (FwUpdate) inspection Update Result : |
|||
* - after start operation Execute (FwUpdate): fw_state = UPDATING |
|||
* - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully") |
|||
* - finished operation Execute (FwUpdate) |
|||
*/ |
|||
public void updateStateOta(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request, |
|||
Registration registration, String path, int value) { |
|||
if (OBJ_5_BINARY.code == this.getUpdateStrategy()) { |
|||
if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { |
|||
if (DOWNLOADED.name().equals(this.getStateUpdate()) |
|||
&& this.conditionalFwExecuteStart()) { |
|||
this.executeFwSwWare(handler, request); |
|||
} else if (UPDATING.name().equals(this.getStateUpdate()) |
|||
&& this.conditionalFwExecuteAfterSuccess()) { |
|||
this.finishFwSwUpdate(handler, true); |
|||
} else if (UPDATING.name().equals(this.getStateUpdate()) |
|||
&& this.conditionalFwExecuteAfterError()) { |
|||
this.finishFwSwUpdate(handler, false); |
|||
} |
|||
} |
|||
} else if (OBJ_5_TEMP_URL.code == this.getUpdateStrategy()) { |
|||
if (this.currentId != null && (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { |
|||
String state = equalsFwSateToFirmwareUpdateStatus(LwM2mTransportUtil.StateFw.fromStateFwByCode(value)).name(); |
|||
if (StringUtils.isNotEmpty(state) && !FAILED.name().equals(this.stateUpdate) && !state.equals(this.stateUpdate)) { |
|||
this.stateUpdate = state; |
|||
this.sendSateOnThingsBoard(handler); |
|||
} |
|||
if (value == LwM2mTransportUtil.StateFw.DOWNLOADED.code) { |
|||
this.executeFwSwWare(handler, request); |
|||
} |
|||
handler.firmwareUpdateState.put(lwM2MClient.getEndpoint(), value); |
|||
} |
|||
if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { |
|||
if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.INITIAL.code) { |
|||
this.setStateUpdate(INITIATED.name()); |
|||
} else if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { |
|||
this.setStateUpdate(UPDATED.name()); |
|||
} else if (value > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { |
|||
this.setStateUpdate(FAILED.name()); |
|||
} |
|||
this.sendSateOnThingsBoard(handler); |
|||
} |
|||
} else if (OBJ_19_BINARY.code == this.getUpdateStrategy()) { |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.common; |
|||
|
|||
import org.thingsboard.common.util.ThingsBoardExecutors; |
|||
|
|||
import javax.annotation.PreDestroy; |
|||
import java.util.concurrent.ExecutorService; |
|||
|
|||
public abstract class LwM2MExecutorAwareService { |
|||
|
|||
protected ExecutorService executor; |
|||
|
|||
protected abstract int getExecutorSize(); |
|||
|
|||
protected abstract String getExecutorName(); |
|||
|
|||
protected void init() { |
|||
this.executor = ThingsBoardExecutors.newWorkStealingPool(getExecutorSize(), getExecutorName()); |
|||
} |
|||
|
|||
public void destroy() { |
|||
if (executor != null) { |
|||
executor.shutdownNow(); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.downlink; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
|||
import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; |
|||
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; |
|||
|
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; |
|||
|
|||
@Slf4j |
|||
public abstract class TbLwM2MUplinkTargetedCallback<R, T> extends TbLwM2MTargetedCallback<R, T> { |
|||
|
|||
protected LwM2mUplinkMsgHandler handler; |
|||
|
|||
public TbLwM2MUplinkTargetedCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String versionedId) { |
|||
super(logService, client, versionedId); |
|||
this.handler = handler; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.log; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; |
|||
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; |
|||
|
|||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; |
|||
|
|||
@Slf4j |
|||
@Service |
|||
@TbLwM2mTransportComponent |
|||
@RequiredArgsConstructor |
|||
public class DefaultLwM2MTelemetryLogService implements LwM2MTelemetryLogService { |
|||
|
|||
private final LwM2mClientContext clientContext; |
|||
private final LwM2mTransportServerHelper helper; |
|||
|
|||
/** |
|||
* @param logMsg - text msg |
|||
* @param registrationId - Id of Registration LwM2M Client |
|||
*/ |
|||
@Override |
|||
public void log(String registrationId, String logMsg) { |
|||
log(clientContext.getClientByRegistrationId(registrationId), logMsg); |
|||
} |
|||
|
|||
@Override |
|||
public void log(LwM2mClient client, String logMsg) { |
|||
if (logMsg != null && client != null && client.getSession() != null) { |
|||
if (logMsg.length() > 1024) { |
|||
logMsg = logMsg.substring(0, 1024); |
|||
} |
|||
this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), client.getSession()); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.log; |
|||
|
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
|||
|
|||
public interface LwM2MTelemetryLogService { |
|||
|
|||
void log(LwM2mClient client, String msg); |
|||
|
|||
void log(String registrationId, String msg); |
|||
|
|||
} |
|||
@ -0,0 +1,111 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.ota; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
import org.thingsboard.server.common.data.ota.OtaPackageType; |
|||
import org.thingsboard.server.transport.lwm2m.server.ota.firmware.LwM2MFirmwareUpdateStrategy; |
|||
import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult; |
|||
import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateState; |
|||
import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy; |
|||
|
|||
import java.util.Optional; |
|||
|
|||
@Data |
|||
public class LwM2MClientOtaInfo { |
|||
|
|||
private final String endpoint; |
|||
private final OtaPackageType type; |
|||
|
|||
private String baseUrl; |
|||
|
|||
private boolean targetFetchFailure; |
|||
private String targetName; |
|||
private String targetVersion; |
|||
private String targetUrl; |
|||
|
|||
private boolean currentFetchFailure; |
|||
private String currentName; |
|||
private String currentVersion3; |
|||
private String currentVersion5; |
|||
private Integer deliveryMethod; |
|||
|
|||
//TODO: use value from device if applicable;
|
|||
private LwM2MFirmwareUpdateStrategy fwStrategy; |
|||
private LwM2MSoftwareUpdateStrategy swStrategy; |
|||
private FirmwareUpdateState updateState; |
|||
private FirmwareUpdateResult updateResult; |
|||
|
|||
private String failedPackageId; |
|||
private int retryAttempts; |
|||
|
|||
public LwM2MClientOtaInfo(String endpoint, OtaPackageType type, Integer strategyCode, String baseUrl) { |
|||
this.endpoint = endpoint; |
|||
this.type = type; |
|||
this.fwStrategy = LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(strategyCode); |
|||
this.baseUrl = baseUrl; |
|||
} |
|||
|
|||
public void updateTarget(String targetName, String targetVersion, Optional<String> newFirmwareUrl) { |
|||
this.targetName = targetName; |
|||
this.targetVersion = targetVersion; |
|||
this.targetUrl = newFirmwareUrl.orElse(null); |
|||
} |
|||
|
|||
public boolean isUpdateRequired() { |
|||
if (StringUtils.isEmpty(targetName) || StringUtils.isEmpty(targetVersion) || !isSupported()) { |
|||
return false; |
|||
} else { |
|||
String targetPackageId = getPackageId(targetName, targetVersion); |
|||
String currentPackageIdUsingObject5 = getPackageId(currentName, currentVersion5); |
|||
if (StringUtils.isNotEmpty(failedPackageId) && failedPackageId.equals(targetPackageId)) { |
|||
return false; |
|||
} else { |
|||
if (targetPackageId.equals(currentPackageIdUsingObject5)) { |
|||
return false; |
|||
} else if (StringUtils.isNotEmpty(currentVersion3)) { |
|||
return !currentVersion3.contains(targetPackageId); |
|||
} else { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public boolean isSupported() { |
|||
return StringUtils.isNotEmpty(currentName) || StringUtils.isNotEmpty(currentVersion5) || StringUtils.isNotEmpty(currentVersion3); |
|||
} |
|||
|
|||
public void setUpdateResult(FirmwareUpdateResult updateResult) { |
|||
this.updateResult = updateResult; |
|||
switch (updateResult) { |
|||
case INITIAL: |
|||
break; |
|||
case UPDATE_SUCCESSFULLY: |
|||
retryAttempts = 0; |
|||
break; |
|||
default: |
|||
failedPackageId = getPackageId(targetName, targetVersion); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
private static String getPackageId(String name, String version) { |
|||
return (StringUtils.isNotEmpty(name) ? name : "") + (StringUtils.isNotEmpty(version) ? version : ""); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.ota.firmware; |
|||
|
|||
public enum FirmwareDeliveryMethod { |
|||
PULL(0, "Pull only"), |
|||
PUSH(1, "Push only"), |
|||
BOTH(2, "Push or Push"); |
|||
|
|||
public int code; |
|||
public String type; |
|||
|
|||
FirmwareDeliveryMethod(int code, String type) { |
|||
this.code = code; |
|||
this.type = type; |
|||
} |
|||
|
|||
public static FirmwareDeliveryMethod fromStateFwByType(String type) { |
|||
for (FirmwareDeliveryMethod to : FirmwareDeliveryMethod.values()) { |
|||
if (to.type.equals(type)) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported FW delivery type : %s", type)); |
|||
} |
|||
|
|||
public static FirmwareDeliveryMethod fromStateFwByCode(int code) { |
|||
for (FirmwareDeliveryMethod to : FirmwareDeliveryMethod.values()) { |
|||
if (to.code == code) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported FW delivery code : %s", code)); |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.ota.firmware; |
|||
|
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* FW Update Result |
|||
* 0: Initial value. Once the updating process is initiated (Download /Update), this Resource MUST be reset to Initial value. |
|||
* 1: Firmware updated successfully. |
|||
* 2: Not enough flash memory for the new firmware package. |
|||
* 3: Out of RAM during downloading process. |
|||
* 4: Connection lost during downloading process. |
|||
* 5: Integrity check failure for new downloaded package. |
|||
* 6: Unsupported package type. |
|||
* 7: Invalid URI. |
|||
* 8: Firmware update failed. |
|||
* 9: Unsupported protocol. |
|||
*/ |
|||
public enum FirmwareUpdateResult { |
|||
INITIAL(0, "Initial value", false), |
|||
UPDATE_SUCCESSFULLY(1, "Firmware updated successfully", false), |
|||
NOT_ENOUGH(2, "Not enough flash memory for the new firmware package", false), |
|||
OUT_OFF_MEMORY(3, "Out of RAM during downloading process", false), |
|||
CONNECTION_LOST(4, "Connection lost during downloading process", true), |
|||
INTEGRITY_CHECK_FAILURE(5, "Integrity check failure for new downloaded package", true), |
|||
UNSUPPORTED_TYPE(6, "Unsupported package type", false), |
|||
INVALID_URI(7, "Invalid URI", false), |
|||
UPDATE_FAILED(8, "Firmware update failed", false), |
|||
UNSUPPORTED_PROTOCOL(9, "Unsupported protocol", false); |
|||
|
|||
@Getter |
|||
private int code; |
|||
@Getter |
|||
private String type; |
|||
@Getter |
|||
private boolean again; |
|||
|
|||
FirmwareUpdateResult(int code, String type, boolean isAgain) { |
|||
this.code = code; |
|||
this.type = type; |
|||
this.again = isAgain; |
|||
} |
|||
|
|||
public static FirmwareUpdateResult fromUpdateResultFwByType(String type) { |
|||
for (FirmwareUpdateResult to : FirmwareUpdateResult.values()) { |
|||
if (to.type.equals(type)) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported FW Update Result type : %s", type)); |
|||
} |
|||
|
|||
public static FirmwareUpdateResult fromUpdateResultFwByCode(int code) { |
|||
for (FirmwareUpdateResult to : FirmwareUpdateResult.values()) { |
|||
if (to.code == code) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported FW Update Result code : %s", code)); |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.ota.firmware; |
|||
|
|||
/** |
|||
* /** State R |
|||
* 0: Idle (before downloading or after successful updating) |
|||
* 1: Downloading (The data sequence is on the way) |
|||
* 2: Downloaded |
|||
* 3: Updating |
|||
*/ |
|||
public enum FirmwareUpdateState { |
|||
IDLE(0, "Idle"), |
|||
DOWNLOADING(1, "Downloading"), |
|||
DOWNLOADED(2, "Downloaded"), |
|||
UPDATING(3, "Updating"); |
|||
|
|||
public int code; |
|||
public String type; |
|||
|
|||
FirmwareUpdateState(int code, String type) { |
|||
this.code = code; |
|||
this.type = type; |
|||
} |
|||
|
|||
public static FirmwareUpdateState fromStateFwByType(String type) { |
|||
for (FirmwareUpdateState to : FirmwareUpdateState.values()) { |
|||
if (to.type.equals(type)) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type)); |
|||
} |
|||
|
|||
public static FirmwareUpdateState fromStateFwByCode(int code) { |
|||
for (FirmwareUpdateState to : FirmwareUpdateState.values()) { |
|||
if (to.code == code) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported FW State code : %s", code)); |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.ota.firmware; |
|||
|
|||
public enum LwM2MFirmwareUpdateStrategy { |
|||
OBJ_5_BINARY(1, "ObjectId 5, Binary"), |
|||
OBJ_5_TEMP_URL(2, "ObjectId 5, URI"), |
|||
OBJ_19_BINARY(3, "ObjectId 19, Binary"); |
|||
|
|||
public int code; |
|||
public String type; |
|||
|
|||
LwM2MFirmwareUpdateStrategy(int code, String type) { |
|||
this.code = code; |
|||
this.type = type; |
|||
} |
|||
|
|||
public static LwM2MFirmwareUpdateStrategy fromStrategyFwByType(String type) { |
|||
for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { |
|||
if (to.type.equals(type)) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type)); |
|||
} |
|||
|
|||
public static LwM2MFirmwareUpdateStrategy fromStrategyFwByCode(int code) { |
|||
for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { |
|||
if (to.code == code) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported FW Strategy code : %s", code)); |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.ota.software; |
|||
|
|||
public enum LwM2MSoftwareUpdateStrategy { |
|||
BINARY(1, "ObjectId 9, Binary"), |
|||
TEMP_URL(2, "ObjectId 9, URI"); |
|||
|
|||
public int code; |
|||
public String type; |
|||
|
|||
LwM2MSoftwareUpdateStrategy(int code, String type) { |
|||
this.code = code; |
|||
this.type = type; |
|||
} |
|||
|
|||
public static LwM2MSoftwareUpdateStrategy fromStrategySwByType(String type) { |
|||
for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { |
|||
if (to.type.equals(type)) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported SW Strategy type : %s", type)); |
|||
} |
|||
|
|||
public static LwM2MSoftwareUpdateStrategy fromStrategySwByCode(int code) { |
|||
for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { |
|||
if (to.code == code) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported SW Strategy code : %s", code)); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.ota.software; |
|||
|
|||
/** |
|||
* SW Update Result |
|||
* Contains the result of downloading or installing/uninstalling the software |
|||
* 0: Initial value. |
|||
* - Prior to download any new package in the Device, Update Result MUST be reset to this initial value. |
|||
* - One side effect of executing the Uninstall resource is to reset Update Result to this initial value "0". |
|||
* 1: Downloading. |
|||
* - The package downloading process is on-going. |
|||
* 2: Software successfully installed. |
|||
* 3: Successfully Downloaded and package integrity verified |
|||
* (( 4-49, for expansion, of other scenarios)) |
|||
* ** Failed |
|||
* 50: Not enough storage for the new software package. |
|||
* 51: Out of memory during downloading process. |
|||
* 52: Connection lost during downloading process. |
|||
* 53: Package integrity check failure. |
|||
* 54: Unsupported package type. |
|||
* 56: Invalid URI |
|||
* 57: Device defined update error |
|||
* 58: Software installation failure |
|||
* 59: Uninstallation Failure during forUpdate(arg=0) |
|||
* 60-200 : (for expansion, selection to be in blocks depending on new introduction of features) |
|||
* This Resource MAY be reported by sending Observe operation. |
|||
*/ |
|||
public enum SoftwareUpdateResult { |
|||
INITIAL(0, "Initial value", false), |
|||
DOWNLOADING(1, "Downloading", false), |
|||
SUCCESSFULLY_INSTALLED(2, "Software successfully installed", false), |
|||
SUCCESSFULLY_DOWNLOADED_VERIFIED(3, "Successfully Downloaded and package integrity verified", false), |
|||
NOT_ENOUGH_STORAGE(50, "Not enough storage for the new software package", true), |
|||
OUT_OFF_MEMORY(51, "Out of memory during downloading process", true), |
|||
CONNECTION_LOST(52, "Connection lost during downloading process", false), |
|||
PACKAGE_CHECK_FAILURE(53, "Package integrity check failure.", false), |
|||
UNSUPPORTED_PACKAGE_TYPE(54, "Unsupported package type", false), |
|||
INVALID_URI(56, "Invalid URI", true), |
|||
UPDATE_ERROR(57, "Device defined update error", true), |
|||
INSTALL_FAILURE(58, "Software installation failure", true), |
|||
UN_INSTALL_FAILURE(59, "Uninstallation Failure during forUpdate(arg=0)", true); |
|||
|
|||
public int code; |
|||
public String type; |
|||
public boolean isAgain; |
|||
|
|||
SoftwareUpdateResult(int code, String type, boolean isAgain) { |
|||
this.code = code; |
|||
this.type = type; |
|||
this.isAgain = isAgain; |
|||
} |
|||
|
|||
public static SoftwareUpdateResult fromUpdateResultSwByType(String type) { |
|||
for (SoftwareUpdateResult to : SoftwareUpdateResult.values()) { |
|||
if (to.type.equals(type)) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported SW Update Result type : %s", type)); |
|||
} |
|||
|
|||
public static SoftwareUpdateResult fromUpdateResultSwByCode(int code) { |
|||
for (SoftwareUpdateResult to : SoftwareUpdateResult.values()) { |
|||
if (to.code == code) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported SW Update Result code : %s", code)); |
|||
} |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.ota.software; |
|||
|
|||
/** |
|||
* SW Update State R |
|||
* 0: INITIAL Before downloading. (see 5.1.2.1) |
|||
* 1: DOWNLOAD STARTED The downloading process has started and is on-going. (see 5.1.2.2) |
|||
* 2: DOWNLOADED The package has been completely downloaded (see 5.1.2.3) |
|||
* 3: DELIVERED In that state, the package has been correctly downloaded and is ready to be installed. (see 5.1.2.4) |
|||
* If executing the Install Resource failed, the state remains at DELIVERED. |
|||
* If executing the Install Resource was successful, the state changes from DELIVERED to INSTALLED. |
|||
* After executing the UnInstall Resource, the state changes to INITIAL. |
|||
* 4: INSTALLED |
|||
*/ |
|||
public enum SoftwareUpdateState { |
|||
INITIAL(0, "Initial"), |
|||
DOWNLOAD_STARTED(1, "DownloadStarted"), |
|||
DOWNLOADED(2, "Downloaded"), |
|||
DELIVERED(3, "Delivered"), |
|||
INSTALLED(4, "Installed"); |
|||
|
|||
public int code; |
|||
public String type; |
|||
|
|||
SoftwareUpdateState(int code, String type) { |
|||
this.code = code; |
|||
this.type = type; |
|||
} |
|||
|
|||
public static SoftwareUpdateState fromUpdateStateSwByType(String type) { |
|||
for (SoftwareUpdateState to : SoftwareUpdateState.values()) { |
|||
if (to.type.equals(type)) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", type)); |
|||
} |
|||
|
|||
public static SoftwareUpdateState fromUpdateStateSwByCode(int code) { |
|||
for (SoftwareUpdateState to : SoftwareUpdateState.values()) { |
|||
if (to.code == code) { |
|||
return to; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", code)); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m.server.uplink; |
|||
|
|||
public enum LwM2mTypeServer { |
|||
BOOTSTRAP(0, "bootstrap"), |
|||
CLIENT(1, "client"); |
|||
|
|||
public int code; |
|||
public String type; |
|||
|
|||
LwM2mTypeServer(int code, String type) { |
|||
this.code = code; |
|||
this.type = type; |
|||
} |
|||
|
|||
public static LwM2mTypeServer fromLwM2mTypeServer(String type) { |
|||
for (LwM2mTypeServer sm : LwM2mTypeServer.values()) { |
|||
if (sm.type.equals(type)) { |
|||
return sm; |
|||
} |
|||
} |
|||
throw new IllegalArgumentException(String.format("Unsupported typeServer type : %d", type)); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.common.util; |
|||
|
|||
import org.springframework.util.StopWatch; |
|||
|
|||
/** |
|||
* Utility method that extends Spring Framework StopWatch |
|||
* It is a MONOTONIC time stopwatch. |
|||
* It is a replacement for any measurements with a wall-clock like System.currentTimeMillis() |
|||
* It is not affected by leap second, day-light saving and wall-clock adjustments by manual or network time synchronization |
|||
* The main features is a single call for common use cases: |
|||
* - create and start: TbStopWatch sw = TbStopWatch.startNew() |
|||
* - stop and get: sw.stopAndGetTotalTimeMillis() or sw.stopAndGetLastTaskTimeMillis() |
|||
* */ |
|||
public class TbStopWatch extends StopWatch { |
|||
|
|||
public static TbStopWatch startNew(){ |
|||
TbStopWatch stopWatch = new TbStopWatch(); |
|||
stopWatch.start(); |
|||
return stopWatch; |
|||
} |
|||
|
|||
public long stopAndGetTotalTimeMillis(){ |
|||
stop(); |
|||
return getTotalTimeMillis(); |
|||
} |
|||
|
|||
public long stopAndGetTotalTimeNanos(){ |
|||
stop(); |
|||
return getLastTaskTimeNanos(); |
|||
} |
|||
|
|||
public long stopAndGetLastTaskTimeMillis(){ |
|||
stop(); |
|||
return getLastTaskTimeMillis(); |
|||
} |
|||
|
|||
public long stopAndGetLastTaskTimeNanos(){ |
|||
stop(); |
|||
return getLastTaskTimeNanos(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.cache; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.common.util.AbstractListeningExecutor; |
|||
|
|||
@Component |
|||
public class CacheExecutorService extends AbstractListeningExecutor { |
|||
|
|||
@Value("${cache.maximumPoolSize}") |
|||
private int poolSize; |
|||
|
|||
@Override |
|||
protected int getThreadPollSize() { |
|||
return poolSize; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/* |
|||
* Copyright © 2016-2021 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. |
|||
*/ |
|||
const config = require('config'), |
|||
logger = require('../config/logger')._logger('httpServer'), |
|||
express = require('express'); |
|||
|
|||
const httpPort = Number(config.get('http_port')); |
|||
|
|||
const app = express(); |
|||
|
|||
app.get('/livenessProbe', async (req, res) => { |
|||
const date = new Date(); |
|||
const message = { now: date.toISOString() }; |
|||
res.send(message); |
|||
}) |
|||
|
|||
app.listen(httpPort, () => logger.info(`Started http endpoint on port ${httpPort}. Please, use /livenessProbe !`)) |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue