Commit 9d14a38966c5351edd16c01b339a9ff3c19e88e7

Authored by Andrii Shvaika
1 parent 9ec4b776

Tenant Profile updates for ApiUsageStateService

... ... @@ -40,8 +40,6 @@ import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
40 40 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
41 41 import org.thingsboard.server.common.msg.queue.RuleEngineException;
42 42 import org.thingsboard.server.common.msg.queue.ServiceType;
43   -import org.thingsboard.server.dao.model.ModelConstants;
44   -import org.thingsboard.server.dao.tenant.TenantProfileService;
45 43 import org.thingsboard.server.dao.tenant.TenantService;
46 44 import org.thingsboard.server.service.profile.TbTenantProfileCache;
47 45 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
... ... @@ -150,15 +148,12 @@ public class AppActor extends ContextAwareActor {
150 148 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
151 149 TbActorRef target = null;
152 150 if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) {
153   - if (msg.getEntityId().getEntityType() == EntityType.TENANT_PROFILE) {
154   - tenantProfileCache.evict(new TenantProfileId(msg.getEntityId().getId()));
155   - } else {
  151 + if (!EntityType.TENANT_PROFILE.equals(msg.getEntityId().getEntityType())) {
156 152 log.warn("Message has system tenant id: {}", msg);
157 153 }
158 154 } else {
159   - if (msg.getEntityId().getEntityType() == EntityType.TENANT) {
  155 + if (EntityType.TENANT.equals(msg.getEntityId().getEntityType())) {
160 156 TenantId tenantId = new TenantId(msg.getEntityId().getId());
161   - tenantProfileCache.evict(tenantId);
162 157 if (msg.getEvent() == ComponentLifecycleEvent.DELETED) {
163 158 log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg);
164 159 deletedTenants.add(tenantId);
... ...
... ... @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.ApiUsageRecordKey;
22 22 import org.thingsboard.server.common.data.ApiUsageState;
23 23 import org.thingsboard.server.common.data.TenantProfile;
24 24 import org.thingsboard.server.common.data.id.TenantId;
  25 +import org.thingsboard.server.common.data.id.TenantProfileId;
25 26 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
26 27 import org.thingsboard.server.common.data.kv.LongDataEntry;
27 28 import org.thingsboard.server.common.data.kv.TsKvEntry;
... ... @@ -87,6 +88,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
87 88 TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB()));
88 89 TenantApiUsageState tenantState;
89 90 List<TsKvEntry> updatedEntries;
  91 + boolean stateUpdated = false;
90 92 updateLock.lock();
91 93 try {
92 94 tenantState = getOrFetchState(tenantId);
... ... @@ -101,13 +103,21 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
101 103 ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey());
102 104 long newValue = tenantState.add(recordKey, kvProto.getValue());
103 105 updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.name(), newValue)));
104   - newValue = tenantState.addToHourly(recordKey, kvProto.getValue());
105   - updatedEntries.add(new BasicTsKvEntry(hourTs, new LongDataEntry(HOURLY + recordKey.name(), newValue)));
  106 + long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue());
  107 + updatedEntries.add(new BasicTsKvEntry(hourTs, new LongDataEntry(HOURLY + recordKey.name(), newHourlyValue)));
  108 + stateUpdated |= tenantState.checkStateUpdatedDueToThreshold(recordKey);
106 109 }
107 110 } finally {
108 111 updateLock.unlock();
109 112 }
110 113 tsService.save(tenantId, tenantState.getEntityId(), updatedEntries, 0L);
  114 + if (stateUpdated) {
  115 + // Save new state into the database;
  116 + apiUsageStateService.update(tenantState.getApiUsageState());
  117 + //TODO: clear cache on cluster repartition.
  118 + //TODO: update profiles on tenant and profile updates.
  119 + //TODO: broadcast to everyone notifications about enabled/disabled features.
  120 + }
