Add support for pollingTime overrides (#2533)
* Add support for pollingTime overrides
* the current format HH:mm:ss is still supported
* add option for deviation percent (HH:mm:ss~\d{1,2}%) which allows the system to do some randomizing of the poll interval
* add support for overriding default polling time interval for devices matching some RSQL filters (in order), e.g. 01:00:00~10%, group == 'eu' -> 00:02:00~15%, status != in_sync -> 00:05:00
* IMPORTANT: overdue time is calculated according to the default polling time. So, the overdue status might be incorrect for targets with overridden poll interval
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
* Remove min polling time from the tenant config - it is a system configuration
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
* Add support for bigger poll intervals and overdue + duration format config support
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
---------
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -161,6 +161,7 @@ import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -850,16 +851,15 @@ public class RepositoryApplicationConfiguration {
|
||||
final DeploymentManagement deploymentManagement, final ConfirmationManagement confirmationManagement,
|
||||
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
|
||||
final DistributionSetManagement distributionSetManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties,
|
||||
final PlatformTransactionManager txManager, final EntityFactory entityFactory, final EntityManager entityManager,
|
||||
final AfterTransactionCommitExecutor afterCommit,
|
||||
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware,
|
||||
final ScheduledExecutorService executorService) {
|
||||
return new JpaControllerManagement(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties,
|
||||
targetRepository, targetTypeManagement, deploymentManagement, confirmationManagement, softwareModuleRepository,
|
||||
softwareModuleMetadataRepository, distributionSetManagement, tenantConfigurationManagement, txManager,
|
||||
entityFactory, entityManager, afterCommit, systemSecurityContext, tenantAware,
|
||||
executorService);
|
||||
softwareModuleMetadataRepository, distributionSetManagement, tenantConfigurationManagement, controllerPollProperties,
|
||||
txManager,entityFactory, entityManager, afterCommit, systemSecurityContext, tenantAware, executorService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -79,6 +79,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.jpa.ql.EntityMatcher;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
|
||||
@@ -101,6 +102,9 @@ import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -116,6 +120,7 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -140,6 +145,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final ControllerPollProperties controllerPollProperties;
|
||||
private final Duration minPollingTime, maxPollingTime;
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final EntityFactory entityFactory;
|
||||
private final EntityManager entityManager;
|
||||
@@ -155,7 +162,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
final DeploymentManagement deploymentManagement, final ConfirmationManagement confirmationManagement,
|
||||
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
|
||||
final DistributionSetManagement distributionSetManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties,
|
||||
final PlatformTransactionManager txManager, final EntityFactory entityFactory, final EntityManager entityManager,
|
||||
final AfterTransactionCommitExecutor afterCommit,
|
||||
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware,
|
||||
@@ -170,6 +177,13 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
this.softwareModuleMetadataRepository = softwareModuleMetadataRepository;
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.controllerPollProperties = controllerPollProperties;
|
||||
minPollingTime = controllerPollProperties.getMinPollingTime() == null
|
||||
? Duration.of(0, ChronoUnit.SECONDS)
|
||||
: DurationHelper.fromString(controllerPollProperties.getMinPollingTime());
|
||||
maxPollingTime = controllerPollProperties.getMaxPollingTime() == null
|
||||
? Duration.of(100, ChronoUnit.YEARS)
|
||||
: DurationHelper.fromString(controllerPollProperties.getMaxPollingTime());
|
||||
this.txManager = txManager;
|
||||
this.entityFactory = entityFactory;
|
||||
this.entityManager = entityManager;
|
||||
@@ -345,6 +359,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address, final String name, final String type) {
|
||||
return findOrRegisterTargetIfItDoesNotExist0(controllerId, address, name, type);
|
||||
}
|
||||
|
||||
private Target findOrRegisterTargetIfItDoesNotExist0(final String controllerId, final URI address, final String name, final String type) {
|
||||
final Specification<JpaTarget> spec = (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
|
||||
return targetRepository.findOne(spec)
|
||||
@@ -373,42 +388,39 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPollingTime() {
|
||||
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
|
||||
.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured minimum polling interval.
|
||||
*
|
||||
* @return current {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}.
|
||||
*/
|
||||
@Override
|
||||
public String getMinPollingTime() {
|
||||
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
|
||||
.getConfigurationValue(TenantConfigurationKey.MIN_POLLING_TIME_INTERVAL, String.class).getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count to be used for reducing polling interval while calling {@link ControllerManagement#getPollingTimeForAction(Action)}.
|
||||
*
|
||||
* @return configured value of
|
||||
* {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}.
|
||||
*/
|
||||
@Override
|
||||
public int getMaintenanceWindowPollCount() {
|
||||
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
|
||||
.getConfigurationValue(TenantConfigurationKey.MAINTENANCE_WINDOW_POLL_COUNT, Integer.class).getValue());
|
||||
public String getPollingTime(final Target target) {
|
||||
return systemSecurityContext.runAsSystem(() -> {
|
||||
final PollingTime pollingTime = new PollingTime(
|
||||
tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.POLLING_TIME, String.class).getValue());
|
||||
if (!ObjectUtils.isEmpty(pollingTime.getOverrides()) && target instanceof JpaTarget jpaTarget) {
|
||||
for (final PollingTime.Override override : pollingTime.getOverrides()) {
|
||||
try {
|
||||
if (EntityMatcher.forRsql(override.qlStr()).match(jpaTarget)) {
|
||||
return override.pollingInterval().getFormattedIntervalWithDeviation(minPollingTime, maxPollingTime);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.warn("Error while evaluating polling override for target {}: {}", jpaTarget.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
// returns default - no overrides or not applicable for the target
|
||||
return pollingTime.getPollingInterval().getFormattedIntervalWithDeviation(minPollingTime, maxPollingTime);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPollingTimeForAction(final Action action) {
|
||||
public String getPollingTimeForAction(final Target target, final Action action) {
|
||||
final String pollingTime = getPollingTime(target);
|
||||
if (!action.hasMaintenanceSchedule() || action.isMaintenanceScheduleLapsed()) {
|
||||
return getPollingTime();
|
||||
return pollingTime;
|
||||
} else {
|
||||
// the count to be used for reducing polling interval -> the configured value of {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}
|
||||
final int maintenanceWindowPollCount = systemSecurityContext.runAsSystem(
|
||||
() -> tenantConfigurationManagement.getConfigurationValue(
|
||||
TenantConfigurationKey.MAINTENANCE_WINDOW_POLL_COUNT, Integer.class).getValue());
|
||||
return new EventTimer(pollingTime, controllerPollProperties.getMinPollingTime(), ChronoUnit.SECONDS)
|
||||
.timeToNextEvent(maintenanceWindowPollCount, action.getMaintenanceWindowStartTime().orElse(null));
|
||||
}
|
||||
|
||||
return new EventTimer(getPollingTime(), getMinPollingTime(), ChronoUnit.SECONDS)
|
||||
.timeToNextEvent(getMaintenanceWindowPollCount(), action.getMaintenanceWindowStartTime().orElse(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.POLLING_TIME;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -18,8 +20,8 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@@ -33,7 +35,9 @@ import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorExce
|
||||
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValueChangeNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration;
|
||||
import org.eclipse.hawkbit.repository.jpa.ql.EntityMatcher;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TenantConfigurationRepository;
|
||||
import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -42,6 +46,7 @@ import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.springframework.cache.Cache;
|
||||
@@ -55,6 +60,7 @@ import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
@@ -90,9 +96,8 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(
|
||||
final String configurationKeyName, final T value) {
|
||||
return addOrUpdateConfiguration0(Collections.singletonMap(configurationKeyName, value)).values().iterator().next();
|
||||
public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(final String configurationKeyName, final T value) {
|
||||
return addOrUpdateConfiguration0(Map.of(configurationKeyName, value)).values().iterator().next();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -138,11 +143,9 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
checkAccess(configurationKeyName);
|
||||
|
||||
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
|
||||
validateTenantConfigurationDataType(configurationKey, propertyType);
|
||||
|
||||
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository.findByKey(configurationKey.getKeyName());
|
||||
|
||||
return buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration);
|
||||
}
|
||||
|
||||
@@ -151,7 +154,6 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
checkAccess(configurationKeyName);
|
||||
|
||||
final TenantConfigurationKey key = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
|
||||
if (!key.getDataType().isAssignableFrom(propertyType)) {
|
||||
throw new TenantConfigurationValidatorException(String.format(
|
||||
"Cannot parse the database value of type %s into the type %s.", key.getDataType(), propertyType));
|
||||
@@ -162,47 +164,62 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
|
||||
@Override
|
||||
public Function<Target, PollStatus> pollStatusResolver() {
|
||||
final Duration pollTime = DurationHelper.formattedStringToDuration(
|
||||
getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue());
|
||||
final Duration overdueTime = DurationHelper.formattedStringToDuration(
|
||||
getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class)
|
||||
.getValue());
|
||||
final PollingTime pollingTime = new PollingTime(
|
||||
getConfigurationValue(TenantConfigurationKey.POLLING_TIME, String.class).getValue());
|
||||
final Duration pollingOverdueTime = DurationHelper.fromString(
|
||||
getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME, String.class).getValue());
|
||||
return target -> {
|
||||
final Long lastTargetQuery = target.getLastTargetQuery();
|
||||
if (lastTargetQuery == null) {
|
||||
return null;
|
||||
}
|
||||
final LocalDateTime currentDate = LocalDateTime.now();
|
||||
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery),
|
||||
ZoneId.systemDefault());
|
||||
final LocalDateTime nextPollDate = lastPollDate.plus(pollTime);
|
||||
final LocalDateTime overdueDate = nextPollDate.plus(overdueTime);
|
||||
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
|
||||
|
||||
if (!ObjectUtils.isEmpty(pollingTime.getOverrides()) && target instanceof JpaTarget jpaTarget) {
|
||||
for (final PollingTime.Override override : pollingTime.getOverrides()) {
|
||||
try {
|
||||
if (EntityMatcher.forRsql(override.qlStr()).match(jpaTarget)) {
|
||||
return pollStatus(lastTargetQuery, override.pollingInterval(), pollingOverdueTime);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.warn("Error while evaluating polling override for target {}: {}", jpaTarget.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
// returns default - no overrides or not applicable for the target
|
||||
return pollStatus(lastTargetQuery, pollingTime.getPollingInterval(), pollingOverdueTime);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the data type of the tenant configuration. If it is possible to
|
||||
* cast to the given data type.
|
||||
*
|
||||
* @param configurationKey the key
|
||||
* @param propertyType the class
|
||||
*/
|
||||
private static <T> void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey,
|
||||
final Class<T> propertyType) {
|
||||
private static PollStatus pollStatus(
|
||||
final long lastTargetQuery,
|
||||
final PollingTime.PollingInterval pollingInterval, final Duration pollingOverdueTime) {
|
||||
final LocalDateTime currentDate = LocalDateTime.now();
|
||||
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery), ZoneId.systemDefault());
|
||||
LocalDateTime nextPollDate = lastPollDate.plus(pollingInterval.getInterval());
|
||||
if (pollingInterval.getDeviationPercent() > 0) {
|
||||
nextPollDate = nextPollDate.plus(
|
||||
pollingInterval.getInterval().toMillis() * pollingInterval.getDeviationPercent() / 100,
|
||||
ChronoUnit.MILLIS);
|
||||
}
|
||||
final LocalDateTime overdueDate = nextPollDate.plus(pollingOverdueTime);
|
||||
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the data type of the tenant configuration. If it is possible to cast to the given data type.
|
||||
*/
|
||||
private static <T> void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey, final Class<T> propertyType) {
|
||||
if (!configurationKey.getDataType().isAssignableFrom(propertyType)) {
|
||||
throw new TenantConfigurationValidatorException(
|
||||
String.format("Cannot parse the database value of type %s into the type %s.",
|
||||
String.format(
|
||||
"Cannot parse the database value of type %s into the type %s.",
|
||||
configurationKey.getDataType(), propertyType));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAccess(final String configurationKeyName) {
|
||||
if (TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY
|
||||
.equalsIgnoreCase(configurationKeyName)) {
|
||||
final SystemSecurityContext systemSecurityContext =
|
||||
SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
||||
if (AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY.equalsIgnoreCase(configurationKeyName)) {
|
||||
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
||||
if (!systemSecurityContext.isCurrentThreadSystemCode() &&
|
||||
!systemSecurityContext.hasPermission(SpPermission.READ_GATEWAY_SEC_TOKEN)) {
|
||||
throw new InsufficientPermissionException(
|
||||
@@ -211,22 +228,28 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration0(Map<String, T> configurations) {
|
||||
List<JpaTenantConfiguration> configurationList = new ArrayList<>();
|
||||
private <T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration0(final Map<String, T> configurations) {
|
||||
final List<JpaTenantConfiguration> configurationList = new ArrayList<>();
|
||||
configurations.forEach((configurationKeyName, value) -> {
|
||||
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
|
||||
if (!configurationKey.getDataType().isAssignableFrom(value.getClass())) {
|
||||
throw new TenantConfigurationValidatorException(String.format(
|
||||
"Cannot parse the value %s of type %s into the type %s defined by the configuration key.", value,
|
||||
value.getClass(), configurationKey.getDataType()));
|
||||
}
|
||||
configurationKey.validate(value, applicationContext);
|
||||
// additional validation for specific configuration keys
|
||||
if (POLLING_TIME.equals(configurationKey.getKeyName())) {
|
||||
final PollingTime pollingTime = new PollingTime(value.toString());
|
||||
if (!ObjectUtils.isEmpty(pollingTime.getOverrides())) {
|
||||
// validate that the QL strings are valid RSQL queries,
|
||||
// nevertheless always when parse them we shall be prepared to catch exceptions if the parsers
|
||||
// has been changed in non backward compatible way
|
||||
pollingTime.getOverrides().forEach(override -> EntityMatcher.forRsql(override.qlStr()));
|
||||
}
|
||||
}
|
||||
|
||||
configurationKey.validate(applicationContext, value);
|
||||
|
||||
JpaTenantConfiguration tenantConfiguration = tenantConfigurationRepository
|
||||
.findByKey(configurationKey.getKeyName());
|
||||
|
||||
JpaTenantConfiguration tenantConfiguration = tenantConfigurationRepository.findByKey(configurationKey.getKeyName());
|
||||
if (tenantConfiguration == null) {
|
||||
tenantConfiguration = new JpaTenantConfiguration(configurationKey.getKeyName(), value.toString());
|
||||
} else {
|
||||
@@ -237,15 +260,12 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
configurationList.add(tenantConfiguration);
|
||||
});
|
||||
|
||||
List<JpaTenantConfiguration> jpaTenantConfigurations = tenantConfigurationRepository
|
||||
.saveAll(configurationList);
|
||||
|
||||
final List<JpaTenantConfiguration> jpaTenantConfigurations = tenantConfigurationRepository.saveAll(configurationList);
|
||||
return jpaTenantConfigurations.stream().collect(Collectors.toMap(
|
||||
JpaTenantConfiguration::getKey,
|
||||
updatedTenantConfiguration -> {
|
||||
|
||||
@SuppressWarnings("unchecked") final Class<T> clazzT = (Class<T>) configurations.get(updatedTenantConfiguration.getKey())
|
||||
.getClass();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<T> clazzT = (Class<T>) configurations.get(updatedTenantConfiguration.getKey()).getClass();
|
||||
return TenantConfigurationValue.<T> builder().global(false)
|
||||
.createdBy(updatedTenantConfiguration.getCreatedBy())
|
||||
.createdAt(updatedTenantConfiguration.getCreatedAt())
|
||||
@@ -257,27 +277,25 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
}
|
||||
|
||||
private <T extends Serializable> TenantConfigurationValue<T> buildTenantConfigurationValueByKey(
|
||||
final TenantConfigurationKey configurationKey, final Class<T> propertyType,
|
||||
final TenantConfiguration tenantConfiguration) {
|
||||
final TenantConfigurationKey configurationKey, final Class<T> propertyType, final TenantConfiguration tenantConfiguration) {
|
||||
if (tenantConfiguration != null) {
|
||||
return TenantConfigurationValue.<T> builder().global(false).createdBy(tenantConfiguration.getCreatedBy())
|
||||
.createdAt(tenantConfiguration.getCreatedAt())
|
||||
.lastModifiedAt(tenantConfiguration.getLastModifiedAt())
|
||||
.lastModifiedBy(tenantConfiguration.getLastModifiedBy())
|
||||
.value(CONVERSION_SERVICE.convert(tenantConfiguration.getValue(), propertyType)).build();
|
||||
|
||||
} else if (configurationKey.getDefaultValue() != null) {
|
||||
|
||||
return TenantConfigurationValue.<T> builder().global(true).createdBy(null).createdAt(null)
|
||||
.lastModifiedAt(null).lastModifiedBy(null)
|
||||
.value(getGlobalConfigurationValue(configurationKey.getKeyName(), propertyType)).build();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the requested configuration value change is allowed. Throws
|
||||
* a {@link TenantConfigurationValueChangeNotAllowedException} otherwise.
|
||||
* Asserts that the requested configuration value change is allowed. Throws a {@link TenantConfigurationValueChangeNotAllowedException}
|
||||
* otherwise.
|
||||
*
|
||||
* @param key The configuration key.
|
||||
* @param valueChange The configuration to be validated.
|
||||
@@ -293,9 +311,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
private void assertAutoCloseValueChange(final String key, final JpaTenantConfiguration valueChange) {
|
||||
if (REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED.equals(key)
|
||||
&& Boolean.TRUE.equals(getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue())) {
|
||||
log.debug(
|
||||
"The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.",
|
||||
key);
|
||||
log.debug("The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.", key);
|
||||
throw new TenantConfigurationValueChangeNotAllowedException();
|
||||
}
|
||||
}
|
||||
@@ -308,8 +324,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
if (MULTI_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
|
||||
JpaTenantConfiguration batchConfig = tenantConfigurationRepository.findByKey(BATCH_ASSIGNMENTS_ENABLED);
|
||||
if (batchConfig != null && Boolean.parseBoolean(batchConfig.getValue())) {
|
||||
log.debug("The Multi-Assignments '{}' feature cannot be enabled as it contradicts with " +
|
||||
"The Batch-Assignments feature, which is already enabled .", key);
|
||||
log.debug("The Multi-Assignments '{}' feature cannot be enabled as it contradicts with the Batch-Assignments feature, which is already enabled .", key);
|
||||
throw new TenantConfigurationValueChangeNotAllowedException();
|
||||
}
|
||||
}
|
||||
@@ -319,10 +334,9 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
if (BATCH_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
|
||||
JpaTenantConfiguration multiConfig = tenantConfigurationRepository.findByKey(MULTI_ASSIGNMENTS_ENABLED);
|
||||
if (multiConfig != null && Boolean.parseBoolean(multiConfig.getValue())) {
|
||||
log.debug("The Batch-Assignments '{}' feature cannot be enabled as it contradicts with " +
|
||||
"The Multi-Assignments feature, which is already enabled .", key);
|
||||
log.debug("The Batch-Assignments '{}' feature cannot be enabled as it contradicts with the Multi-Assignments feature, which is already enabled .", key);
|
||||
throw new TenantConfigurationValueChangeNotAllowedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user