committed by
GitHub
381 changed files with 11590 additions and 2845 deletions
@ -0,0 +1,54 @@ |
|||
# |
|||
# Copyright © 2016-2024 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. |
|||
# |
|||
|
|||
name: License header format |
|||
|
|||
on: |
|||
push: |
|||
branches: |
|||
- 'master' |
|||
- 'develop/3*' |
|||
- 'hotfix/3*' |
|||
|
|||
jobs: |
|||
license-format: |
|||
runs-on: ubuntu-latest |
|||
|
|||
steps: |
|||
- name: Checkout Repository |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Set up JDK |
|||
uses: actions/setup-java@v4 |
|||
with: |
|||
distribution: 'corretto' # https://github.com/actions/setup-java?tab=readme-ov-file#supported-distributions |
|||
java-version: '21' |
|||
cache: 'maven' # https://github.com/actions/setup-java?tab=readme-ov-file#caching-sbt-dependencies |
|||
|
|||
- name: License header format |
|||
run: mvn -T 1C license:format |
|||
|
|||
- name: License header format (msa/black-box-tests/) |
|||
run: mvn -T 1C license:format -f msa/black-box-tests/ |
|||
|
|||
- name: Set Git user information |
|||
run: | |
|||
git config user.name "ThingsBoard Bot" |
|||
git config user.email "noreply@thingsboard.io" |
|||
|
|||
- name: Check and push changes |
|||
run: | |
|||
git diff --exit-code || git commit -am "License header format" && git push |
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,29 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.edge.rpc.constructor.alarm; |
|||
|
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.common.data.alarm.AlarmComment; |
|||
import org.thingsboard.server.gen.edge.v1.AlarmCommentUpdateMsg; |
|||
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
|||
|
|||
public abstract class BaseAlarmMsgConstructor implements AlarmMsgConstructor { |
|||
|
|||
@Override |
|||
public AlarmCommentUpdateMsg constructAlarmCommentUpdatedMsg(UpdateMsgType msgType, AlarmComment alarmComment) { |
|||
return AlarmCommentUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(alarmComment)).build(); |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.actors.tenant; |
|||
|
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.actors.ActorSystemContext; |
|||
import org.thingsboard.server.actors.TbActorCtx; |
|||
import org.thingsboard.server.actors.TbActorRef; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
|||
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
|||
import org.thingsboard.server.common.msg.queue.ServiceType; |
|||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
|||
import org.thingsboard.server.common.msg.rule.engine.DeviceDeleteMsg; |
|||
import org.thingsboard.server.dao.tenant.TenantService; |
|||
|
|||
import static org.mockito.ArgumentMatchers.any; |
|||
import static org.mockito.ArgumentMatchers.eq; |
|||
import static org.mockito.Mockito.mock; |
|||
import static org.mockito.Mockito.never; |
|||
import static org.mockito.Mockito.reset; |
|||
import static org.mockito.Mockito.verify; |
|||
import static org.mockito.Mockito.when; |
|||
|
|||
public class TenantActorTest { |
|||
|
|||
TenantActor tenantActor; |
|||
TbActorCtx ctx; |
|||
ActorSystemContext systemContext; |
|||
TenantId tenantId = TenantId.SYS_TENANT_ID; |
|||
DeviceId deviceId = DeviceId.fromString("78bf9b26-74ef-4af2-9cfb-ad6cf24ad2ec"); |
|||
|
|||
@Before |
|||
public void setUp() throws Exception { |
|||
systemContext = mock(ActorSystemContext.class); |
|||
ctx = mock(TbActorCtx.class); |
|||
tenantActor = (TenantActor) new TenantActor.ActorCreator(systemContext, tenantId).createActor(); |
|||
when(systemContext.getTenantService()).thenReturn(mock(TenantService.class)); |
|||
tenantActor.init(ctx); |
|||
tenantActor.cantFindTenant = false; |
|||
} |
|||
|
|||
@Test |
|||
public void deleteDeviceTest() { |
|||
TbActorRef deviceActorRef = mock(TbActorRef.class); |
|||
when(systemContext.resolve(ServiceType.TB_CORE, tenantId, deviceId)).thenReturn(new TopicPartitionInfo("Main", tenantId, 0,true)); |
|||
when(ctx.getOrCreateChildActor(any(), any(), any(), any())).thenReturn(deviceActorRef); |
|||
ComponentLifecycleMsg componentLifecycleMsg = new ComponentLifecycleMsg(tenantId, deviceId, ComponentLifecycleEvent.DELETED); |
|||
tenantActor.doProcess(componentLifecycleMsg); |
|||
verify(deviceActorRef).tellWithHighPriority(eq(new DeviceDeleteMsg(tenantId, deviceId))); |
|||
|
|||
reset(ctx, deviceActorRef); |
|||
when(systemContext.resolve(ServiceType.TB_CORE, tenantId, deviceId)).thenReturn(new TopicPartitionInfo("Main", tenantId, 1,false)); |
|||
tenantActor.doProcess(componentLifecycleMsg); |
|||
verify(ctx, never()).getOrCreateChildActor(any(), any(), any(), any()); |
|||
verify(deviceActorRef, never()).tellWithHighPriority(any()); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,92 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.kv; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.Getter; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
|
|||
import java.time.DateTimeException; |
|||
import java.time.ZoneId; |
|||
import java.util.Map; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@AllArgsConstructor |
|||
@EqualsAndHashCode |
|||
@Slf4j |
|||
public class AggregationParams { |
|||
private static final Map<String, String> TZ_LINKS = Map.of("EST", "America/New_York", "GMT+0", "GMT", "GMT-0", "GMT", "HST", "US/Hawaii", "MST", "America/Phoenix", "ROC", "Asia/Taipei"); |
|||
@Getter |
|||
private final Aggregation aggregation; |
|||
@Getter |
|||
private final IntervalType intervalType; |
|||
@Getter |
|||
private final ZoneId tzId; |
|||
|
|||
private final long interval; |
|||
|
|||
public static AggregationParams none() { |
|||
return new AggregationParams(Aggregation.NONE, null, null, 0L); |
|||
} |
|||
|
|||
public static AggregationParams milliseconds(Aggregation aggregationType, long aggregationIntervalMs) { |
|||
return new AggregationParams(aggregationType, IntervalType.MILLISECONDS, null, aggregationIntervalMs); |
|||
} |
|||
|
|||
public static AggregationParams calendar(Aggregation aggregationType, IntervalType intervalType, String tzIdStr) { |
|||
return calendar(aggregationType, intervalType, getZoneId(tzIdStr)); |
|||
} |
|||
|
|||
public static AggregationParams calendar(Aggregation aggregationType, IntervalType intervalType, ZoneId tzId) { |
|||
return new AggregationParams(aggregationType, intervalType, tzId, 0L); |
|||
} |
|||
|
|||
public static AggregationParams of(Aggregation aggregation, IntervalType intervalType, ZoneId tzId, long interval) { |
|||
return new AggregationParams(aggregation, intervalType, tzId, interval); |
|||
} |
|||
|
|||
public long getInterval() { |
|||
if (intervalType == null) { |
|||
return 0L; |
|||
} else { |
|||
switch (intervalType) { |
|||
case WEEK: |
|||
case WEEK_ISO: |
|||
return TimeUnit.DAYS.toMillis(7); |
|||
case MONTH: |
|||
return TimeUnit.DAYS.toMillis(30); |
|||
case QUARTER: |
|||
return TimeUnit.DAYS.toMillis(90); |
|||
default: |
|||
return interval; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static ZoneId getZoneId(String tzIdStr) { |
|||
if (StringUtils.isEmpty(tzIdStr)) { |
|||
return ZoneId.systemDefault(); |
|||
} |
|||
try { |
|||
return ZoneId.of(tzIdStr, TZ_LINKS); |
|||
} catch (DateTimeException e) { |
|||
log.warn("[{}] Failed to convert the time zone. Fallback to default.", tzIdStr); |
|||
return ZoneId.systemDefault(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.kv; |
|||
|
|||
public enum IntervalType { |
|||
|
|||
MILLISECONDS, WEEK/*Sunday-Saturday*/, WEEK_ISO/*Monday-Sunday*/, MONTH, QUARTER |
|||
|
|||
} |
|||
@ -0,0 +1,176 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.transport.activity; |
|||
|
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.thingsboard.server.common.transport.activity.strategy.ActivityStrategy; |
|||
import org.thingsboard.server.queue.scheduler.SchedulerComponent; |
|||
|
|||
import java.util.Map; |
|||
import java.util.Random; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.concurrent.atomic.AtomicBoolean; |
|||
import java.util.concurrent.atomic.AtomicLong; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractActivityManager<Key, Metadata> implements ActivityManager<Key, Metadata> { |
|||
|
|||
private final ConcurrentMap<Key, ActivityStateWrapper> states = new ConcurrentHashMap<>(); |
|||
|
|||
@Autowired |
|||
protected SchedulerComponent scheduler; |
|||
|
|||
@Data |
|||
private class ActivityStateWrapper { |
|||
|
|||
private volatile ActivityState<Metadata> state; |
|||
private volatile long lastReportedTime; |
|||
private volatile ActivityStrategy strategy; |
|||
|
|||
} |
|||
|
|||
protected void init() { |
|||
var reportingPeriodMillis = getReportingPeriodMillis(); |
|||
scheduler.scheduleAtFixedRate(this::onReportingPeriodEnd, new Random().nextInt((int) reportingPeriodMillis), reportingPeriodMillis, TimeUnit.MILLISECONDS); |
|||
} |
|||
|
|||
protected abstract long getReportingPeriodMillis(); |
|||
|
|||
protected abstract ActivityStrategy getStrategy(); |
|||
|
|||
protected abstract ActivityState<Metadata> updateState(Key key, ActivityState<Metadata> state); |
|||
|
|||
protected abstract boolean hasExpired(long lastRecordedTime); |
|||
|
|||
protected abstract void onStateExpiry(Key key, Metadata metadata); |
|||
|
|||
protected abstract void reportActivity(Key key, Metadata metadata, long timeToReport, ActivityReportCallback<Key> callback); |
|||
|
|||
@Override |
|||
public void onActivity(Key key, Metadata metadata, long newLastRecordedTime) { |
|||
if (key == null) { |
|||
log.error("Failed to process activity event: provided activity key is null."); |
|||
return; |
|||
} |
|||
log.debug("Received activity event for key: [{}]", key); |
|||
|
|||
var shouldReport = new AtomicBoolean(false); |
|||
var lastRecordedTime = new AtomicLong(); |
|||
var lastReportedTime = new AtomicLong(); |
|||
|
|||
states.compute(key, (__, stateWrapper) -> { |
|||
if (stateWrapper == null) { |
|||
ActivityState<Metadata> newState = new ActivityState<>(); |
|||
stateWrapper = new ActivityStateWrapper(); |
|||
stateWrapper.setState(newState); |
|||
stateWrapper.setStrategy(getStrategy()); |
|||
} |
|||
var state = stateWrapper.getState(); |
|||
state.setMetadata(metadata); |
|||
if (state.getLastRecordedTime() < newLastRecordedTime) { |
|||
state.setLastRecordedTime(newLastRecordedTime); |
|||
} |
|||
shouldReport.set(stateWrapper.getStrategy().onActivity()); |
|||
lastRecordedTime.set(state.getLastRecordedTime()); |
|||
lastReportedTime.set(stateWrapper.getLastReportedTime()); |
|||
return stateWrapper; |
|||
}); |
|||
|
|||
if (shouldReport.get() && lastReportedTime.get() < lastRecordedTime.get()) { |
|||
log.debug("Going to report first activity event for key: [{}].", key); |
|||
reportActivity(key, metadata, lastRecordedTime.get(), new ActivityReportCallback<>() { |
|||
@Override |
|||
public void onSuccess(Key key, long reportedTime) { |
|||
updateLastReportedTime(key, reportedTime); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(Key key, Throwable t) { |
|||
log.debug("Failed to report first activity event for key: [{}].", key, t); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onReportingPeriodEnd() { |
|||
log.debug("Going to end reporting period."); |
|||
for (Map.Entry<Key, ActivityStateWrapper> entry : states.entrySet()) { |
|||
var key = entry.getKey(); |
|||
var stateWrapper = entry.getValue(); |
|||
var currentState = stateWrapper.getState(); |
|||
|
|||
long lastRecordedTime = currentState.getLastRecordedTime(); |
|||
long lastReportedTime = stateWrapper.getLastReportedTime(); |
|||
var metadata = currentState.getMetadata(); |
|||
|
|||
boolean hasExpired; |
|||
boolean shouldReport; |
|||
|
|||
var updatedState = updateState(key, currentState); |
|||
if (updatedState != null) { |
|||
stateWrapper.setState(updatedState); |
|||
lastRecordedTime = updatedState.getLastRecordedTime(); |
|||
metadata = updatedState.getMetadata(); |
|||
hasExpired = hasExpired(lastRecordedTime); |
|||
shouldReport = stateWrapper.getStrategy().onReportingPeriodEnd(); |
|||
} else { |
|||
states.remove(key); |
|||
hasExpired = false; |
|||
shouldReport = true; |
|||
} |
|||
|
|||
if (hasExpired) { |
|||
states.remove(key); |
|||
onStateExpiry(key, metadata); |
|||
shouldReport = true; |
|||
} |
|||
|
|||
if (shouldReport && lastReportedTime < lastRecordedTime) { |
|||
log.debug("Going to report last activity event for key: [{}].", key); |
|||
reportActivity(key, metadata, lastRecordedTime, new ActivityReportCallback<>() { |
|||
@Override |
|||
public void onSuccess(Key key, long reportedTime) { |
|||
updateLastReportedTime(key, reportedTime); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(Key key, Throwable t) { |
|||
log.debug("Failed to report last activity event for key: [{}].", key, t); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public long getLastRecordedTime(Key key) { |
|||
ActivityStateWrapper stateWrapper = states.get(key); |
|||
return stateWrapper == null ? 0L : stateWrapper.getState().getLastRecordedTime(); |
|||
} |
|||
|
|||
private void updateLastReportedTime(Key key, long newLastReportedTime) { |
|||
states.computeIfPresent(key, (__, stateWrapper) -> { |
|||
stateWrapper.setLastReportedTime(Math.max(stateWrapper.getLastReportedTime(), newLastReportedTime)); |
|||
return stateWrapper; |
|||
}); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.transport.activity; |
|||
|
|||
public interface ActivityManager<Key, Metadata> { |
|||
|
|||
void onActivity(Key key, Metadata metadata, long activityTimeMillis); |
|||
|
|||
void onReportingPeriodEnd(); |
|||
|
|||
long getLastRecordedTime(Key key); |
|||
|
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.transport.activity; |
|||
|
|||
public interface ActivityReportCallback<Key> { |
|||
|
|||
void onSuccess(Key key, long reportedTime); |
|||
|
|||
void onFailure(Key key, Throwable t); |
|||
|
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.transport.activity.strategy; |
|||
|
|||
public interface ActivityStrategy { |
|||
|
|||
boolean onActivity(); |
|||
|
|||
boolean onReportingPeriodEnd(); |
|||
|
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.transport.activity.strategy; |
|||
|
|||
public enum ActivityStrategyType { |
|||
|
|||
ALL { |
|||
@Override |
|||
public ActivityStrategy toStrategy() { |
|||
return AllEventsActivityStrategy.getInstance(); |
|||
} |
|||
}, |
|||
FIRST { |
|||
@Override |
|||
public ActivityStrategy toStrategy() { |
|||
return new FirstEventActivityStrategy(); |
|||
} |
|||
}, |
|||
LAST { |
|||
@Override |
|||
public ActivityStrategy toStrategy() { |
|||
return LastEventActivityStrategy.getInstance(); |
|||
} |
|||
}, |
|||
FIRST_AND_LAST { |
|||
@Override |
|||
public ActivityStrategy toStrategy() { |
|||
return new FirstAndLastEventActivityStrategy(); |
|||
} |
|||
}; |
|||
|
|||
public abstract ActivityStrategy toStrategy(); |
|||
|
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.transport.activity.strategy; |
|||
|
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
@EqualsAndHashCode |
|||
public final class FirstAndLastEventActivityStrategy implements ActivityStrategy { |
|||
|
|||
private boolean firstEventReceived; |
|||
|
|||
@Override |
|||
public synchronized boolean onActivity() { |
|||
if (!firstEventReceived) { |
|||
firstEventReceived = true; |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public synchronized boolean onReportingPeriodEnd() { |
|||
firstEventReceived = false; |
|||
return true; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.transport.activity.strategy; |
|||
|
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
@EqualsAndHashCode |
|||
public final class FirstEventActivityStrategy implements ActivityStrategy { |
|||
|
|||
private boolean firstEventReceived; |
|||
|
|||
@Override |
|||
public synchronized boolean onActivity() { |
|||
if (!firstEventReceived) { |
|||
firstEventReceived = true; |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public synchronized boolean onReportingPeriodEnd() { |
|||
firstEventReceived = false; |
|||
return false; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.transport.activity.strategy; |
|||
|
|||
public final class LastEventActivityStrategy implements ActivityStrategy { |
|||
|
|||
private static final LastEventActivityStrategy INSTANCE = new LastEventActivityStrategy(); |
|||
|
|||
private LastEventActivityStrategy() { |
|||
} |
|||
|
|||
public static LastEventActivityStrategy getInstance() { |
|||
return INSTANCE; |
|||
} |
|||
|
|||
@Override |
|||
public boolean onActivity() { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public boolean onReportingPeriodEnd() { |
|||
return true; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,138 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.transport.service; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.thingsboard.server.common.transport.TransportService; |
|||
import org.thingsboard.server.common.transport.TransportServiceCallback; |
|||
import org.thingsboard.server.common.transport.activity.AbstractActivityManager; |
|||
import org.thingsboard.server.common.transport.activity.ActivityReportCallback; |
|||
import org.thingsboard.server.common.transport.activity.ActivityState; |
|||
import org.thingsboard.server.common.transport.activity.strategy.ActivityStrategy; |
|||
import org.thingsboard.server.common.transport.activity.strategy.ActivityStrategyType; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
|
|||
import java.util.UUID; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
|
|||
@Slf4j |
|||
public abstract class TransportActivityManager extends AbstractActivityManager<UUID, TransportProtos.SessionInfoProto> implements TransportService { |
|||
|
|||
public static final String SESSION_EXPIRED_MESSAGE = "Session has expired due to last activity time!"; |
|||
|
|||
public static final TransportProtos.SessionEventMsg SESSION_EVENT_MSG_CLOSED = TransportProtos.SessionEventMsg.newBuilder() |
|||
.setSessionType(TransportProtos.SessionType.ASYNC) |
|||
.setEvent(TransportProtos.SessionEvent.CLOSED).build(); |
|||
public static final TransportProtos.SessionCloseNotificationProto SESSION_EXPIRED_NOTIFICATION_PROTO = TransportProtos.SessionCloseNotificationProto.newBuilder() |
|||
.setMessage(SESSION_EXPIRED_MESSAGE).build(); |
|||
|
|||
public final ConcurrentMap<UUID, SessionMetaData> sessions = new ConcurrentHashMap<>(); |
|||
|
|||
@Value("${transport.sessions.report_timeout}") |
|||
protected long sessionReportTimeout; |
|||
|
|||
@Value("${transport.sessions.inactivity_timeout}") |
|||
protected long sessionInactivityTimeout; |
|||
|
|||
@Value("${transport.activity.reporting_strategy:LAST}") |
|||
private ActivityStrategyType reportingStrategyType; |
|||
|
|||
@Override |
|||
protected long getReportingPeriodMillis() { |
|||
return sessionReportTimeout; |
|||
} |
|||
|
|||
@Override |
|||
protected ActivityStrategy getStrategy() { |
|||
return reportingStrategyType.toStrategy(); |
|||
} |
|||
|
|||
@Override |
|||
protected ActivityState<TransportProtos.SessionInfoProto> updateState(UUID sessionId, ActivityState<TransportProtos.SessionInfoProto> state) { |
|||
SessionMetaData session = sessions.get(sessionId); |
|||
if (session == null) { |
|||
return null; |
|||
} |
|||
|
|||
state.setMetadata(session.getSessionInfo()); |
|||
var sessionInfo = state.getMetadata(); |
|||
|
|||
if (sessionInfo.getGwSessionIdMSB() == 0L || sessionInfo.getGwSessionIdLSB() == 0L) { |
|||
return state; |
|||
} |
|||
|
|||
var gwSessionId = new UUID(sessionInfo.getGwSessionIdMSB(), sessionInfo.getGwSessionIdLSB()); |
|||
SessionMetaData gwSession = sessions.get(gwSessionId); |
|||
if (gwSession == null || !gwSession.isOverwriteActivityTime()) { |
|||
return state; |
|||
} |
|||
|
|||
long lastRecordedTime = state.getLastRecordedTime(); |
|||
long gwLastRecordedTime = getLastRecordedTime(gwSessionId); |
|||
log.debug("Session with id: [{}] has gateway session with id: [{}] with overwrite activity time enabled. " + |
|||
"Updating last activity time. Session last recorded time: [{}], gateway session last recorded time: [{}].", |
|||
sessionId, gwSessionId, lastRecordedTime, gwLastRecordedTime); |
|||
state.setLastRecordedTime(Math.max(lastRecordedTime, gwLastRecordedTime)); |
|||
return state; |
|||
} |
|||
|
|||
@Override |
|||
protected boolean hasExpired(long lastRecordedTime) { |
|||
return (getCurrentTimeMillis() - sessionInactivityTimeout) > lastRecordedTime; |
|||
} |
|||
|
|||
@Override |
|||
protected void onStateExpiry(UUID sessionId, TransportProtos.SessionInfoProto sessionInfo) { |
|||
log.debug("Session with id: [{}] has expired due to last activity time.", sessionId); |
|||
SessionMetaData expiredSession = sessions.remove(sessionId); |
|||
if (expiredSession != null) { |
|||
deregisterSession(sessionInfo); |
|||
process(sessionInfo, SESSION_EVENT_MSG_CLOSED, null); |
|||
expiredSession.getListener().onRemoteSessionCloseCommand(sessionId, SESSION_EXPIRED_NOTIFICATION_PROTO); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void reportActivity(UUID sessionId, TransportProtos.SessionInfoProto currentSessionInfo, long timeToReport, ActivityReportCallback<UUID> callback) { |
|||
log.debug("Reporting activity state for session with id: [{}]. Time to report: [{}].", sessionId, timeToReport); |
|||
SessionMetaData session = sessions.get(sessionId); |
|||
TransportProtos.SubscriptionInfoProto subscriptionInfo = TransportProtos.SubscriptionInfoProto.newBuilder() |
|||
.setAttributeSubscription(session != null && session.isSubscribedToAttributes()) |
|||
.setRpcSubscription(session != null && session.isSubscribedToRPC()) |
|||
.setLastActivityTime(timeToReport) |
|||
.build(); |
|||
TransportProtos.SessionInfoProto sessionInfo = session != null ? session.getSessionInfo() : currentSessionInfo; |
|||
process(sessionInfo, subscriptionInfo, new TransportServiceCallback<>() { |
|||
@Override |
|||
public void onSuccess(Void msgAcknowledged) { |
|||
callback.onSuccess(sessionId, timeToReport); |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void onError(Throwable e) { |
|||
callback.onFailure(sessionId, e); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
protected long getCurrentTimeMillis() { |
|||
return System.currentTimeMillis(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.transport.activity.strategy; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
|
|||
public class ActivityStrategyTypeTest { |
|||
|
|||
@Test |
|||
public void testCreateAllEventsStrategy() { |
|||
assertThat(ActivityStrategyType.ALL.toStrategy()).isEqualTo(AllEventsActivityStrategy.getInstance()); |
|||
} |
|||
|
|||
@Test |
|||
public void testCreateFirstEventStrategy() { |
|||
assertThat(ActivityStrategyType.FIRST.toStrategy()).isEqualTo(new FirstEventActivityStrategy()); |
|||
} |
|||
|
|||
@Test |
|||
public void testCreateLastEventStrategy() { |
|||
assertThat(ActivityStrategyType.LAST.toStrategy()).isEqualTo(LastEventActivityStrategy.getInstance()); |
|||
} |
|||
|
|||
@Test |
|||
public void testCreateFirstAndLastEventStrategy() { |
|||
assertThat(ActivityStrategyType.FIRST_AND_LAST.toStrategy()).isEqualTo(new FirstAndLastEventActivityStrategy()); |
|||
} |
|||
|
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue