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:
Avgustin Marinov
2025-07-07 16:33:55 +03:00
committed by GitHub
parent baab05f009
commit 7f97d6f441
31 changed files with 664 additions and 514 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}
}
}
}
}