111 121 }
112 122
113 123 @Override
... ... @@ -116,12 +126,26 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
116 126 }
117 127
118 128 @Override
119   - public void onAddedToAllowList(TenantId tenantId) {
120   -
  129 + public void onTenantProfileUpdate(TenantProfileId tenantProfileId) {
  130 + TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId);
  131 + updateLock.lock();
  132 + try {
  133 + tenantStates.values().forEach(state -> {
  134 + if (tenantProfile.getId().equals(state.getTenantProfileId())) {
  135 + state.setTenantProfileData(tenantProfile.getProfileData());
  136 + if (state.checkStateUpdatedDueToThresholds()) {
  137 + apiUsageStateService.update(state.getApiUsageState());
  138 + //TODO: send notification to cluster;
  139 + }
  140 + }
  141 + });
  142 + } finally {
  143 + updateLock.unlock();
  144 + }
121 145 }
122 146
123 147 @Override
124   - public void onAddedToDenyList(TenantId tenantId) {
  148 + public void onTenantUpdate(TenantId tenantId) {
125 149
126 150 }
127 151
... ... @@ -150,7 +174,8 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
150 174 dbStateEntity = apiUsageStateService.findTenantApiUsageState(tenantId);
151 175 }
152 176 }
153   - tenantState = new TenantApiUsageState(dbStateEntity.getEntityId());
  177 + TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
  178 + tenantState = new TenantApiUsageState(tenantProfile, dbStateEntity);
