14 changed files with 420 additions and 706 deletions
@ -1,11 +1,39 @@ |
|||
/** |
|||
* ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL |
|||
* |
|||
* Copyright © 2016-2023 ThingsBoard, Inc. All Rights Reserved. |
|||
* |
|||
* NOTICE: All information contained herein is, and remains |
|||
* the property of ThingsBoard, Inc. and its suppliers, |
|||
* if any. The intellectual and technical concepts contained |
|||
* herein are proprietary to ThingsBoard, Inc. |
|||
* and its suppliers and may be covered by U.S. and Foreign Patents, |
|||
* patents in process, and are protected by trade secret or copyright law. |
|||
* |
|||
* Dissemination of this information or reproduction of this material is strictly forbidden |
|||
* unless prior written permission is obtained from COMPANY. |
|||
* |
|||
* Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, |
|||
* managers or contractors who have executed Confidentiality and Non-disclosure agreements |
|||
* explicitly covering such access. |
|||
* |
|||
* The copyright notice above does not evidence any actual or intended publication |
|||
* or disclosure of this source code, which includes |
|||
* information that is confidential and/or proprietary, and is a trade secret, of COMPANY. |
|||
* ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, |
|||
* OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT |
|||
* THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, |
|||
* AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. |
|||
* THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION |
|||
* DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, |
|||
* OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.activity.strategy; |
|||
|
|||
import org.thingsboard.server.common.transport.activity.ActivityState; |
|||
|
|||
public interface ActivityStrategy { |
|||
|
|||
boolean onActivity(ActivityState state); |
|||
boolean onActivity(); |
|||
|
|||
boolean onReportingPeriodEnd(ActivityState state); |
|||
boolean onReportingPeriodEnd(); |
|||
|
|||
} |
|||
|
|||
@ -0,0 +1,58 @@ |
|||
/** |
|||
* ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL |
|||
* |
|||
* Copyright © 2016-2023 ThingsBoard, Inc. All Rights Reserved. |
|||
* |
|||
* NOTICE: All information contained herein is, and remains |
|||
* the property of ThingsBoard, Inc. and its suppliers, |
|||
* if any. The intellectual and technical concepts contained |
|||
* herein are proprietary to ThingsBoard, Inc. |
|||
* and its suppliers and may be covered by U.S. and Foreign Patents, |
|||
* patents in process, and are protected by trade secret or copyright law. |
|||
* |
|||
* Dissemination of this information or reproduction of this material is strictly forbidden |
|||
* unless prior written permission is obtained from COMPANY. |
|||
* |
|||
* Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, |
|||
* managers or contractors who have executed Confidentiality and Non-disclosure agreements |
|||
* explicitly covering such access. |
|||
* |
|||
* The copyright notice above does not evidence any actual or intended publication |
|||
* or disclosure of this source code, which includes |
|||
* information that is confidential and/or proprietary, and is a trade secret, of COMPANY. |
|||
* ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, |
|||
* OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT |
|||
* THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, |
|||
* AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. |
|||
* THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION |
|||
* DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, |
|||
* OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.activity.strategy; |
|||
|
|||
public final class ActivityStrategyFactory { |
|||
|
|||
private static final String ALL_EVENTS_STRATEGY_NAME = "ALL"; |
|||
private static final String FIRST_EVENT_STRATEGY_NAME = "FIRST"; |
|||
private static final String LAST_EVENT_STRATEGY_NAME = "LAST"; |
|||
private static final String FIRST_AND_LAST_STRATEGY_NAME = "FIRST_AND_LAST"; |
|||
|
|||
private ActivityStrategyFactory() { |
|||
} |
|||
|
|||
public static ActivityStrategy createStrategy(String strategyName) { |
|||
switch (strategyName) { |
|||
case ALL_EVENTS_STRATEGY_NAME: |
|||
return new AllEventsActivityStrategy(); |
|||
case FIRST_EVENT_STRATEGY_NAME: |
|||
return new FirstEventActivityStrategy(); |
|||
case LAST_EVENT_STRATEGY_NAME: |
|||
return new LastEventActivityStrategy(); |
|||
case FIRST_AND_LAST_STRATEGY_NAME: |
|||
return new FirstAndLastEventActivityStrategy(); |
|||
default: |
|||
throw new IllegalArgumentException("Unknown activity strategy name: [" + strategyName + "."); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -1,20 +0,0 @@ |
|||
package org.thingsboard.server.common.transport.activity.strategy; |
|||
|
|||
public enum ActivityStrategyType { |
|||
|
|||
FIRST(new FirstEventActivityStrategy()), |
|||
LAST(new LastEventActivityStrategy()), |
|||
FIRST_AND_LAST(new FirstAndLastEventActivityStrategy()), |
|||
ALL(new AllEventsActivityStrategy()); |
|||
|
|||
private final ActivityStrategy strategy; |
|||
|
|||
ActivityStrategyType(ActivityStrategy strategy) { |
|||
this.strategy = strategy; |
|||
} |
|||
|
|||
public ActivityStrategy getStrategy() { |
|||
return strategy; |
|||
} |
|||
|
|||
} |
|||
@ -1,17 +1,45 @@ |
|||
/** |
|||
* ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL |
|||
* |
|||
* Copyright © 2016-2023 ThingsBoard, Inc. All Rights Reserved. |
|||
* |
|||
* NOTICE: All information contained herein is, and remains |
|||
* the property of ThingsBoard, Inc. and its suppliers, |
|||
* if any. The intellectual and technical concepts contained |
|||
* herein are proprietary to ThingsBoard, Inc. |
|||
* and its suppliers and may be covered by U.S. and Foreign Patents, |
|||
* patents in process, and are protected by trade secret or copyright law. |
|||
* |
|||
* Dissemination of this information or reproduction of this material is strictly forbidden |
|||
* unless prior written permission is obtained from COMPANY. |
|||
* |
|||
* Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, |
|||
* managers or contractors who have executed Confidentiality and Non-disclosure agreements |
|||
* explicitly covering such access. |
|||
* |
|||
* The copyright notice above does not evidence any actual or intended publication |
|||
* or disclosure of this source code, which includes |
|||
* information that is confidential and/or proprietary, and is a trade secret, of COMPANY. |
|||
* ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, |
|||
* OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT |
|||
* THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, |
|||
* AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. |
|||
* THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION |
|||
* DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, |
|||
* OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.activity.strategy; |
|||
|
|||
import org.thingsboard.server.common.transport.activity.ActivityState; |
|||
|
|||
public class AllEventsActivityStrategy implements ActivityStrategy { |
|||
|
|||
@Override |
|||
public boolean onActivity(ActivityState state) { |
|||
return state.getLastReportedTime() < state.getLastReportedTime(); |
|||
public boolean onActivity() { |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
public boolean onReportingPeriodEnd(ActivityState state) { |
|||
return state.getLastReportedTime() < state.getLastReportedTime(); |
|||
public boolean onReportingPeriodEnd() { |
|||
return true; |
|||
} |
|||
|
|||
} |
|||
|
|||
@ -1,25 +1,52 @@ |
|||
/** |
|||
* ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL |
|||
* |
|||
* Copyright © 2016-2023 ThingsBoard, Inc. All Rights Reserved. |
|||
* |
|||
* NOTICE: All information contained herein is, and remains |
|||
* the property of ThingsBoard, Inc. and its suppliers, |
|||
* if any. The intellectual and technical concepts contained |
|||
* herein are proprietary to ThingsBoard, Inc. |
|||
* and its suppliers and may be covered by U.S. and Foreign Patents, |
|||
* patents in process, and are protected by trade secret or copyright law. |
|||
* |
|||
* Dissemination of this information or reproduction of this material is strictly forbidden |
|||
* unless prior written permission is obtained from COMPANY. |
|||
* |
|||
* Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, |
|||
* managers or contractors who have executed Confidentiality and Non-disclosure agreements |
|||
* explicitly covering such access. |
|||
* |
|||
* The copyright notice above does not evidence any actual or intended publication |
|||
* or disclosure of this source code, which includes |
|||
* information that is confidential and/or proprietary, and is a trade secret, of COMPANY. |
|||
* ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, |
|||
* OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT |
|||
* THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, |
|||
* AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. |
|||
* THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION |
|||
* DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, |
|||
* OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.activity.strategy; |
|||
|
|||
import org.thingsboard.server.common.transport.activity.ActivityState; |
|||
|
|||
public class FirstAndLastEventActivityStrategy implements ActivityStrategy { |
|||
|
|||
private volatile long firstEventTs; |
|||
private boolean firstEventReceived; |
|||
|
|||
@Override |
|||
public boolean onActivity(ActivityState state) { |
|||
long lastRecordedTime = state.getLastRecordedTime(); |
|||
if (firstEventTs == 0L) { |
|||
firstEventTs = lastRecordedTime; |
|||
return state.getLastReportedTime() < firstEventTs; |
|||
public synchronized boolean onActivity() { |
|||
if (!firstEventReceived) { |
|||
firstEventReceived = true; |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public boolean onReportingPeriodEnd(ActivityState state) { |
|||
firstEventTs = 0L; |
|||
return state.getLastReportedTime() < state.getLastRecordedTime(); |
|||
public synchronized boolean onReportingPeriodEnd() { |
|||
firstEventReceived = false; |
|||
return true; |
|||
} |
|||
|
|||
} |
|||
|
|||
@ -1,17 +1,45 @@ |
|||
/** |
|||
* ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL |
|||
* |
|||
* Copyright © 2016-2023 ThingsBoard, Inc. All Rights Reserved. |
|||
* |
|||
* NOTICE: All information contained herein is, and remains |
|||
* the property of ThingsBoard, Inc. and its suppliers, |
|||
* if any. The intellectual and technical concepts contained |
|||
* herein are proprietary to ThingsBoard, Inc. |
|||
* and its suppliers and may be covered by U.S. and Foreign Patents, |
|||
* patents in process, and are protected by trade secret or copyright law. |
|||
* |
|||
* Dissemination of this information or reproduction of this material is strictly forbidden |
|||
* unless prior written permission is obtained from COMPANY. |
|||
* |
|||
* Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, |
|||
* managers or contractors who have executed Confidentiality and Non-disclosure agreements |
|||
* explicitly covering such access. |
|||
* |
|||
* The copyright notice above does not evidence any actual or intended publication |
|||
* or disclosure of this source code, which includes |
|||
* information that is confidential and/or proprietary, and is a trade secret, of COMPANY. |
|||
* ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, |
|||
* OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT |
|||
* THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, |
|||
* AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. |
|||
* THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION |
|||
* DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, |
|||
* OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.activity.strategy; |
|||
|
|||
import org.thingsboard.server.common.transport.activity.ActivityState; |
|||
|
|||
public class LastEventActivityStrategy implements ActivityStrategy { |
|||
|
|||
@Override |
|||
public boolean onActivity(ActivityState state) { |
|||
public boolean onActivity() { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public boolean onReportingPeriodEnd(ActivityState state) { |
|||
return state.getLastReportedTime() < state.getLastRecordedTime(); |
|||
public boolean onReportingPeriodEnd() { |
|||
return true; |
|||
} |
|||
|
|||
} |
|||
|
|||
@ -1,199 +0,0 @@ |
|||
/** |
|||
* ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL |
|||
* |
|||
* Copyright © 2016-2023 ThingsBoard, Inc. All Rights Reserved. |
|||
* |
|||
* NOTICE: All information contained herein is, and remains |
|||
* the property of ThingsBoard, Inc. and its suppliers, |
|||
* if any. The intellectual and technical concepts contained |
|||
* herein are proprietary to ThingsBoard, Inc. |
|||
* and its suppliers and may be covered by U.S. and Foreign Patents, |
|||
* patents in process, and are protected by trade secret or copyright law. |
|||
* |
|||
* Dissemination of this information or reproduction of this material is strictly forbidden |
|||
* unless prior written permission is obtained from COMPANY. |
|||
* |
|||
* Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, |
|||
* managers or contractors who have executed Confidentiality and Non-disclosure agreements |
|||
* explicitly covering such access. |
|||
* |
|||
* The copyright notice above does not evidence any actual or intended publication |
|||
* or disclosure of this source code, which includes |
|||
* information that is confidential and/or proprietary, and is a trade secret, of COMPANY. |
|||
* ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, |
|||
* OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT |
|||
* THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, |
|||
* AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. |
|||
* THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION |
|||
* DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, |
|||
* OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.service.activity; |
|||
|
|||
import com.google.common.util.concurrent.FutureCallback; |
|||
import com.google.common.util.concurrent.Futures; |
|||
import com.google.common.util.concurrent.MoreExecutors; |
|||
import com.google.common.util.concurrent.SettableFuture; |
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.checkerframework.checker.nullness.qual.NonNull; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.data.util.Pair; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.TransportService; |
|||
import org.thingsboard.server.common.transport.activity.AbstractActivityManager; |
|||
import org.thingsboard.server.common.transport.activity.ActivityReportCallback; |
|||
import org.thingsboard.server.common.transport.service.SessionMetaData; |
|||
import org.thingsboard.server.common.transport.service.TransportActivityState; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.queue.util.TbTransportComponent; |
|||
|
|||
import java.util.HashSet; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
import java.util.function.Supplier; |
|||
|
|||
import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED; |
|||
import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EXPIRED_NOTIFICATION_PROTO; |
|||
|
|||
@Slf4j |
|||
@Component |
|||
@TbTransportComponent |
|||
@ConditionalOnProperty(prefix = "transport.activity", value = "reporting_strategy", havingValue = "first-and-last") |
|||
public class FirstAndLastEventTransportActivityManager extends AbstractActivityManager<UUID, TransportActivityState> { |
|||
|
|||
private final ConcurrentMap<UUID, ActivityStateWrapper> states = new ConcurrentHashMap<>(); |
|||
|
|||
@Data |
|||
private static class ActivityStateWrapper { |
|||
|
|||
volatile TransportActivityState state; |
|||
volatile boolean alreadyBeenReported; |
|||
|
|||
} |
|||
|
|||
@Value("${transport.sessions.inactivity_timeout}") |
|||
private long sessionInactivityTimeout; |
|||
|
|||
@Autowired |
|||
private TransportService transportService; |
|||
|
|||
@Override |
|||
protected void doOnActivity(UUID sessionId, Supplier<TransportActivityState> newStateSupplier) { |
|||
long newLastRecordedTime = System.currentTimeMillis(); |
|||
SettableFuture<Pair<UUID, Long>> reportCompletedFuture = SettableFuture.create(); |
|||
states.compute(sessionId, (key, activityStateWrapper) -> { |
|||
if (activityStateWrapper == null) { |
|||
activityStateWrapper = new ActivityStateWrapper(); |
|||
activityStateWrapper.setState(newStateSupplier.get()); |
|||
} |
|||
var activityState = activityStateWrapper.getState(); |
|||
if (activityState.getLastRecordedTime() < newLastRecordedTime) { |
|||
activityState.setLastRecordedTime(newLastRecordedTime); |
|||
} |
|||
if (activityStateWrapper.isAlreadyBeenReported()) { |
|||
return activityStateWrapper; |
|||
} |
|||
if (activityState.getLastReportedTime() < activityState.getLastRecordedTime()) { |
|||
log.debug("[{}] Going to report first activity event for session with id: [{}]", name, sessionId); |
|||
reporter.report(key, activityState.getLastRecordedTime(), activityState, new ActivityReportCallback<>() { |
|||
@Override |
|||
public void onSuccess(UUID key, long reportedTime) { |
|||
reportCompletedFuture.set(Pair.of(key, reportedTime)); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(UUID key, Throwable t) { |
|||
reportCompletedFuture.setException(t); |
|||
} |
|||
}); |
|||
} |
|||
activityStateWrapper.setAlreadyBeenReported(true); |
|||
return activityStateWrapper; |
|||
}); |
|||
Futures.addCallback(reportCompletedFuture, new FutureCallback<>() { |
|||
@Override |
|||
public void onSuccess(Pair<UUID, Long> reportResult) { |
|||
updateLastReportedTime(reportResult.getFirst(), reportResult.getSecond()); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull Throwable t) { |
|||
log.debug("[{}] Failed to report first activity event for session with id: [{}]", name, sessionId); |
|||
} |
|||
}, MoreExecutors.directExecutor()); |
|||
} |
|||
|
|||
@Override |
|||
protected void doOnReportingPeriodEnd() { |
|||
Set<UUID> statesToRemove = new HashSet<>(); |
|||
for (Map.Entry<UUID, ActivityStateWrapper> entry : states.entrySet()) { |
|||
var sessionId = entry.getKey(); |
|||
var activityStateWrapper = entry.getValue(); |
|||
var activityState = activityStateWrapper.getState(); |
|||
|
|||
SessionMetaData sessionMetaData = transportService.getSession(sessionId); |
|||
if (sessionMetaData != null) { |
|||
activityState.setSessionInfoProto(sessionMetaData.getSessionInfo()); |
|||
} else { |
|||
log.debug("[{}] Session with id: [{}] is not present. Marking it's activity state for removal.", name, sessionId); |
|||
statesToRemove.add(sessionId); |
|||
} |
|||
|
|||
long lastActivityTime = activityState.getLastRecordedTime(); |
|||
TransportProtos.SessionInfoProto sessionInfo = activityState.getSessionInfoProto(); |
|||
|
|||
if (sessionInfo.getGwSessionIdMSB() != 0 && sessionInfo.getGwSessionIdLSB() != 0) { |
|||
var gwSessionId = new UUID(sessionInfo.getGwSessionIdMSB(), sessionInfo.getGwSessionIdLSB()); |
|||
SessionMetaData gwSessionMetaData = transportService.getSession(gwSessionId); |
|||
if (gwSessionMetaData != null && gwSessionMetaData.isOverwriteActivityTime()) { |
|||
ActivityStateWrapper gwActivityStateWrapper = states.get(gwSessionId); |
|||
if (gwActivityStateWrapper != null) { |
|||
log.debug("[{}] Session with id: [{}] has gateway session with id: [{}] with overwrite activity time enabled. Updating last activity time.", name, sessionId, gwSessionId); |
|||
lastActivityTime = Math.max(gwActivityStateWrapper.getState().getLastRecordedTime(), lastActivityTime); |
|||
} |
|||
} |
|||
} |
|||
|
|||
long expirationTime = System.currentTimeMillis() - sessionInactivityTimeout; |
|||
boolean hasExpired = sessionMetaData != null && lastActivityTime < expirationTime; |
|||
if (hasExpired) { |
|||
log.debug("[{}] Session with id: [{}] has expired due to last activity time: [{}]. Marking it's activity state for removal.", name, sessionId, lastActivityTime); |
|||
transportService.deregisterSession(sessionInfo); |
|||
statesToRemove.add(sessionId); |
|||
transportService.process(sessionInfo, SESSION_EVENT_MSG_CLOSED, null); |
|||
sessionMetaData.getListener().onRemoteSessionCloseCommand(sessionId, SESSION_EXPIRED_NOTIFICATION_PROTO); |
|||
} |
|||
if (activityState.getLastReportedTime() < lastActivityTime) { |
|||
log.debug("[{}] Going to report last activity event for session with id: [{}].", name, sessionId); |
|||
reporter.report(sessionId, lastActivityTime, activityState, new ActivityReportCallback<>() { |
|||
@Override |
|||
public void onSuccess(UUID key, long reportedTime) { |
|||
updateLastReportedTime(key, reportedTime); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(UUID key, Throwable t) { |
|||
log.debug("[{}] Failed to report last activity event for session with id: [{}].", name, sessionId); |
|||
} |
|||
}); |
|||
} |
|||
activityStateWrapper.setAlreadyBeenReported(false); |
|||
} |
|||
statesToRemove.forEach(states::remove); |
|||
} |
|||
|
|||
private void updateLastReportedTime(UUID key, long newLastReportedTime) { |
|||
states.computeIfPresent(key, (__, activityStateWrapper) -> { |
|||
var activityState = activityStateWrapper.getState(); |
|||
activityState.setLastReportedTime(Math.max(activityState.getLastReportedTime(), newLastReportedTime)); |
|||
return activityStateWrapper; |
|||
}); |
|||
} |
|||
|
|||
} |
|||
@ -1,200 +0,0 @@ |
|||
/** |
|||
* ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL |
|||
* |
|||
* Copyright © 2016-2023 ThingsBoard, Inc. All Rights Reserved. |
|||
* |
|||
* NOTICE: All information contained herein is, and remains |
|||
* the property of ThingsBoard, Inc. and its suppliers, |
|||
* if any. The intellectual and technical concepts contained |
|||
* herein are proprietary to ThingsBoard, Inc. |
|||
* and its suppliers and may be covered by U.S. and Foreign Patents, |
|||
* patents in process, and are protected by trade secret or copyright law. |
|||
* |
|||
* Dissemination of this information or reproduction of this material is strictly forbidden |
|||
* unless prior written permission is obtained from COMPANY. |
|||
* |
|||
* Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, |
|||
* managers or contractors who have executed Confidentiality and Non-disclosure agreements |
|||
* explicitly covering such access. |
|||
* |
|||
* The copyright notice above does not evidence any actual or intended publication |
|||
* or disclosure of this source code, which includes |
|||
* information that is confidential and/or proprietary, and is a trade secret, of COMPANY. |
|||
* ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, |
|||
* OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT |
|||
* THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, |
|||
* AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. |
|||
* THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION |
|||
* DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, |
|||
* OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.service.activity; |
|||
|
|||
import com.google.common.util.concurrent.FutureCallback; |
|||
import com.google.common.util.concurrent.Futures; |
|||
import com.google.common.util.concurrent.MoreExecutors; |
|||
import com.google.common.util.concurrent.SettableFuture; |
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.checkerframework.checker.nullness.qual.NonNull; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.data.util.Pair; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.TransportService; |
|||
import org.thingsboard.server.common.transport.activity.AbstractActivityManager; |
|||
import org.thingsboard.server.common.transport.activity.ActivityReportCallback; |
|||
import org.thingsboard.server.common.transport.service.SessionMetaData; |
|||
import org.thingsboard.server.common.transport.service.TransportActivityState; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.queue.util.TbTransportComponent; |
|||
|
|||
import java.util.HashSet; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
import java.util.function.Supplier; |
|||
|
|||
import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED; |
|||
import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EXPIRED_NOTIFICATION_PROTO; |
|||
|
|||
@Slf4j |
|||
@Component |
|||
@TbTransportComponent |
|||
@ConditionalOnProperty(prefix = "transport.activity", value = "reporting_strategy", havingValue = "first") |
|||
public class OnlyFirstEventTransportActivityManager extends AbstractActivityManager<UUID, TransportActivityState> { |
|||
|
|||
private final ConcurrentMap<UUID, ActivityStateWrapper> states = new ConcurrentHashMap<>(); |
|||
|
|||
@Data |
|||
private static class ActivityStateWrapper { |
|||
|
|||
volatile TransportActivityState state; |
|||
volatile boolean alreadyBeenReported; |
|||
|
|||
} |
|||
|
|||
@Value("${transport.sessions.inactivity_timeout}") |
|||
private long sessionInactivityTimeout; |
|||
|
|||
@Autowired |
|||
private TransportService transportService; |
|||
|
|||
@Override |
|||
protected void doOnActivity(UUID sessionId, Supplier<TransportActivityState> newStateSupplier) { |
|||
long newLastRecordedTime = System.currentTimeMillis(); |
|||
SettableFuture<Pair<UUID, Long>> reportCompletedFuture = SettableFuture.create(); |
|||
states.compute(sessionId, (key, activityStateWrapper) -> { |
|||
if (activityStateWrapper == null) { |
|||
activityStateWrapper = new ActivityStateWrapper(); |
|||
activityStateWrapper.setState(newStateSupplier.get()); |
|||
} |
|||
var activityState = activityStateWrapper.getState(); |
|||
if (activityState.getLastRecordedTime() < newLastRecordedTime) { |
|||
activityState.setLastRecordedTime(newLastRecordedTime); |
|||
} |
|||
if (activityStateWrapper.isAlreadyBeenReported()) { |
|||
return activityStateWrapper; |
|||
} |
|||
if (activityState.getLastReportedTime() < activityState.getLastRecordedTime()) { |
|||
log.debug("[{}] Going to report first activity event for session with id: [{}]", name, sessionId); |
|||
reporter.report(key, activityState.getLastRecordedTime(), activityState, new ActivityReportCallback<>() { |
|||
@Override |
|||
public void onSuccess(UUID key, long reportedTime) { |
|||
reportCompletedFuture.set(Pair.of(key, reportedTime)); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(UUID key, Throwable t) { |
|||
reportCompletedFuture.setException(t); |
|||
} |
|||
}); |
|||
} |
|||
activityStateWrapper.setAlreadyBeenReported(true); |
|||
return activityStateWrapper; |
|||
}); |
|||
Futures.addCallback(reportCompletedFuture, new FutureCallback<>() { |
|||
@Override |
|||
public void onSuccess(Pair<UUID, Long> reportResult) { |
|||
updateLastReportedTime(reportResult.getFirst(), reportResult.getSecond()); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull Throwable t) { |
|||
log.debug("[{}] Failed to report first activity event for session with id: [{}]", name, sessionId); |
|||
} |
|||
}, MoreExecutors.directExecutor()); |
|||
} |
|||
|
|||
@Override |
|||
protected void doOnReportingPeriodEnd() { |
|||
Set<UUID> statesToRemove = new HashSet<>(); |
|||
for (Map.Entry<UUID, ActivityStateWrapper> entry : states.entrySet()) { |
|||
var sessionId = entry.getKey(); |
|||
var activityStateWrapper = entry.getValue(); |
|||
var activityState = activityStateWrapper.getState(); |
|||
|
|||
SessionMetaData sessionMetaData = transportService.getSession(sessionId); |
|||
if (sessionMetaData != null) { |
|||
activityState.setSessionInfoProto(sessionMetaData.getSessionInfo()); |
|||
} else { |
|||
log.debug("[{}] Session with id: [{}] is not present. Marking it's activity state for removal.", name, sessionId); |
|||
statesToRemove.add(sessionId); |
|||
} |
|||
|
|||
long lastActivityTime = activityState.getLastRecordedTime(); |
|||
TransportProtos.SessionInfoProto sessionInfo = activityState.getSessionInfoProto(); |
|||
|
|||
if (sessionInfo.getGwSessionIdMSB() != 0 && sessionInfo.getGwSessionIdLSB() != 0) { |
|||
var gwSessionId = new UUID(sessionInfo.getGwSessionIdMSB(), sessionInfo.getGwSessionIdLSB()); |
|||
SessionMetaData gwSessionMetaData = transportService.getSession(gwSessionId); |
|||
if (gwSessionMetaData != null && gwSessionMetaData.isOverwriteActivityTime()) { |
|||
ActivityStateWrapper gwActivityStateWrapper = states.get(gwSessionId); |
|||
if (gwActivityStateWrapper != null) { |
|||
log.debug("[{}] Session with id: [{}] has gateway session with id: [{}] with overwrite activity time enabled. Updating last activity time.", name, sessionId, gwSessionId); |
|||
lastActivityTime = Math.max(gwActivityStateWrapper.getState().getLastRecordedTime(), lastActivityTime); |
|||
} |
|||
} |
|||
} |
|||
|
|||
long expirationTime = System.currentTimeMillis() - sessionInactivityTimeout; |
|||
boolean hasExpired = sessionMetaData != null && lastActivityTime < expirationTime; |
|||
if (hasExpired) { |
|||
log.debug("[{}] Session with id: [{}] has expired due to last activity time: [{}]. Marking it's activity state for removal.", name, sessionId, lastActivityTime); |
|||
transportService.deregisterSession(sessionInfo); |
|||
statesToRemove.add(sessionId); |
|||
transportService.process(sessionInfo, SESSION_EVENT_MSG_CLOSED, null); |
|||
sessionMetaData.getListener().onRemoteSessionCloseCommand(sessionId, SESSION_EXPIRED_NOTIFICATION_PROTO); |
|||
} |
|||
boolean shouldReportLeftoverEvents = sessionMetaData == null || hasExpired; |
|||
if (shouldReportLeftoverEvents && activityState.getLastReportedTime() < lastActivityTime) { |
|||
log.debug("[{}] Going to report leftover activity event for session with id: [{}].", name, sessionId); |
|||
reporter.report(sessionId, lastActivityTime, activityState, new ActivityReportCallback<>() { |
|||
@Override |
|||
public void onSuccess(UUID key, long reportedTime) { |
|||
updateLastReportedTime(key, reportedTime); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(UUID key, Throwable t) { |
|||
log.debug("[{}] Failed to report leftover activity event for session with id: [{}].", name, sessionId); |
|||
} |
|||
}); |
|||
} |
|||
activityStateWrapper.setAlreadyBeenReported(false); |
|||
} |
|||
statesToRemove.forEach(states::remove); |
|||
} |
|||
|
|||
private void updateLastReportedTime(UUID key, long newLastReportedTime) { |
|||
states.computeIfPresent(key, (__, activityStateWrapper) -> { |
|||
var activityState = activityStateWrapper.getState(); |
|||
activityState.setLastReportedTime(Math.max(activityState.getLastReportedTime(), newLastReportedTime)); |
|||
return activityStateWrapper; |
|||
}); |
|||
} |
|||
|
|||
} |
|||
@ -1,149 +0,0 @@ |
|||
/** |
|||
* ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL |
|||
* |
|||
* Copyright © 2016-2023 ThingsBoard, Inc. All Rights Reserved. |
|||
* |
|||
* NOTICE: All information contained herein is, and remains |
|||
* the property of ThingsBoard, Inc. and its suppliers, |
|||
* if any. The intellectual and technical concepts contained |
|||
* herein are proprietary to ThingsBoard, Inc. |
|||
* and its suppliers and may be covered by U.S. and Foreign Patents, |
|||
* patents in process, and are protected by trade secret or copyright law. |
|||
* |
|||
* Dissemination of this information or reproduction of this material is strictly forbidden |
|||
* unless prior written permission is obtained from COMPANY. |
|||
* |
|||
* Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, |
|||
* managers or contractors who have executed Confidentiality and Non-disclosure agreements |
|||
* explicitly covering such access. |
|||
* |
|||
* The copyright notice above does not evidence any actual or intended publication |
|||
* or disclosure of this source code, which includes |
|||
* information that is confidential and/or proprietary, and is a trade secret, of COMPANY. |
|||
* ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, |
|||
* OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT |
|||
* THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, |
|||
* AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. |
|||
* THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION |
|||
* DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, |
|||
* OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.service.activity; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.TransportService; |
|||
import org.thingsboard.server.common.transport.activity.AbstractActivityManager; |
|||
import org.thingsboard.server.common.transport.activity.ActivityReportCallback; |
|||
import org.thingsboard.server.common.transport.service.SessionMetaData; |
|||
import org.thingsboard.server.common.transport.service.TransportActivityState; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.queue.util.TbTransportComponent; |
|||
|
|||
import java.util.HashSet; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
import java.util.function.Supplier; |
|||
|
|||
import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED; |
|||
import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EXPIRED_NOTIFICATION_PROTO; |
|||
|
|||
@Slf4j |
|||
@Component |
|||
@TbTransportComponent |
|||
@ConditionalOnProperty(prefix = "transport.activity", value = "reporting_strategy", havingValue = "last") |
|||
public class OnlyLastEventTransportActivityManager extends AbstractActivityManager<UUID, TransportActivityState> { |
|||
|
|||
private final ConcurrentMap<UUID, TransportActivityState> states = new ConcurrentHashMap<>(); |
|||
|
|||
@Value("${transport.sessions.inactivity_timeout}") |
|||
private long sessionInactivityTimeout; |
|||
|
|||
@Autowired |
|||
private TransportService transportService; |
|||
|
|||
@Override |
|||
protected void doOnActivity(UUID sessionId, Supplier<TransportActivityState> newStateSupplier) { |
|||
long newLastRecordedTime = System.currentTimeMillis(); |
|||
states.compute(sessionId, (__, activityState) -> { |
|||
if (activityState == null) { |
|||
activityState = newStateSupplier.get(); |
|||
} |
|||
if (activityState.getLastRecordedTime() < newLastRecordedTime) { |
|||
activityState.setLastRecordedTime(newLastRecordedTime); |
|||
} |
|||
return activityState; |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
protected void doOnReportingPeriodEnd() { |
|||
Set<UUID> statesToRemove = new HashSet<>(); |
|||
for (Map.Entry<UUID, TransportActivityState> entry : states.entrySet()) { |
|||
var sessionId = entry.getKey(); |
|||
var activityState = entry.getValue(); |
|||
|
|||
SessionMetaData sessionMetaData = transportService.getSession(sessionId); |
|||
if (sessionMetaData != null) { |
|||
activityState.setSessionInfoProto(sessionMetaData.getSessionInfo()); |
|||
} else { |
|||
log.debug("[{}] Session with id: [{}] is not present. Marking it's activity state for removal.", name, sessionId); |
|||
statesToRemove.add(sessionId); |
|||
} |
|||
|
|||
long lastActivityTime = activityState.getLastRecordedTime(); |
|||
TransportProtos.SessionInfoProto sessionInfo = activityState.getSessionInfoProto(); |
|||
|
|||
if (sessionInfo.getGwSessionIdMSB() != 0 && sessionInfo.getGwSessionIdLSB() != 0) { |
|||
var gwSessionId = new UUID(sessionInfo.getGwSessionIdMSB(), sessionInfo.getGwSessionIdLSB()); |
|||
SessionMetaData gwSessionMetaData = transportService.getSession(gwSessionId); |
|||
if (gwSessionMetaData != null && gwSessionMetaData.isOverwriteActivityTime()) { |
|||
TransportActivityState gwActivityState = states.get(gwSessionId); |
|||
if (gwActivityState != null) { |
|||
log.debug("[{}] Session with id: [{}] has gateway session with id: [{}] with overwrite activity time enabled. Updating last activity time.", name, sessionId, gwSessionId); |
|||
lastActivityTime = Math.max(gwActivityState.getLastRecordedTime(), lastActivityTime); |
|||
} |
|||
} |
|||
} |
|||
|
|||
long expirationTime = System.currentTimeMillis() - sessionInactivityTimeout; |
|||
boolean hasExpired = sessionMetaData != null && lastActivityTime < expirationTime; |
|||
if (hasExpired) { |
|||
log.debug("[{}] Session with id: [{}] has expired due to last activity time: [{}]. Marking it's activity state for removal.", name, sessionId, lastActivityTime); |
|||
transportService.deregisterSession(sessionInfo); |
|||
statesToRemove.add(sessionId); |
|||
transportService.process(sessionInfo, SESSION_EVENT_MSG_CLOSED, null); |
|||
sessionMetaData.getListener().onRemoteSessionCloseCommand(sessionId, SESSION_EXPIRED_NOTIFICATION_PROTO); |
|||
} |
|||
if (activityState.getLastReportedTime() < lastActivityTime) { |
|||
log.debug("[{}] Going to report last activity event for session with id: [{}].", name, sessionId); |
|||
reporter.report(sessionId, lastActivityTime, activityState, new ActivityReportCallback<>() { |
|||
@Override |
|||
public void onSuccess(UUID key, long reportedTime) { |
|||
updateLastReportedTime(key, reportedTime); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(UUID key, Throwable t) { |
|||
log.debug("[{}] Failed to report last activity event for session with id: [{}].", name, sessionId); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
statesToRemove.forEach(states::remove); |
|||
} |
|||
|
|||
private void updateLastReportedTime(UUID key, long newLastReportedTime) { |
|||
states.computeIfPresent(key, (__, activityState) -> { |
|||
activityState.setLastReportedTime(Math.max(activityState.getLastReportedTime(), newLastReportedTime)); |
|||
return activityState; |
|||
}); |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue