66import com .milesight .beaveriot .context .constants .ExchangeContextKeys ;
77import com .milesight .beaveriot .context .integration .model .Device ;
88import com .milesight .beaveriot .context .integration .model .ExchangePayload ;
9+ import com .milesight .beaveriot .context .security .TenantContext ;
910import com .milesight .beaveriot .eventbus .annotations .EventSubscribe ;
1011import com .milesight .beaveriot .eventbus .api .Event ;
1112import com .milesight .beaveriot .integration .msc .constant .MscIntegrationConstants ;
2526import javax .annotation .Nonnull ;
2627import javax .annotation .Nullable ;
2728import java .io .IOException ;
29+ import java .util .Map ;
2830import java .util .Optional ;
29- import java .util .Timer ;
30- import java .util .TimerTask ;
3131import java .util .concurrent .CompletableFuture ;
3232import java .util .concurrent .ConcurrentHashMap ;
3333import java .util .concurrent .ExecutorService ;
@@ -57,9 +57,10 @@ public class MscDataSyncService {
5757 @ Autowired
5858 private EntityValueServiceProvider entityValueServiceProvider ;
5959
60- private Timer timer ;
60+ @ Autowired
61+ private MscTimerService mscTimerService ;
6162
62- private int periodSeconds = 0 ;
63+ private final Map < String , Long > tenantIdToPeriodSecond = new ConcurrentHashMap <>() ;
6364
6465 // Only two existing tasks allowed at a time (one running and one waiting)
6566 private static final ExecutorService syncAllDataExecutor = new ThreadPoolExecutor (1 , 1 ,
@@ -75,18 +76,23 @@ public class MscDataSyncService {
7576
7677 @ EventSubscribe (payloadKeyExpression = "msc-integration.integration.scheduled_data_fetch.*" )
7778 public void onScheduledDataFetchPropertiesUpdate (Event <MscConnectionPropertiesEntities .ScheduledDataFetch > event ) {
79+ val tenantId = TenantContext .getTenantId ();
7880 if (event .getPayload ().getPeriod () != null ) {
79- periodSeconds = event .getPayload ().getPeriod ();
81+ tenantIdToPeriodSecond . put ( tenantId , event .getPayload ().getPeriod (). longValue () );
8082 }
81- restart ();
83+ restart (tenantId );
8284 }
8385
8486 @ EventSubscribe (payloadKeyExpression = "msc-integration.integration.openapi_status" )
8587 public void onOpenapiStatusUpdate (Event <MscConnectionPropertiesEntities > event ) {
8688 val status = event .getPayload ().getOpenapiStatus ();
8789 if (IntegrationStatus .READY .name ().equals (status )) {
8890 try {
89- syncAllDataExecutor .submit (this ::syncDeltaData );
91+ val tenantId = TenantContext .getTenantId ();
92+ syncAllDataExecutor .submit (() -> {
93+ TenantContext .setTenantId (tenantId );
94+ this .syncDeltaData ();
95+ });
9096 } catch (RejectedExecutionException e ) {
9197 log .error ("Task rejected: " , e );
9298 }
@@ -96,66 +102,77 @@ public void onOpenapiStatusUpdate(Event<MscConnectionPropertiesEntities> event)
96102 @ SneakyThrows
97103 @ EventSubscribe (payloadKeyExpression = "msc-integration.integration.sync_device" )
98104 public void onSyncDevice (Event <MscServiceEntities .SyncDevice > event ) {
99- syncAllDataExecutor .submit (this ::syncAllData ).get ();
105+ val tenantId = TenantContext .getTenantId ();
106+ syncAllDataExecutor .submit (() -> {
107+ TenantContext .setTenantId (tenantId );
108+ this .syncAllData ();
109+ }).get ();
100110 }
101111
102112
103- public void restart () {
104- stop ();
105- start ();
113+ public void restart (String tenantId ) {
114+ stop (tenantId );
115+ start (tenantId );
106116 }
107117
108118 public void stop () {
109- if (timer != null ) {
110- timer .cancel ();
111- timer = null ;
112- }
113- log .info ("timer stopped" );
119+ mscTimerService .clear ();
120+ }
121+
122+ public void stop (String tenantId ) {
123+ mscTimerService .cancelTask (tenantId );
124+ log .info ("msc-integration timer stopped, tenant: '{}'" , tenantId );
114125 }
115126
116- public void init () {
117- start ();
127+ public void init (String tenantId ) {
128+ start (tenantId );
118129 }
119130
120- public void start () {
121- log .info ("timer starting" );
122- if (timer != null ) {
131+ public void start (String tenantId ) {
132+ log .info ("msc-integration timer starting, tenant: '{}'" , tenantId );
133+ if (mscTimerService . isScheduled ( tenantId ) ) {
123134 return ;
124135 }
125- if (periodSeconds == 0 ) {
136+ long periodSecond = tenantIdToPeriodSecond .getOrDefault (tenantId , 0L );
137+ if (periodSecond == 0 ) {
126138 val scheduledDataFetchSettings = entityValueServiceProvider .findValuesByKey (
127139 MscConnectionPropertiesEntities .getKey (MscConnectionPropertiesEntities .Fields .scheduledDataFetch ),
128140 MscConnectionPropertiesEntities .ScheduledDataFetch .class );
141+
129142 if (scheduledDataFetchSettings .isEmpty ()) {
130- periodSeconds = -1 ;
143+ periodSecond = -1L ;
144+ tenantIdToPeriodSecond .put (tenantId , periodSecond );
131145 return ;
132146 }
147+
133148 if (!Boolean .TRUE .equals (scheduledDataFetchSettings .getEnabled ())
134149 || scheduledDataFetchSettings .getPeriod () == null
135150 || scheduledDataFetchSettings .getPeriod () == 0 ) {
136151 // not enabled or invalid period
137- periodSeconds = -1 ;
152+ periodSecond = -1L ;
153+ tenantIdToPeriodSecond .put (tenantId , periodSecond );
138154 } else if (scheduledDataFetchSettings .getPeriod () > 0 ) {
139- periodSeconds = scheduledDataFetchSettings .getPeriod ();
155+ periodSecond = scheduledDataFetchSettings .getPeriod ();
156+ tenantIdToPeriodSecond .put (tenantId , periodSecond );
140157 }
141158 }
142- if (periodSeconds < 0 ) {
159+
160+ if (periodSecond < 0 ) {
143161 return ;
144162 }
145- timer = new Timer ();
146163
147164 // setup timer
148- val periodMills = periodSeconds * 1000L ;
149- timer . scheduleAtFixedRate ( new TimerTask () {
150- @ Override
151- public void run () {
152- try {
153- syncAllDataExecutor . submit (() -> syncDeltaData () );
154- } catch ( RejectedExecutionException e ) {
155- log . error ( "Task rejected: " , e );
156- }
165+ TenantContext . getTenantId () ;
166+ mscTimerService . scheduleTask (() -> {
167+ try {
168+ syncAllDataExecutor . submit (() -> {
169+ TenantContext . setTenantId ( tenantId );
170+ this . syncDeltaData ();
171+ });
172+ } catch ( RejectedExecutionException e ) {
173+ log . error ( "Task rejected: " , e );
157174 }
158- }, periodMills , periodMills );
175+ }, tenantId , periodSecond , periodSecond );
159176
160177 log .info ("timer started" );
161178 }
@@ -296,13 +313,17 @@ public CompletableFuture<Boolean> syncDeviceData(Task task) {
296313 log .info ("Skip execution because device task is running: {}" , task .identifier );
297314 return CompletableFuture .completedFuture (null );
298315 }
316+ val tenantId = TenantContext .getTenantId ();
299317 return CompletableFuture .supplyAsync (() -> {
318+ TenantContext .setTenantId (tenantId );
300319 try {
301320 Device device = null ;
302321 switch (task .type ) {
303- case REMOVE_LOCAL_DEVICE -> device = removeLocalDevice (task .identifier );
304322 case ADD_LOCAL_DEVICE -> device = addLocalDevice (task );
305323 case UPDATE_LOCAL_DEVICE -> device = updateLocalDevice (task );
324+ default -> {
325+ // do nothing
326+ }
306327 }
307328
308329 if (task .type != Task .Type .REMOVE_LOCAL_DEVICE && device == null ) {
@@ -357,21 +378,15 @@ private void syncPropertiesHistory(Device device, long lastSyncTime) {
357378 page .getData ().getList ().forEach (item -> {
358379 val objectMapper = mscClientProvider .getMscClient ().getObjectMapper ();
359380 val properties = objectMapper .convertValue (item .getProperties (), JsonNode .class );
360- saveHistoryData (device .getKey (), null , properties , truncateTimestampMs (item .getTs ()), isLatestData .get ());
381+ val timestamp = item .getTs () != null ? item .getTs () : TimeUtils .currentTimeMillis ();
382+ saveHistoryData (device .getKey (), null , properties , timestamp , isLatestData .get ());
361383 if (isLatestData .get ()) {
362384 isLatestData .set (false );
363385 }
364386 });
365387 }
366388 }
367389
368- private static long truncateTimestampMs (Long ts ) {
369- if (ts == null ) {
370- return TimeUtils .currentTimeMillis ();
371- }
372- return ts - ts % 1000 ;
373- }
374-
375390 public void saveHistoryData (String deviceKey , String eventId , JsonNode data , long timestampMs , boolean isLatestData ) {
376391 val payload = eventId == null
377392 ? MscTslUtils .convertJsonNodeToExchangePayload (deviceKey , data )
@@ -427,11 +442,6 @@ private DeviceDetailResponse getDeviceDetails(Task task)
427442 return details ;
428443 }
429444
430- private Device removeLocalDevice (String identifier ) {
431- // delete is unsupported currently
432- return null ;
433- }
434-
435445
436446 public record Task (@ Nonnull Type type , @ Nonnull String identifier , @ Nullable DeviceDetailResponse details ) {
437447
0 commit comments