154 179 try {
155 180 List<TsKvEntry> dbValues = tsService.findAllLatest(tenantId, dbStateEntity.getEntityId()).get();
156 181 for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.service.apiusage;
17 17
18 18 import org.thingsboard.server.common.data.id.TenantId;
  19 +import org.thingsboard.server.common.data.id.TenantProfileId;
19 20 import org.thingsboard.server.common.msg.queue.TbCallback;
20 21 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
21 22 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
... ... @@ -26,8 +27,7 @@ public interface TbApiUsageStateService {
26 27
27 28 TenantApiUsageState getApiUsageState(TenantId tenantId);
28 29
29   - void onAddedToAllowList(TenantId tenantId);
30   -
31   - void onAddedToDenyList(TenantId tenantId);
  30 + void onTenantProfileUpdate(TenantProfileId tenantProfileId);
32 31
  32 + void onTenantUpdate(TenantId tenantId);
33 33 }
... ...
... ... @@ -16,8 +16,13 @@
16 16 package org.thingsboard.server.service.apiusage;
17 17
18 18 import lombok.Getter;
  19 +import lombok.Setter;
19 20 import org.thingsboard.server.common.data.ApiUsageRecordKey;
  21 +import org.thingsboard.server.common.data.ApiUsageState;
  22 +import org.thingsboard.server.common.data.TenantProfile;
  23 +import org.thingsboard.server.common.data.TenantProfileData;
20 24 import org.thingsboard.server.common.data.id.EntityId;
  25 +import org.thingsboard.server.common.data.id.TenantProfileId;
21 26 import org.thingsboard.server.common.msg.tools.SchedulerUtils;
22 27
23 28 import java.util.Map;
... ... @@ -29,7 +34,13 @@ public class TenantApiUsageState {
29 34 private final Map<ApiUsageRecordKey, Long> currentHourValues = new ConcurrentHashMap<>();
30 35
31 36 @Getter
32   - private final EntityId entityId;
  37 + @Setter
  38 + private TenantProfileId tenantProfileId;
  39 + @Getter
  40 + @Setter
  41 + private TenantProfileData tenantProfileData;
  42 + @Getter
  43 + private final ApiUsageState apiUsageState;
33 44 @Getter
34 45 private volatile long currentCycleTs;
35 46 @Getter
... ... @@ -37,8 +48,10 @@ public class TenantApiUsageState {
37 48 @Getter
38 49 private volatile long currentHourTs;
39 50
40   - public TenantApiUsageState(EntityId entityId) {
41   - this.entityId = entityId;
  51 + public TenantApiUsageState(TenantProfile tenantProfile, ApiUsageState apiUsageState) {
  52 + this.tenantProfileId = tenantProfile.getId();
  53 + this.tenantProfileData = tenantProfile.getProfileData();
  54 + this.apiUsageState = apiUsageState;
42 55 this.currentCycleTs = SchedulerUtils.getStartOfCurrentMonth();
43 56 this.nextCycleTs = SchedulerUtils.getStartOfNextMonth();
44 57 this.currentHourTs = SchedulerUtils.getStartOfCurrentHour();
... ... @@ -58,6 +71,10 @@ public class TenantApiUsageState {
58 71 return result;
59 72 }
60 73
  74 + public long get(ApiUsageRecordKey key) {
  75 + return currentCycleValues.getOrDefault(key, 0L);
  76 + }
  77 +
61 78 public long addToHourly(ApiUsageRecordKey key, long value) {
62 79 long result = currentHourValues.getOrDefault(key, 0L) + value;
63 80 currentHourValues.put(key, result);
... ... @@ -79,4 +96,103 @@ public class TenantApiUsageState {
79 96 }
80 97 }
81 98
  99 + public long getProfileThreshold(ApiUsageRecordKey key) {
  100 + Object threshold = tenantProfileData.getProperties().get(key.name());
  101 + if (threshold != null) {
  102 + if (threshold instanceof String) {
  103 + return Long.parseLong((String) threshold);
  104 + } else if (threshold instanceof Long) {
  105 + return (Long) threshold;
  106 + }
  107 + }
  108 + return 0L;
  109 + }
  110 +
  111 + public EntityId getEntityId() {
  112 + return apiUsageState.getEntityId();
  113 + }
  114 +
  115 + public boolean isTransportEnabled() {
  116 + return apiUsageState.isTransportEnabled();
  117 + }
  118 +
  119 + public boolean isDbStorageEnabled() {
  120 + return apiUsageState.isDbStorageEnabled();
  121 + }
  122 +
  123 + public boolean isRuleEngineEnabled() {
  124 + return apiUsageState.isRuleEngineEnabled();
  125 + }
  126 +
  127 + public boolean isJsExecEnabled() {
  128 + return apiUsageState.isJsExecEnabled();
  129 + }
  130 +
  131 + public void setTransportEnabled(boolean transportEnabled) {
  132 + apiUsageState.setTransportEnabled(transportEnabled);
  133 + }
  134 +
  135 + public void setDbStorageEnabled(boolean dbStorageEnabled) {
  136 + apiUsageState.setDbStorageEnabled(dbStorageEnabled);
  137 + }
  138 +
  139 + public void setRuleEngineEnabled(boolean ruleEngineEnabled) {
  140 + apiUsageState.setRuleEngineEnabled(ruleEngineEnabled);
  141 + }
  142 +
  143 + public void setJsExecEnabled(boolean jsExecEnabled) {
  144 + apiUsageState.setJsExecEnabled(jsExecEnabled);
  145 + }
  146 +
  147 + public boolean isFeatureEnabled(ApiUsageRecordKey recordKey) {
  148 + switch (recordKey) {
  149 + case MSG_COUNT:
  150 + case MSG_BYTES_COUNT:
  151 + case DP_TRANSPORT_COUNT:
  152 + return isTransportEnabled();
  153 + case RE_EXEC_COUNT:
  154 + return isRuleEngineEnabled();
  155 + case DP_STORAGE_COUNT:
  156 + return isDbStorageEnabled();
  157 + case JS_EXEC_COUNT:
  158 + return isJsExecEnabled();
  159 + default:
  160 + return true;
  161 + }
  162 + }
  163 +
  164 + public boolean setFeatureValue(ApiUsageRecordKey recordKey, boolean value) {
  165 + boolean currentValue = isFeatureEnabled(recordKey);
  166 + switch (recordKey) {
  167 + case MSG_COUNT:
  168 + case MSG_BYTES_COUNT:
  169 + case DP_TRANSPORT_COUNT:
  170 + setTransportEnabled(value);
  171 + break;
  172 + case RE_EXEC_COUNT:
  173 + setRuleEngineEnabled(value);
  174 + break;
  175 + case DP_STORAGE_COUNT:
  176 + setDbStorageEnabled(value);
  177 + break;
  178 + case JS_EXEC_COUNT:
  179 + setJsExecEnabled(value);
  180 + break;
  181 + }
  182 + return currentValue == value;
  183 + }
  184 +
  185 + public boolean checkStateUpdatedDueToThresholds() {
  186 + boolean update = false;
  187 + for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
  188 + update |= checkStateUpdatedDueToThreshold(key);
  189 + }
  190 + return update;
  191 + }
  192 +
  193 + public boolean checkStateUpdatedDueToThreshold(ApiUsageRecordKey recordKey) {
  194 + long value = get(recordKey);
  195 + long threshold = getProfileThreshold(recordKey);
  196 + return setFeatureValue(recordKey, threshold == 0 || value < threshold);
  197 + }
82 198 }
... ...
... ... @@ -107,7 +107,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
107 107 SubscriptionManagerService subscriptionManagerService, DataDecodingEncodingService encodingService,
108 108 TbCoreDeviceRpcService tbCoreDeviceRpcService, StatsFactory statsFactory, TbDeviceProfileCache deviceProfileCache,
109 109 TbApiUsageStateService statsService) {
110   - super(actorContext, encodingService, deviceProfileCache, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer());
  110 + super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, apiUsageStateService, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer());
111 111 this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer();
112 112 this.usageStatsConsumer = tbCoreQueueFactory.createToUsageStatsServiceMsgConsumer();
113 113 this.stateService = stateService;
... ...
... ... @@ -15,7 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.service.queue;
17 17
18   -import com.google.protobuf.ByteString;
19 18 import com.google.protobuf.ProtocolStringList;
20 19 import lombok.extern.slf4j.Slf4j;
21 20 import org.springframework.beans.factory.annotation.Value;
... ... @@ -24,7 +23,6 @@ import org.springframework.stereotype.Service;
24 23 import org.thingsboard.rule.engine.api.RpcError;
25 24 import org.thingsboard.server.actors.ActorSystemContext;
26 25 import org.thingsboard.server.common.data.id.TenantId;
27   -import org.thingsboard.server.common.msg.TbActorMsg;
28 26 import org.thingsboard.server.common.msg.TbMsg;
29 27 import org.thingsboard.server.common.msg.queue.*;
30 28 import org.thingsboard.server.common.stats.StatsFactory;
... ... @@ -83,7 +81,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
83 81 ActorSystemContext actorContext, DataDecodingEncodingService encodingService,
84 82 TbRuleEngineDeviceRpcService tbDeviceRpcService,
85 83 StatsFactory statsFactory, TbDeviceProfileCache deviceProfileCache) {
86   - super(actorContext, encodingService, deviceProfileCache, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer());
  84 + super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, apiUsageStateService, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer());
87 85 this.statisticsService = statisticsService;
88 86 this.ruleEngineSettings = ruleEngineSettings;
89 87 this.tbRuleEngineQueueFactory = tbRuleEngineQueueFactory;
... ...
... ... @@ -25,6 +25,7 @@ import org.thingsboard.server.actors.ActorSystemContext;
25 25 import org.thingsboard.server.common.data.EntityType;
26 26 import org.thingsboard.server.common.data.id.DeviceId;
27 27 import org.thingsboard.server.common.data.id.DeviceProfileId;
  28 +import org.thingsboard.server.common.data.id.TenantProfileId;
28 29 import org.thingsboard.server.common.msg.TbActorMsg;
29 30 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
30 31 import org.thingsboard.server.common.msg.queue.ServiceType;
... ... @@ -33,7 +34,9 @@ import org.thingsboard.server.queue.TbQueueConsumer;
33 34 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
34 35 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
35 36 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
  37 +import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
36 38 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
  39 +import org.thingsboard.server.service.profile.TbTenantProfileCache;
37 40 import org.thingsboard.server.service.queue.TbPackCallback;
38 41 import org.thingsboard.server.service.queue.TbPackProcessingContext;
39 42
... ... @@ -59,15 +62,19 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
59 62
60 63 protected final ActorSystemContext actorContext;
61 64 protected final DataDecodingEncodingService encodingService;
  65 + protected final TbTenantProfileCache tenantProfileCache;
62 66 protected final TbDeviceProfileCache deviceProfileCache;
  67 + protected final TbApiUsageStateService apiUsageStateService;
63 68
64 69 protected final TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer;
65 70
66 71 public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService,
67   - TbDeviceProfileCache deviceProfileCache, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) {
  72 + TbTenantProfileCache tenantProfileCache, TbDeviceProfileCache deviceProfileCache, TbApiUsageStateService apiUsageStateService, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) {
68 73 this.actorContext = actorContext;
69 74 this.encodingService = encodingService;
  75 + this.tenantProfileCache = tenantProfileCache;
70 76 this.deviceProfileCache = deviceProfileCache;
  77 + this.apiUsageStateService = apiUsageStateService;
71 78 this.nfConsumer = nfConsumer;
72 79 }
73 80
... ... @@ -143,7 +150,14 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
143 150 TbActorMsg actorMsg = actorMsgOpt.get();
144 151 if (actorMsg instanceof ComponentLifecycleMsg) {
145 152 ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg;
146   - if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
  153 + if (EntityType.TENANT_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
  154 + TenantProfileId tenantProfileId = new TenantProfileId(componentLifecycleMsg.getEntityId().getId());
  155 + tenantProfileCache.evict(tenantProfileId);
  156 + apiUsageStateService.onTenantProfileUpdate(tenantProfileId);
  157 + } else if (EntityType.TENANT.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
  158 + tenantProfileCache.evict(componentLifecycleMsg.getTenantId());
  159 + apiUsageStateService.onTenantUpdate(componentLifecycleMsg.getTenantId());
  160 + } else if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
147 161 deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceProfileId(componentLifecycleMsg.getEntityId().getId()));
148 162 } else if (EntityType.DEVICE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
149 163 deviceProfileCache.evict(new DeviceId(componentLifecycleMsg.getEntityId().getId()));
... ...
... ... @@ -20,9 +20,12 @@ import org.thingsboard.server.common.data.id.TenantId;
20 20
21 21 public interface ApiUsageStateService {
22 22
  23 + ApiUsageState createDefaultApiUsageState(TenantId id);
  24 +
  25 + ApiUsageState update(ApiUsageState apiUsageState);
  26 +
23 27 ApiUsageState findTenantApiUsageState(TenantId tenantId);
24 28
25 29 void deleteApiUsageStateByTenantId(TenantId tenantId);
26 30
27   - ApiUsageState createDefaultApiUsageState(TenantId id);
28 31 }
... ...
... ... @@ -16,6 +16,8 @@
16 16 package org.thingsboard.server.common.data;
17 17
18 18 import lombok.EqualsAndHashCode;
  19 +import lombok.Getter;
  20 +import lombok.Setter;
19 21 import lombok.ToString;
20 22 import org.thingsboard.server.common.data.id.EntityId;
21 23 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -27,8 +29,18 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
27 29
28 30 private static final long serialVersionUID = 8250339805336035966L;
29 31
  32 + @Getter @Setter
30 33 private TenantId tenantId;
  34 + @Getter @Setter
31 35 private EntityId entityId;
  36 + @Getter @Setter
  37 + private boolean transportEnabled;
  38 + @Getter @Setter
  39 + private boolean dbStorageEnabled;
  40 + @Getter @Setter
  41 + private boolean ruleEngineEnabled;
  42 + @Getter @Setter
  43 + private boolean jsExecEnabled;
32 44
33 45 public ApiUsageState() {
34 46 super();
... ... @@ -43,22 +55,4 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
43 55 this.tenantId = ur.getTenantId();
44 56 this.entityId = ur.getEntityId();
45 57 }
46   -
47   - @Override
48   - public TenantId getTenantId() {
49   - return tenantId;
50   - }
51   -
52   - public void setTenantId(TenantId tenantId) {
53   - this.tenantId = tenantId;
54   - }
55   -
56   - public EntityId getEntityId() {
57   - return entityId;
58   - }
59   -
60   - public void setEntityId(EntityId entityId) {
61   - this.entityId = entityId;
62   - }
63   -
64 58 }
... ...
... ... @@ -60,6 +60,14 @@ public class ApiApiUsageStateServiceImpl extends AbstractEntityService implement
60 60 }
61 61
62 62 @Override
  63 + public ApiUsageState update(ApiUsageState apiUsageState) {
  64 + log.trace("Executing save [{}]", apiUsageState.getTenantId());
  65 + validateId(apiUsageState.getTenantId(), INCORRECT_TENANT_ID + apiUsageState.getTenantId());
  66 + validateId(apiUsageState.getId(), "Can't save new usage state. Only update is allowed!");
  67 + return apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState);
  68 + }
  69 +
  70 + @Override
63 71 public ApiUsageState findTenantApiUsageState(TenantId tenantId) {
64 72 log.trace("Executing findTenantUsageRecord, tenantId [{}]", tenantId);
65 73 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
... ...