Notes: 1. (!) Eclipselink shall be migrated to 5.0 (in 4.0.8 there are incompatible classes, e.g EJBQueryImpl doesn't implement some newer methods). In the moment is with beta (5.0.0-B12) - JUST for testing! 2. (!) Ethlo plugin doesn't work with Eclipselink 5.0, it builds with Eclipselink 4.0.8 (could be a problem) 3. Dependencies - new starters, test starters changes, some dependencies refactoring 4. Auto-configs split - package changes, some properties classes changes 5. Spring nullable org.springframework.lang.Nullable/NonNull are depecated and replaced with jspcify -> org.jspecify.annotations.Nullable/NonNull (NullMarked) 6. Lombok config - adding lombok.addNullAnnotations=jspecify - to do not mess annotations 7. Distributed lock table changes - SP_LOCK table db migration 8. Spring Retry replaced with Spring Core Retry - does repace retry in hawkbit 9. Specifications -> added Update/Delete(/Predicate) Specifications and JpaSpecificationExecutor changed 10. HawkbitBaseRepositoryFactoryBean modified to register properly 11. Jackson - 2 -> 3, package migrations, finals are not deserialized by default(enable finals deserialization, consider make non-final), too ‘smart’ tries to set complex objects instead of using non args constructor (-> @JsonIgnore), some other default configs made Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -15,18 +15,27 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.HawkbitBaseRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.query.EscapeCharacter;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory;
|
||||
import org.springframework.data.jpa.repository.query.QueryEnhancerSelector;
|
||||
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
|
||||
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
|
||||
import org.springframework.data.jpa.repository.support.JpaRepositoryFragmentsContributor;
|
||||
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.querydsl.EntityPathResolver;
|
||||
@@ -35,16 +44,17 @@ import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link TransactionalRepositoryFactoryBeanSupport} extension that uses {@link HawkbitBaseRepository} as base repository and
|
||||
* proxied repositories in order to convert exceptions to management exceptions.
|
||||
*/
|
||||
@Slf4j
|
||||
@SuppressWarnings("java:S119") // java:S119 - ID is inherited from TransactionalRepositoryFactoryBeanSupport
|
||||
public class HawkbitBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID> extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> {
|
||||
|
||||
private EntityPathResolver entityPathResolver;
|
||||
private JpaRepositoryFragmentsContributor repositoryFragmentsContributor;
|
||||
private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT;
|
||||
private JpaQueryMethodFactory queryMethodFactory;
|
||||
|
||||
@@ -65,6 +75,11 @@ public class HawkbitBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID
|
||||
this.entityPathResolver = resolver.getIfAvailable(() -> SimpleEntityPathResolver.INSTANCE);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setRepositoryFragmentsContributor(final ObjectProvider<JpaRepositoryFragmentsContributor> repositoryFragmentsContributor) {
|
||||
this.repositoryFragmentsContributor = repositoryFragmentsContributor.getIfAvailable(() -> JpaRepositoryFragmentsContributor.DEFAULT);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setEscapeCharacter(final char escapeCharacter) {
|
||||
this.escapeCharacter = EscapeCharacter.of(escapeCharacter);
|
||||
@@ -77,6 +92,45 @@ public class HawkbitBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setQueryMethodFactory(final ObjectProvider<JpaQueryMethodFactory> resolver) {
|
||||
final JpaQueryMethodFactory factory = resolver.getIfAvailable();
|
||||
if (factory != null) {
|
||||
this.queryMethodFactory = factory;
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable BeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(final BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
super.setBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
private @Nullable Function<@Nullable BeanFactory, QueryEnhancerSelector> queryEnhancerSelectorSource;
|
||||
|
||||
public void setQueryEnhancerSelectorSource(QueryEnhancerSelector queryEnhancerSelectorSource) {
|
||||
this.queryEnhancerSelectorSource = bf -> queryEnhancerSelectorSource;
|
||||
}
|
||||
|
||||
public void setQueryEnhancerSelector(final Class<? extends QueryEnhancerSelector> queryEnhancerSelectorType) {
|
||||
queryEnhancerSelectorSource = bf -> {
|
||||
if (bf != null) {
|
||||
final QueryEnhancerSelector selector = bf.getBeanProvider(queryEnhancerSelectorType).getIfAvailable();
|
||||
if (selector != null) {
|
||||
return selector;
|
||||
}
|
||||
|
||||
if (bf instanceof AutowireCapableBeanFactory acbf) {
|
||||
return acbf.createBean(queryEnhancerSelectorType);
|
||||
}
|
||||
}
|
||||
|
||||
return BeanUtils.instantiateClass(queryEnhancerSelectorType);
|
||||
};
|
||||
}
|
||||
|
||||
@PersistenceContext
|
||||
public void setEntityManager(final EntityManager entityManager) {
|
||||
this.entityManager = entityManager;
|
||||
@@ -85,6 +139,7 @@ public class HawkbitBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Objects.requireNonNull(entityManager, "EntityManager must not be null");
|
||||
setRepositoryBaseClass(HawkbitBaseRepository.class); // overrides set by properties base class
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@@ -107,6 +162,19 @@ public class HawkbitBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID
|
||||
interfaces(jpaRepositoryImplementation.getClass(), new HashSet<>()).toArray(new Class<?>[0]),
|
||||
(proxy, method, args) -> {
|
||||
try {
|
||||
if (args != null) {
|
||||
final Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
if (parameterTypes[i] == Specification.class && args[i] == null) {
|
||||
// replaces null specifications with unrestricted specifications (null not accepted since Spring Boot 4.0
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Replace null Specification argument with unrestricted Specification",
|
||||
new Exception("Method " + method + ", arg[" + i + "]"));
|
||||
}
|
||||
args[i] = Specification.unrestricted();
|
||||
}
|
||||
}
|
||||
}
|
||||
return method.invoke(jpaRepositoryImplementation, args);
|
||||
} catch (final InvocationTargetException e) {
|
||||
final Throwable cause = e.getCause() == null ? e : e.getCause();
|
||||
@@ -120,10 +188,13 @@ public class HawkbitBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID
|
||||
|
||||
jpaRepositoryFactory.setEntityPathResolver(entityPathResolver);
|
||||
jpaRepositoryFactory.setEscapeCharacter(escapeCharacter);
|
||||
|
||||
jpaRepositoryFactory.setFragmentsContributor(repositoryFragmentsContributor);
|
||||
if (queryMethodFactory != null) {
|
||||
jpaRepositoryFactory.setQueryMethodFactory(queryMethodFactory);
|
||||
}
|
||||
if (queryEnhancerSelectorSource != null) {
|
||||
jpaRepositoryFactory.setQueryEnhancerSelector(queryEnhancerSelectorSource.apply(beanFactory));
|
||||
}
|
||||
jpaRepositoryFactory.setRepositoryBaseClass(HawkbitBaseRepository.class);
|
||||
return jpaRepositoryFactory;
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.persistence.autoconfigure.EntityScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -111,8 +111,8 @@ import org.springframework.integration.jdbc.lock.JdbcLockRegistry;
|
||||
import org.springframework.integration.jdbc.lock.LockRepository;
|
||||
import org.springframework.integration.support.locks.DefaultLockRegistry;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.retry.annotation.EnableRetry;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.springframework.resilience.annotation.EnableResilientMethods;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||
import org.springframework.security.authorization.AuthorizationResult;
|
||||
@@ -130,7 +130,7 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
|
||||
@EnableAspectJAutoProxy
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@EnableRetry
|
||||
@EnableResilientMethods
|
||||
@EntityScan("org.eclipse.hawkbit.repository.jpa.model")
|
||||
@ComponentScan({ "org.eclipse.hawkbit.repository.jpa.management", "org.eclipse.hawkbit.repository.jpa.scheduler" })
|
||||
@PropertySource("classpath:/hawkbit-jpa-defaults.properties")
|
||||
|
||||
@@ -11,9 +11,14 @@ package org.eclipse.hawkbit.repository.jpa.acm;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.criteria.Root;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.springframework.data.jpa.domain.DeleteSpecification;
|
||||
import org.springframework.data.jpa.domain.PredicateSpecification;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.data.jpa.domain.UpdateSpecification;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Interface of an extended access control by providing means or fine-grained access control.
|
||||
@@ -49,6 +54,20 @@ public interface AccessController<T> {
|
||||
.orElse(specification);
|
||||
}
|
||||
|
||||
default UpdateSpecification<T> appendAccessRules(final Operation operation, @Nullable final UpdateSpecification<T> specification) {
|
||||
return getAccessRules(operation)
|
||||
.map(this::predicateSpec)
|
||||
.map(accessRules -> specification == null ? UpdateSpecification.where(accessRules) : specification.and(accessRules))
|
||||
.orElse(specification);
|
||||
}
|
||||
|
||||
default DeleteSpecification<T> appendAccessRules(final Operation operation, @Nullable final DeleteSpecification<T> specification) {
|
||||
return getAccessRules(operation)
|
||||
.map(this::predicateSpec)
|
||||
.map(accessRules -> specification == null ? DeleteSpecification.where(accessRules) : specification.and(accessRules))
|
||||
.orElse(specification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the given {@link Operation} is permitted for the provided entity.
|
||||
*
|
||||
@@ -68,6 +87,11 @@ public interface AccessController<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
default PredicateSpecification<T> predicateSpec(final Specification<T> spec) {
|
||||
return (from, cb) -> spec.toPredicate((Root<T>) from, cb.createQuery(), cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum to define the perform operation to verify
|
||||
*/
|
||||
|
||||
@@ -38,6 +38,8 @@ import org.eclipse.hawkbit.repository.qfields.SoftwareModuleFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleTypeFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetTypeFields;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -145,13 +147,16 @@ public class AccessControllerConfiguration {
|
||||
final DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler() {
|
||||
|
||||
@Override
|
||||
public EvaluationContext createEvaluationContext(final Supplier<Authentication> authentication, final MethodInvocation mi) {
|
||||
@NullMarked
|
||||
public EvaluationContext createEvaluationContext(
|
||||
final Supplier<? extends @Nullable Authentication> authentication, final MethodInvocation mi) {
|
||||
return super.createEvaluationContext(SingletonSupplier.of(() -> new RawAuthoritiesAuthentication(authentication.get())), mi);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NullMarked
|
||||
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
|
||||
final Authentication authentication, final MethodInvocation mi) {
|
||||
@Nullable final Authentication authentication, final MethodInvocation mi) {
|
||||
return super.createSecurityExpressionRoot(new RawAuthoritiesAuthentication(authentication), mi);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,8 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.configuration;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
|
||||
/**
|
||||
* A constant class which holds only static constants used within the SP server.
|
||||
@@ -28,12 +27,13 @@ public final class Constants {
|
||||
* number.
|
||||
*/
|
||||
public static final int MAX_ENTRIES_IN_STATEMENT = 999;
|
||||
|
||||
/**
|
||||
* @see Retryable#maxAttempts()
|
||||
* See {@link Retryable#maxRetries()}
|
||||
*/
|
||||
public static final int TX_RT_MAX = 10;
|
||||
public static final String RETRY_MAX = "${org.eclipse.hawkbit.repository.jpa.retry-max:10}";
|
||||
/**
|
||||
* @see Backoff#delay()
|
||||
* See {@link Retryable#delayString()}
|
||||
*/
|
||||
public static final long TX_RT_DELAY = 100;
|
||||
public static final String RETRY_DELAY = "${org.eclipse.hawkbit.repository.jpa.retry-delay:100}";
|
||||
}
|
||||
@@ -9,9 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -47,6 +44,7 @@ import org.eclipse.hawkbit.repository.jpa.Jpa;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
|
||||
@@ -56,8 +54,7 @@ import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.security.authorization.method.HandleAuthorizationDenied;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -131,14 +128,14 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public T create(final C create) {
|
||||
return jpaRepository.save(AccessController.Operation.CREATE, jpaEntity(create));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public List<T> create(final Collection<C> create) {
|
||||
return jpaRepository.saveAll(AccessController.Operation.CREATE, create.stream().map(this::jpaEntity).toList());
|
||||
}
|
||||
@@ -189,7 +186,7 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return jpaRepository.count(isNotDeleted().orElse(null));
|
||||
return jpaRepository.count(isNotDeleted().orElseGet(Specification::unrestricted));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -210,7 +207,7 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
@SuppressWarnings("java:S1066") // javaS1066 - better readable that way
|
||||
public T update(final U update) {
|
||||
final T entity = getValid(update.getId());
|
||||
@@ -225,7 +222,7 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
@SuppressWarnings("java:S1066") // javaS1066 - better readable that way
|
||||
public Map<Long, T> update(final Collection<U> update) {
|
||||
final Map<Long, T> toUpdate = findAllById(update.stream().map(Identifiable::getId).toList(), true)
|
||||
@@ -261,14 +258,14 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void delete(final long id) {
|
||||
delete0(List.of(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void delete(final Collection<Long> ids) {
|
||||
delete0(ids);
|
||||
}
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@@ -28,13 +25,13 @@ import org.eclipse.hawkbit.ql.QueryField;
|
||||
import org.eclipse.hawkbit.repository.Identifiable;
|
||||
import org.eclipse.hawkbit.repository.MetadataSupport;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.WithMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
|
||||
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@SuppressWarnings("java:S119") // java:S119 - better self explainable
|
||||
@@ -76,7 +73,7 @@ abstract class AbstractJpaRepositoryWithMetadataManagement<T extends AbstractJpa
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void createMetadata(final Long id, final String key, final MV value) {
|
||||
final T jpaEntity = getValid(id);
|
||||
final Map<String, MVI> metadataValueMap = jpaEntity.getMetadata();
|
||||
@@ -91,7 +88,7 @@ abstract class AbstractJpaRepositoryWithMetadataManagement<T extends AbstractJpa
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void createMetadata(final Long id, final Map<String, ? extends MV> metadata) {
|
||||
final T jpaEntity = getValid(id);
|
||||
final Map<String, MVI> metadataValueMap = jpaEntity.getMetadata();
|
||||
@@ -126,7 +123,7 @@ abstract class AbstractJpaRepositoryWithMetadataManagement<T extends AbstractJpa
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void deleteMetadata(final Long id, final String key) {
|
||||
final T jpaEntity = getValid(id);
|
||||
final Map<String, MVI> metadataValueMap = jpaEntity.getMetadata();
|
||||
|
||||
@@ -52,8 +52,7 @@ import org.eclipse.hawkbit.repository.model.ArtifactUpload;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -93,8 +92,7 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Artifact create(final ArtifactUpload artifactUpload) {
|
||||
if (artifactStorage == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
@@ -164,8 +162,7 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void delete(final long id) {
|
||||
if (artifactStorage == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@@ -42,8 +42,7 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -103,8 +102,7 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Action confirmAction(final long actionId, final Integer code, final Collection<String> deviceMessages) {
|
||||
log.trace("Action with id {} confirm request is triggered.", actionId);
|
||||
final Action action = actionRepository.getById(actionId);
|
||||
@@ -119,8 +117,7 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Action denyAction(final long actionId, final Integer code, final Collection<String> deviceMessages) {
|
||||
log.trace("Action with id {} deny request is triggered.", actionId);
|
||||
final Action action = actionRepository.getById(actionId);
|
||||
|
||||
@@ -118,8 +118,7 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
@@ -229,8 +228,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Action addCancelActionStatus(final ActionStatusCreate create) {
|
||||
final JpaAction action = actionRepository.getById(create.getActionId());
|
||||
if (!action.isCancelingOrCanceled()) {
|
||||
@@ -275,8 +273,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public ActionStatus addInformationalActionStatus(final ActionStatusCreate create) {
|
||||
final JpaAction action = actionRepository.getById(create.getActionId());
|
||||
assertActionStatusQuota(create, action);
|
||||
@@ -290,8 +287,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Action addUpdateActionStatus(final ActionStatusCreate statusCreate) {
|
||||
return addActionStatus(statusCreate);
|
||||
}
|
||||
@@ -334,14 +330,14 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = ConcurrencyFailureException.class, noRetryFor = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, excludes = EntityAlreadyExistsException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address) {
|
||||
return findOrRegisterTargetIfItDoesNotExist0(controllerId, address, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = ConcurrencyFailureException.class, noRetryFor = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, excludes = EntityAlreadyExistsException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address, final String name, final String type) {
|
||||
return findOrRegisterTargetIfItDoesNotExist0(controllerId, address, name, type);
|
||||
}
|
||||
@@ -425,16 +421,14 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void registerRetrieved(final long actionId, final String message) {
|
||||
handleRegisterRetrieved(actionId, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Target updateControllerAttributes(final String controllerId, final Map<String, String> data, final UpdateMode mode) {
|
||||
// Constraints on attribute keys & values are not validated by EclipseLink. Hence, they are validated here.
|
||||
if (data.entrySet().stream().anyMatch(e -> !isAttributeEntryValid(e))) {
|
||||
@@ -518,7 +512,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
jpaAction.setStatus(Status.CANCELING);
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(
|
||||
new JpaActionStatus(jpaAction, Status.CANCELING, java.lang.System.currentTimeMillis(), "manual cancelation requested"));
|
||||
new JpaActionStatus(jpaAction, Status.CANCELING, System.currentTimeMillis(), "manual cancelation requested"));
|
||||
final Action saveAction = actionRepository.save(jpaAction);
|
||||
|
||||
cancelAssignDistributionSetEvent(jpaAction);
|
||||
@@ -651,7 +645,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
jpaTarget.setName((StringUtils.hasText(name) ? name : controllerId));
|
||||
jpaTarget.setSecurityToken(SecurityTokenGenerator.generateToken());
|
||||
jpaTarget.setUpdateStatus(TargetUpdateStatus.REGISTERED);
|
||||
jpaTarget.setLastTargetQuery(java.lang.System.currentTimeMillis());
|
||||
jpaTarget.setLastTargetQuery(System.currentTimeMillis());
|
||||
jpaTarget.setAddress(Optional.ofNullable(address).map(URI::toString).orElse(null));
|
||||
|
||||
if (StringUtils.hasText(type)) {
|
||||
@@ -712,7 +706,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
Constants.MAX_ENTRIES_IN_STATEMENT);
|
||||
|
||||
pollChunks.forEach(chunk -> {
|
||||
final long lastTargetQuery = java.lang.System.currentTimeMillis();
|
||||
final long lastTargetQuery = System.currentTimeMillis();
|
||||
setLastTargetQuery(tenant, lastTargetQuery, chunk);
|
||||
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new TargetPollEvent(chunk, lastTargetQuery, tenant)));
|
||||
@@ -774,7 +768,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
if (isStatusUnknown(toUpdate.getUpdateStatus())) {
|
||||
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
|
||||
}
|
||||
toUpdate.setLastTargetQuery(java.lang.System.currentTimeMillis());
|
||||
toUpdate.setLastTargetQuery(System.currentTimeMillis());
|
||||
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetPollEvent(toUpdate)));
|
||||
return targetRepository.save(toUpdate);
|
||||
}
|
||||
@@ -901,7 +895,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
// case controller retrieves a action multiple times.
|
||||
if (resultList.isEmpty() || (Status.RETRIEVED != resultList.get(0)[1])) {
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.RETRIEVED, java.lang.System.currentTimeMillis(), message));
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.RETRIEVED, System.currentTimeMillis(), message));
|
||||
|
||||
// don't change the action status itself in case the action is in
|
||||
// canceling state otherwise
|
||||
|
||||
@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.repository.jpa.management;
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -39,6 +40,7 @@ import jakarta.persistence.criteria.Root;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.exception.GenericSpServerException;
|
||||
import org.eclipse.hawkbit.ql.jpa.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
@@ -87,21 +89,23 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.eclipse.hawkbit.repository.qfields.ActionFields;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
|
||||
import org.springframework.core.retry.RetryException;
|
||||
import org.springframework.core.retry.RetryPolicy;
|
||||
import org.springframework.core.retry.RetryTemplate;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jpa.domain.DeleteSpecification;
|
||||
import org.springframework.data.jpa.domain.PredicateSpecification;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.retry.backoff.FixedBackOffPolicy;
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
@@ -144,7 +148,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final QuotaManagement quotaManagement, final RepositoryProperties repositoryProperties,
|
||||
final JpaDistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
|
||||
final EntityManager entityManager, final PlatformTransactionManager txManager, final JpaProperties jpaProperties) {
|
||||
final EntityManager entityManager, final PlatformTransactionManager txManager, final JpaProperties jpaProperties,
|
||||
@Value(Constants.RETRY_MAX) final long maxRetries, @Value(Constants.RETRY_DELAY) final long delay) {
|
||||
super(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties);
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.targetRepository = targetRepository;
|
||||
@@ -152,7 +157,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
this.txManager = txManager;
|
||||
this.database = jpaProperties.getDatabase();
|
||||
|
||||
retryTemplate = createRetryTemplate();
|
||||
retryTemplate = new RetryTemplate(RetryPolicy.builder()
|
||||
.includes(ConcurrencyFailureException.class)
|
||||
.maxRetries(maxRetries)
|
||||
.maxDelay(Duration.ofMillis(delay))
|
||||
.build());
|
||||
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentsExceededHandler = maxAssignmentsExceededInfo ->
|
||||
handleMaxAssignmentsExceeded(
|
||||
maxAssignmentsExceededInfo.targetId,
|
||||
@@ -187,8 +196,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Action cancelAction(final long actionId) {
|
||||
return cancelAction0(actionId);
|
||||
}
|
||||
@@ -209,7 +217,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
action.setStatus(Status.CANCELING);
|
||||
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, java.lang.System.currentTimeMillis(),
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "manual cancelation requested"));
|
||||
final Action saveAction = actionRepository.save(action);
|
||||
|
||||
@@ -329,8 +337,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Action forceQuitAction(final long actionId) {
|
||||
return forceQuitAction0(actionId);
|
||||
}
|
||||
@@ -351,7 +358,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
log.warn("action ({}) was still active and has been force quite.", action);
|
||||
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELED, java.lang.System.currentTimeMillis(),
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELED, System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "A force quit has been performed."));
|
||||
|
||||
DeploymentHelper.successCancellation(action, actionRepository, targetRepository);
|
||||
@@ -361,8 +368,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Action forceTargetAction(final long actionId) {
|
||||
final JpaAction action = actionRepository.findById(actionId)
|
||||
.map(this::assertTargetUpdateAllowed)
|
||||
@@ -386,7 +392,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
@Transactional
|
||||
public void deleteActionsByRsql(final String rsql) {
|
||||
log.info("Deleting actions matching rsql {}", rsql);
|
||||
actionRepository.delete(QLSupport.getInstance().buildSpec(rsql, ActionFields.class));
|
||||
actionRepository.delete(DeleteSpecification.where(predicateSpec(QLSupport.getInstance().buildSpec(rsql, ActionFields.class))));
|
||||
}
|
||||
@Deprecated
|
||||
static <T> PredicateSpecification<T> predicateSpec(final Specification<T> spec) {
|
||||
return (from, cb) -> spec.toPredicate((Root<T>) from, cb.createQuery(), cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -419,8 +429,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void cancelInactiveScheduledActionsForTargets(final List<Long> targetIds) {
|
||||
targetRepository.getAccessController().ifPresent(v -> {
|
||||
if (targetRepository.count(AccessController.Operation.UPDATE, TargetSpecifications.hasIdIn(targetIds)) != targetIds.size()) {
|
||||
@@ -621,20 +630,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
return QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.getOrDefault(database, QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED_DEFAULT);
|
||||
}
|
||||
|
||||
private static RetryTemplate createRetryTemplate() {
|
||||
final RetryTemplate template = new RetryTemplate();
|
||||
|
||||
final FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
|
||||
backOffPolicy.setBackOffPeriod(Constants.TX_RT_DELAY);
|
||||
template.setBackOffPolicy(backOffPolicy);
|
||||
|
||||
final SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(
|
||||
Constants.TX_RT_MAX, Collections.singletonMap(ConcurrencyFailureException.class, true));
|
||||
template.setRetryPolicy(retryPolicy);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
private List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
final List<DeploymentRequest> deploymentRequests, final String actionMessage, final AbstractDsAssignmentStrategy strategy) {
|
||||
final List<DeploymentRequest> validatedRequests = validateAndFilterRequestForAssignments(deploymentRequests);
|
||||
@@ -734,8 +729,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(
|
||||
final Long dsId, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy) {
|
||||
return retryTemplate.execute(retryContext ->
|
||||
assignDistributionSetToTargets(dsId, targetsWithActionType, actionMessage, assignmentStrategy));
|
||||
try {
|
||||
return retryTemplate.execute(() -> assignDistributionSetToTargets(dsId, targetsWithActionType, actionMessage, assignmentStrategy));
|
||||
} catch (final RetryException e) {
|
||||
throw new GenericSpServerException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -41,6 +39,7 @@ import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
@@ -62,8 +61,7 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -195,7 +193,7 @@ public class JpaDistributionSetManagement
|
||||
// implicitly lock a distribution set if not already locked and implicit lock is enabled and not to skip
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public boolean shouldLockImplicitly(final DistributionSet distributionSet) {
|
||||
final JpaDistributionSet jpaDistributionSet = toJpaDistributionSet(distributionSet);
|
||||
if (jpaDistributionSet.isLocked()) {
|
||||
@@ -227,7 +225,7 @@ public class JpaDistributionSetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public JpaDistributionSet lock(final DistributionSet distributionSet) {
|
||||
final JpaDistributionSet jpaDistributionSet = toJpaDistributionSet(distributionSet);
|
||||
if (distributionSet.isLocked()) {
|
||||
@@ -244,7 +242,7 @@ public class JpaDistributionSetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public JpaDistributionSet unlock(final DistributionSet distributionSet) {
|
||||
final JpaDistributionSet jpaDistributionSet = toJpaDistributionSet(distributionSet);
|
||||
if (jpaDistributionSet.isLocked()) {
|
||||
@@ -265,7 +263,7 @@ public class JpaDistributionSetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public JpaDistributionSet assignSoftwareModules(final long id, final Collection<Long> softwareModuleId) {
|
||||
final JpaDistributionSet set = getValid0(id);
|
||||
assertSoftwareModuleQuota(id, softwareModuleId.size());
|
||||
@@ -283,7 +281,7 @@ public class JpaDistributionSetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public JpaDistributionSet unassignSoftwareModule(final long id, final long moduleId) {
|
||||
final JpaDistributionSet set = getValid0(id);
|
||||
|
||||
@@ -295,7 +293,7 @@ public class JpaDistributionSetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public List<JpaDistributionSet> assignTag(final Collection<Long> ids, final long dsTagId) {
|
||||
return updateTag(ids, dsTagId, (tag, distributionSet) -> {
|
||||
if (distributionSet.getTags().contains(tag)) {
|
||||
@@ -309,7 +307,7 @@ public class JpaDistributionSetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public List<JpaDistributionSet> unassignTag(final Collection<Long> ids, final long dsTagId) {
|
||||
return updateTag(ids, dsTagId, (tag, distributionSet) -> {
|
||||
if (distributionSet.getTags().contains(tag)) {
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -24,6 +21,7 @@ import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
|
||||
@@ -40,8 +38,7 @@ import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -71,7 +68,7 @@ public class JpaDistributionSetTypeManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void delete(final long id) {
|
||||
final JpaDistributionSetType toDelete = jpaRepository.getById(id);
|
||||
|
||||
@@ -103,21 +100,21 @@ public class JpaDistributionSetTypeManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public JpaDistributionSetType assignOptionalSoftwareModuleTypes(final long id, final Collection<Long> softwareModulesTypeIds) {
|
||||
return assignSoftwareModuleTypes(id, softwareModulesTypeIds, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public JpaDistributionSetType assignMandatorySoftwareModuleTypes(final long id, final Collection<Long> softwareModuleTypeIds) {
|
||||
return assignSoftwareModuleTypes(id, softwareModuleTypeIds, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public JpaDistributionSetType unassignSoftwareModuleType(final long id, final long softwareModuleTypeId) {
|
||||
final JpaDistributionSetType type = jpaRepository.getById(id);
|
||||
checkDistributionSetTypeNotAssigned(id);
|
||||
|
||||
@@ -96,8 +96,7 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -197,8 +196,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Rollout create(
|
||||
final Create rollout, final int amountGroup, final boolean confirmationRequired,
|
||||
final RolloutGroupConditions conditions, final DynamicRolloutGroupTemplate dynamicRolloutGroupTemplate) {
|
||||
@@ -240,8 +238,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Rollout create(
|
||||
@NotNull @Valid Create create, int amountGroup, boolean confirmationRequired,
|
||||
@NotNull RolloutGroupConditions conditions) {
|
||||
@@ -250,8 +247,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Rollout create(final Create rollout, final List<GroupCreate> groups, final RolloutGroupConditions conditions) {
|
||||
if (groups.isEmpty()) {
|
||||
throw new ValidationException("The amount of groups cannot be 0");
|
||||
@@ -335,8 +331,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void pauseRollout(final long rolloutId) {
|
||||
final JpaRollout rollout = rolloutRepository.getById(rolloutId);
|
||||
if (RolloutStatus.RUNNING != rollout.getStatus()) {
|
||||
@@ -352,8 +347,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void resumeRollout(final long rolloutId) {
|
||||
final JpaRollout rollout = rolloutRepository.getById(rolloutId);
|
||||
if (RolloutStatus.PAUSED != rollout.getStatus()) {
|
||||
@@ -391,16 +385,14 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Rollout approveOrDeny(final long rolloutId, final Rollout.ApprovalDecision decision) {
|
||||
return approveOrDeny0(rolloutId, decision, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Rollout approveOrDeny(final long rolloutId, final Rollout.ApprovalDecision decision, final String remark) {
|
||||
return approveOrDeny0(rolloutId, decision, remark);
|
||||
}
|
||||
@@ -431,8 +423,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Rollout start(final long rolloutId) {
|
||||
log.debug("startRollout called for rollout {}", rolloutId);
|
||||
|
||||
@@ -445,8 +436,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Rollout update(final Update update) {
|
||||
final JpaRollout rollout = rolloutRepository.getById(update.getId());
|
||||
checkIfDeleted(update.getId(), rollout.getStatus());
|
||||
@@ -457,8 +447,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Rollout stop(long rolloutId) {
|
||||
final JpaRollout jpaRollout = rolloutRepository.getById(rolloutId);
|
||||
|
||||
@@ -474,8 +463,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void delete(final long rolloutId) {
|
||||
this.delete0(rolloutRepository.getById(rolloutId));
|
||||
}
|
||||
@@ -503,8 +491,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void triggerNextGroup(final long rolloutId) {
|
||||
final JpaRollout rollout = rolloutRepository.getById(rolloutId);
|
||||
if (RolloutStatus.RUNNING != rollout.getStatus()) {
|
||||
@@ -519,15 +506,11 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
throw new RolloutIllegalStateException("Rollout does not have any groups left to be triggered");
|
||||
}
|
||||
|
||||
final List<JpaRolloutGroup> startedRolloutGroups = rollout.getRolloutGroups().stream()
|
||||
final RolloutGroup lastStartedRolloutGroup = rollout.getRolloutGroups().stream()
|
||||
.filter(group -> group.getStatus() != RolloutGroupStatus.SCHEDULED)
|
||||
.sorted(ROLLOUT_GROUP_DESC_COMP)
|
||||
.map(JpaRolloutGroup.class::cast)
|
||||
.toList();
|
||||
if (startedRolloutGroups.isEmpty()) {
|
||||
throw new RolloutIllegalStateException("Cannot find any started rollout group to trigger next from");
|
||||
}
|
||||
startNextRolloutGroupAction.exec(rollout, startedRolloutGroups.get(0));
|
||||
.min(ROLLOUT_GROUP_DESC_COMP)
|
||||
.orElseThrow(() -> new RolloutIllegalStateException("Cannot find any started rollout group to trigger next from"));
|
||||
startNextRolloutGroupAction.exec(rollout, lastStartedRolloutGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
|
||||
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -32,6 +30,7 @@ import org.eclipse.hawkbit.repository.exception.IncompleteSoftwareModuleExceptio
|
||||
import org.eclipse.hawkbit.repository.exception.LockedException;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
|
||||
@@ -46,8 +45,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProp
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -137,7 +135,7 @@ public class JpaSoftwareModuleManagement extends
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public JpaSoftwareModule lock(final SoftwareModule softwareModule) {
|
||||
final JpaSoftwareModule jpaSoftwareModule = toJpaSoftwareModule(softwareModule);
|
||||
if (jpaSoftwareModule.isLocked()) {
|
||||
@@ -153,7 +151,7 @@ public class JpaSoftwareModuleManagement extends
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public JpaSoftwareModule unlock(final SoftwareModule softwareModule) {
|
||||
final JpaSoftwareModule jpaSoftwareModule = toJpaSoftwareModule(softwareModule);
|
||||
if (softwareModule.isLocked()) {
|
||||
|
||||
@@ -49,15 +49,14 @@ import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
@@ -179,8 +178,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public TenantMetaData updateTenantMetadata(final long defaultDsType) {
|
||||
final JpaTenantMetaData data = (JpaTenantMetaData) getTenantMetadataWithoutDetails();
|
||||
data.setDefaultDsType(distributionSetTypeRepository.getById(defaultDsType));
|
||||
@@ -188,8 +186,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
}
|
||||
|
||||
@Override
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void deleteTenant(final String t) {
|
||||
if (artifactStorage == null) {
|
||||
throw new IllegalStateException("Artifact repository is not available. Can't delete tenant.");
|
||||
|
||||
@@ -61,8 +61,7 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -241,16 +240,14 @@ public class JpaTargetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void deleteByControllerId(final String controllerId) {
|
||||
jpaRepository.delete(jpaRepository.getByControllerId(controllerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public List<Target> assignTag(
|
||||
final Collection<String> controllerIds, final long targetTagId, final Consumer<Collection<String>> notFoundHandler) {
|
||||
return assignTag0(controllerIds, targetTagId, notFoundHandler);
|
||||
@@ -258,8 +255,7 @@ public class JpaTargetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public List<Target> assignTag(final Collection<String> controllerIds, final long targetTagId) {
|
||||
return assignTag0(controllerIds, targetTagId, null);
|
||||
}
|
||||
@@ -284,8 +280,7 @@ public class JpaTargetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public List<Target> unassignTag(
|
||||
final Collection<String> controllerIds, final long targetTagId, final Consumer<Collection<String>> notFoundHandler) {
|
||||
return unassignTag0(controllerIds, targetTagId, notFoundHandler);
|
||||
@@ -293,8 +288,7 @@ public class JpaTargetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public List<Target> unassignTag(final Collection<String> controllerIds, final long targetTagId) {
|
||||
return unassignTag0(controllerIds, targetTagId, null);
|
||||
}
|
||||
@@ -313,8 +307,7 @@ public class JpaTargetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Target assignType(final String controllerId, final Long targetTypeId) {
|
||||
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
|
||||
|
||||
@@ -328,8 +321,7 @@ public class JpaTargetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public Target unassignType(final String controllerId) {
|
||||
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
|
||||
target.setTargetType(null);
|
||||
@@ -338,8 +330,7 @@ public class JpaTargetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void assignTargetGroupWithRsql(String group, String rsql) {
|
||||
final Specification<JpaTarget> rsqlSpecification = QLSupport.getInstance().buildSpec(rsql, TargetFields.class);
|
||||
|
||||
@@ -356,8 +347,7 @@ public class JpaTargetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void assignTargetsWithGroup(String group, List<String> controllerIds) {
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
CriteriaUpdate<JpaTarget> criteriaQuery = cb.createCriteriaUpdate(JpaTarget.class);
|
||||
@@ -387,8 +377,7 @@ public class JpaTargetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void createMetadata(final String controllerId, final String key, final String value) {
|
||||
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
|
||||
|
||||
@@ -404,8 +393,7 @@ public class JpaTargetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void createMetadata(final String controllerId, final Map<String, String> md) {
|
||||
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
|
||||
|
||||
@@ -434,8 +422,7 @@ public class JpaTargetManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void deleteMetadata(final String controllerId, final String key) {
|
||||
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
|
||||
|
||||
|
||||
@@ -36,8 +36,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProp
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -94,10 +93,8 @@ public class JpaTargetTypeManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public TargetType assignCompatibleDistributionSetTypes(final long id,
|
||||
final Collection<Long> distributionSetTypeIds) {
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public TargetType assignCompatibleDistributionSetTypes(final long id, final Collection<Long> distributionSetTypeIds) {
|
||||
final Collection<JpaDistributionSetType> dsTypes = distributionSetTypeRepository.findAllById(distributionSetTypeIds);
|
||||
|
||||
if (dsTypes.size() < distributionSetTypeIds.size()) {
|
||||
@@ -115,8 +112,7 @@ public class JpaTargetTypeManagement
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public TargetType unassignDistributionSetType(final long id, final long distributionSetTypeId) {
|
||||
final JpaTargetType type = jpaRepository.getById(id);
|
||||
if (!distributionSetTypeRepository.existsById(distributionSetTypeId)) {
|
||||
|
||||
@@ -12,7 +12,6 @@ package org.eclipse.hawkbit.repository.jpa.management;
|
||||
import static org.eclipse.hawkbit.auth.SpPermission.READ_GATEWAY_SECURITY_TOKEN;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
|
||||
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.time.Duration;
|
||||
import java.time.Instant;
|
||||
@@ -32,7 +31,6 @@ import org.eclipse.hawkbit.ql.jpa.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValueChangeNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
@@ -57,8 +55,7 @@ import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.resilience.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -98,16 +95,14 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void addOrUpdateConfiguration(final String keyName, final Object value) {
|
||||
addOrUpdateConfiguration0(Map.of(keyName, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void addOrUpdateConfiguration(final Map<String, Object> configurations) {
|
||||
addOrUpdateConfiguration0(configurations);
|
||||
}
|
||||
@@ -124,8 +119,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
|
||||
public void deleteConfiguration(final String keyName) {
|
||||
tenantConfigurationRepository.deleteByKey(keyName);
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Repository interface that offers some actions that takes in account a target operation.
|
||||
@@ -30,35 +30,42 @@ public interface ACMRepository<T> {
|
||||
* Saves only if the caller have access for the operation over the entity. This method could be used to
|
||||
* check CREATE access in creating an entity (save without operation would check for UPDATE access).
|
||||
*
|
||||
* @param operation access operationIf operation is <code>null</code> no access is checked! Should be used
|
||||
* only for tenant context.
|
||||
* @param operation access operationIf operation is <code>null</code> no access is checked! Should be used only for tenant context.
|
||||
* @param entity the entity to save
|
||||
* @return the saved entity
|
||||
*/
|
||||
@NonNull
|
||||
<S extends T> S save(@Nullable AccessController.Operation operation, @NonNull final S entity);
|
||||
<S extends T> S save(AccessController.Operation operation, @NonNull S entity);
|
||||
|
||||
/**
|
||||
* Saves only if the caller have access for the operation over all entities. This method could be used to
|
||||
* check CREATE access in creating an entity (save without operation would check for UPDATE access).
|
||||
*
|
||||
* @param operation access operationIf operation is <code>null</code> no access is checked! Should be used
|
||||
* only for tenant context.
|
||||
* @param operation access operationIf operation is <code>null</code> no access is checked! Should be used only for tenant context.
|
||||
* @param entities the entities to save
|
||||
* @return the saved entities
|
||||
*/
|
||||
<S extends T> List<S> saveAll(@Nullable AccessController.Operation operation, final Iterable<S> entities);
|
||||
<S extends T> List<S> saveAll(AccessController.Operation operation, Iterable<S> entities);
|
||||
|
||||
/**
|
||||
* Returns single entry that match specification and the operation is allowed for.
|
||||
*
|
||||
* @param operation access operation. If operation is <code>null</code> no access is checked! Should be used
|
||||
* only for tenant context.
|
||||
* @param operation access operation. If operation is <code>null</code> no access is checked! Should be used only for tenant context.
|
||||
* @param spec specification
|
||||
* @return matching entity
|
||||
*/
|
||||
@NonNull
|
||||
Optional<T> findOne(@Nullable AccessController.Operation operation, @NonNull Specification<T> spec);
|
||||
Optional<T> findOne(AccessController.Operation operation, @NonNull Specification<T> spec);
|
||||
|
||||
/**
|
||||
* Returns all entries that match specification and the operation is allowed for.
|
||||
*
|
||||
* @param operation access operation. If operation is <code>null</code> no access is checked! Should be used only for tenant context.
|
||||
* @param spec specification
|
||||
* @return matching entities
|
||||
*/
|
||||
@NonNull
|
||||
List<T> findAll(AccessController.Operation operation, @Nullable Specification<T> spec);
|
||||
|
||||
/**
|
||||
* Returns all entries that match specification and the operation is allowed for.
|
||||
@@ -68,19 +75,7 @@ public interface ACMRepository<T> {
|
||||
* @param spec specification
|
||||
* @return matching entities
|
||||
*/
|
||||
@NonNull
|
||||
List<T> findAll(@Nullable AccessController.Operation operation, @Nullable Specification<T> spec);
|
||||
|
||||
/**
|
||||
* Returns all entries that match specification and the operation is allowed for.
|
||||
*
|
||||
* @param operation access operation. If operation is <code>null</code> no access is checked! Should be used
|
||||
* only for tenant context.
|
||||
* @param spec specification
|
||||
* @return matching entities
|
||||
*/
|
||||
@NonNull
|
||||
boolean exists(@Nullable AccessController.Operation operation, Specification<T> spec);
|
||||
boolean exists(AccessController.Operation operation, Specification<T> spec);
|
||||
|
||||
/**
|
||||
* Returns count of all entries that match specification and the operation is allowed for.
|
||||
@@ -90,8 +85,7 @@ public interface ACMRepository<T> {
|
||||
* @param spec specification
|
||||
* @return count of matching entities
|
||||
*/
|
||||
@NonNull
|
||||
long count(@Nullable AccessController.Operation operation, @Nullable Specification<T> spec);
|
||||
long count(AccessController.Operation operation, @Nullable Specification<T> spec);
|
||||
|
||||
/**
|
||||
* Returns all entries, without count, that match specification and the operation is allowed for.
|
||||
@@ -103,9 +97,8 @@ public interface ACMRepository<T> {
|
||||
* @return count of matching entities
|
||||
*/
|
||||
@NonNull
|
||||
Slice<T> findAllWithoutCount(
|
||||
@Nullable final AccessController.Operation operation, @Nullable Specification<T> spec, Pageable pageable);
|
||||
Slice<T> findAllWithoutCount(AccessController.Operation operation, @Nullable Specification<T> spec, Pageable pageable);
|
||||
|
||||
@NonNull
|
||||
Class<T> getDomainClass();
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,11 @@ package org.eclipse.hawkbit.repository.jpa.repository;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
@@ -74,14 +78,6 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
@Param("statusToSet") Action.Status statusToSet, @Param("targetsIds") List<Long> targetIds,
|
||||
@Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
|
||||
|
||||
/**
|
||||
* Retrieves an {@link Action} that matches the queried externalRef.
|
||||
*
|
||||
* @param externalRef of the action. See {@link Action#getExternalRef()}
|
||||
* @return the found {@link Action}
|
||||
*/
|
||||
Optional<Action> findByExternalRef(@Param("externalRef") String externalRef);
|
||||
|
||||
/**
|
||||
* Counts all {@link Action}s referring to the given target.
|
||||
* <p/>
|
||||
@@ -145,7 +141,19 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
* @param statuses the list of statuses the action should not have
|
||||
* @return the count of actions referring the rollout and rollout group and are not in given states
|
||||
*/
|
||||
Long countByRolloutAndRolloutGroupAndStatusNotIn(JpaRollout rollout, JpaRolloutGroup rolloutGroup, List<Status> statuses);
|
||||
// TODO (Spring Boot 4 Migration): Eclipse link (5 beta) doesn't handle correctly the IN clause generated by Spring Data JPA
|
||||
// Long countByRolloutAndRolloutGroupAndStatusNotIn(JpaRollout rollout, JpaRolloutGroup rolloutGroup, List<Status> statuses);
|
||||
default Long countByRolloutAndRolloutGroupAndStatusNotIn(
|
||||
final JpaRollout rollout, final JpaRolloutGroup rolloutGroup, final List<Status> statuses) {
|
||||
return count((root, query, cb) -> {
|
||||
final CriteriaBuilder.In<Status> in = cb.in(root.get(JpaAction_.status));
|
||||
statuses.forEach(in::value);
|
||||
return cb.and(
|
||||
cb.equal(root.get(JpaAction_.rollout).get(AbstractJpaBaseEntity_.id), rollout.getId()),
|
||||
cb.equal(root.get(JpaAction_.rolloutGroup).get(AbstractJpaBaseEntity_.id), rolloutGroup.getId()),
|
||||
cb.not(in));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout and rollout group.
|
||||
@@ -160,7 +168,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout, rollout group and status.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutId the ID of rollout the actions belong to
|
||||
@@ -214,7 +222,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if actions for the given rollout exists, otherwise {@code false}
|
||||
* <p/>
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutId the ID of the rollout the actions belong to
|
||||
@@ -222,13 +230,13 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
* @return {@code true} if actions for the given rollout exists, otherwise {@code false}
|
||||
*/
|
||||
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaAction a WHERE a.rollout.id=:rolloutId AND a.status != :status")
|
||||
boolean existsByRolloutIdAndStatusNotIn(@Param("rolloutId") Long rolloutId, @Param("status") Status status);
|
||||
boolean existsByRolloutIdAndStatusNot(@Param("rolloutId") Long rolloutId, @Param("status") Status status);
|
||||
|
||||
/**
|
||||
* Retrieving all actions referring to a given rollout with a specific action as parent reference and a specific status.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* Finding all actions of a specific rollout group parent relation.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param pageable page parameters
|
||||
@@ -242,7 +250,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
|
||||
/**
|
||||
* Retrieving all actions referring to the first group of a rollout.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param pageable page parameters
|
||||
@@ -255,7 +263,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
|
||||
/**
|
||||
* Retrieves all actions for a specific rollout and in a specific status.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param pageable page parameters
|
||||
@@ -267,7 +275,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in each status in specified rollout.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutId id of {@link Rollout}
|
||||
@@ -278,7 +286,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in each status in specified rollout.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutId id of {@link Rollout}
|
||||
@@ -289,7 +297,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in each status in specified rollout group.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutGroupId id of {@link RolloutGroup}
|
||||
@@ -300,7 +308,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in each status in specified rollout group.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutGroupId list of id of {@link RolloutGroup}
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,14 +25,17 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController.Operation;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.DeleteSpecification;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.data.jpa.domain.UpdateSpecification;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@Slf4j
|
||||
public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements BaseEntityRepository<T> {
|
||||
@@ -52,7 +55,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
@Override
|
||||
@NonNull
|
||||
public <S extends T> S save(@NonNull final S entity) {
|
||||
accessController.assertOperationAllowed(AccessController.Operation.UPDATE, entity);
|
||||
accessController.assertOperationAllowed(Operation.UPDATE, entity);
|
||||
return repository.save(entity);
|
||||
}
|
||||
|
||||
@@ -75,15 +78,15 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return count(null);
|
||||
return count((Specification<T>) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(@NonNull final Long id) {
|
||||
if (!exists(AccessController.Operation.READ, byIdSpec(id))) {
|
||||
if (!exists(Operation.READ, byIdSpec(id))) {
|
||||
throw new EntityNotFoundException(repository.getDomainClass(), id);
|
||||
}
|
||||
if (!exists(AccessController.Operation.DELETE, byIdSpec(id))) {
|
||||
if (!exists(Operation.DELETE, byIdSpec(id))) {
|
||||
throw new InsufficientPermissionException();
|
||||
}
|
||||
repository.deleteById(id);
|
||||
@@ -91,7 +94,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
|
||||
@Override
|
||||
public void delete(@NonNull final T entity) {
|
||||
accessController.assertOperationAllowed(AccessController.Operation.DELETE, entity);
|
||||
accessController.assertOperationAllowed(Operation.DELETE, entity);
|
||||
repository.delete(entity);
|
||||
}
|
||||
|
||||
@@ -99,7 +102,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
public void deleteAllById(@NonNull final Iterable<? extends Long> ids) {
|
||||
final Set<Long> idList = new HashSet<>();
|
||||
ids.forEach(idList::add);
|
||||
if (count(AccessController.Operation.DELETE, byIdsSpec(idList)) != idList.size()) {
|
||||
if (count(Operation.DELETE, byIdsSpec(idList)) != idList.size()) {
|
||||
throw new InsufficientPermissionException("Has at least one id that is not allowed for deletion!");
|
||||
}
|
||||
repository.deleteAllById(idList);
|
||||
@@ -107,7 +110,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
|
||||
@Override
|
||||
public void deleteAll(@NonNull final Iterable<? extends T> entities) {
|
||||
accessController.assertOperationAllowed(AccessController.Operation.DELETE, entities);
|
||||
accessController.assertOperationAllowed(Operation.DELETE, entities);
|
||||
repository.deleteAll(entities);
|
||||
}
|
||||
|
||||
@@ -124,7 +127,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
|
||||
@Override
|
||||
public <S extends T> List<S> saveAll(final Iterable<S> entities) {
|
||||
accessController.assertOperationAllowed(AccessController.Operation.UPDATE, entities);
|
||||
accessController.assertOperationAllowed(Operation.UPDATE, entities);
|
||||
return repository.saveAll(entities);
|
||||
}
|
||||
|
||||
@@ -142,7 +145,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
|
||||
@Override
|
||||
public void deleteByTenant(final String tenant) {
|
||||
if (accessController.getAccessRules(AccessController.Operation.DELETE).isPresent()) {
|
||||
if (accessController.getAccessRules(Operation.DELETE).isPresent()) {
|
||||
throw new InsufficientPermissionException("DELETE operation has restriction for given context! deleteAll can't be executed!");
|
||||
}
|
||||
repository.deleteByTenant(tenant);
|
||||
@@ -160,47 +163,52 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
return repository.findOne(
|
||||
// spec shall be non-null and the result of appending rules shall be non-null
|
||||
Objects.requireNonNull(
|
||||
accessController.appendAccessRules(AccessController.Operation.READ, spec),
|
||||
accessController.appendAccessRules(Operation.READ, spec),
|
||||
APPENDED_ACCESS_RULES_SPEC_OF_NON_NULL_SPEC_MUST_NOT_BE_NULL));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public List<T> findAll(final Specification<T> spec) {
|
||||
return repository.findAll(accessController.appendAccessRules(AccessController.Operation.READ, spec));
|
||||
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Page<T> findAll(final Specification<T> spec, @NonNull final Pageable pageable) {
|
||||
return repository.findAll(accessController.appendAccessRules(AccessController.Operation.READ, spec), pageable);
|
||||
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> findAll(final Specification<T> spec, final Specification<T> countSpec, final Pageable pageable) {
|
||||
return repository.findAll(accessController.appendAccessRules(AccessController.Operation.READ, spec), countSpec, pageable);
|
||||
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), countSpec, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public List<T> findAll(final Specification<T> spec, @NonNull final Sort sort) {
|
||||
return repository.findAll(accessController.appendAccessRules(AccessController.Operation.READ, spec), sort);
|
||||
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), sort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(final Specification<T> spec) {
|
||||
return repository.count(accessController.appendAccessRules(AccessController.Operation.READ, spec));
|
||||
return repository.count(accessController.appendAccessRules(Operation.READ, spec));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(@NonNull final Specification<T> spec) {
|
||||
return repository.exists(
|
||||
Objects.requireNonNull(accessController.appendAccessRules(AccessController.Operation.READ, spec)));
|
||||
Objects.requireNonNull(accessController.appendAccessRules(Operation.READ, spec)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long delete(final Specification<T> spec) {
|
||||
return repository.delete(accessController.appendAccessRules(AccessController.Operation.DELETE, spec));
|
||||
public long update(final UpdateSpecification<T> spec) {
|
||||
return repository.update(accessController.appendAccessRules(Operation.UPDATE, spec));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long delete(final DeleteSpecification<T> spec) {
|
||||
return repository.delete(accessController.appendAccessRules(Operation.DELETE, spec));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -209,7 +217,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
return repository.findBy(
|
||||
// spec shall be non-null and the result of appending rules shall be non-null
|
||||
Objects.requireNonNull(
|
||||
accessController.appendAccessRules(AccessController.Operation.READ, spec),
|
||||
accessController.appendAccessRules(Operation.READ, spec),
|
||||
APPENDED_ACCESS_RULES_SPEC_OF_NON_NULL_SPEC_MUST_NOT_BE_NULL),
|
||||
queryFunction);
|
||||
}
|
||||
@@ -234,13 +242,13 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
@Override
|
||||
public Slice<T> findAllWithoutCount(final Specification<T> spec, final Pageable pageable) {
|
||||
return repository.findAllWithoutCount(
|
||||
accessController.appendAccessRules(AccessController.Operation.READ, spec), pageable);
|
||||
accessController.appendAccessRules(Operation.READ, spec), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@NonNull
|
||||
public <S extends T> S save(@Nullable AccessController.Operation operation, @NonNull final S entity) {
|
||||
public <S extends T> S save(Operation operation, @NonNull final S entity) {
|
||||
if (operation != null) {
|
||||
accessController.assertOperationAllowed(operation, entity);
|
||||
}
|
||||
@@ -249,7 +257,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public <S extends T> List<S> saveAll(@Nullable AccessController.Operation operation, final Iterable<S> entities) {
|
||||
public <S extends T> List<S> saveAll(final Operation operation, final Iterable<S> entities) {
|
||||
if (operation != null) {
|
||||
accessController.assertOperationAllowed(operation, entities);
|
||||
}
|
||||
@@ -257,7 +265,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Optional<T> findOne(@Nullable AccessController.Operation operation, @NonNull Specification<T> spec) {
|
||||
public Optional<T> findOne(final Operation operation, @NonNull Specification<T> spec) {
|
||||
Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL);
|
||||
if (operation == null) {
|
||||
return repository.findOne(spec);
|
||||
@@ -272,7 +280,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public List<T> findAll(@Nullable final AccessController.Operation operation, @Nullable final Specification<T> spec) {
|
||||
public List<T> findAll(final Operation operation, @Nullable final Specification<T> spec) {
|
||||
if (operation == null) {
|
||||
return repository.findAll(spec);
|
||||
} else {
|
||||
@@ -282,7 +290,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public boolean exists(@Nullable AccessController.Operation operation, Specification<T> spec) {
|
||||
public boolean exists(final Operation operation, Specification<T> spec) {
|
||||
if (operation == null) {
|
||||
return repository.exists(spec);
|
||||
} else {
|
||||
@@ -292,8 +300,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public long count(@Nullable final AccessController.Operation operation, @Nullable final Specification<T> spec) {
|
||||
public long count(final Operation operation, @Nullable final Specification<T> spec) {
|
||||
if (operation == null) {
|
||||
return repository.count(spec);
|
||||
} else {
|
||||
@@ -303,8 +310,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Slice<T> findAllWithoutCount(
|
||||
@Nullable final AccessController.Operation operation, @Nullable Specification<T> spec, Pageable pageable) {
|
||||
public Slice<T> findAllWithoutCount(final Operation operation, @Nullable Specification<T> spec, Pageable pageable) {
|
||||
if (operation == null) {
|
||||
return repository.findAllWithoutCount(spec, pageable);
|
||||
} else {
|
||||
@@ -321,25 +327,25 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
@Override
|
||||
public Optional<T> findOne(final Specification<T> spec, final String entityGraph) {
|
||||
return repository.findOne(
|
||||
accessController.appendAccessRules(AccessController.Operation.READ, spec), entityGraph);
|
||||
accessController.appendAccessRules(Operation.READ, spec), entityGraph);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> findAll(final Specification<T> spec, final String entityGraph) {
|
||||
return repository.findAll(
|
||||
accessController.appendAccessRules(AccessController.Operation.READ, spec), entityGraph);
|
||||
accessController.appendAccessRules(Operation.READ, spec), entityGraph);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> findAll(final Specification<T> spec, final String entityGraph, final Pageable pageable) {
|
||||
return repository.findAll(
|
||||
accessController.appendAccessRules(AccessController.Operation.READ, spec), entityGraph, pageable);
|
||||
accessController.appendAccessRules(Operation.READ, spec), entityGraph, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> findAll(final Specification<T> spec, final String entityGraph, final Sort sort) {
|
||||
return repository.findAll(
|
||||
accessController.appendAccessRules(AccessController.Operation.READ, spec), entityGraph, sort);
|
||||
accessController.appendAccessRules(Operation.READ, spec), entityGraph, sort);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -370,7 +376,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
if (Iterable.class.isAssignableFrom(method.getReturnType())) {
|
||||
for (final Object e : (Iterable<?>) result) {
|
||||
if (repository.getDomainClass().isAssignableFrom(e.getClass())) {
|
||||
accessController.assertOperationAllowed(AccessController.Operation.READ, (T) e);
|
||||
accessController.assertOperationAllowed(Operation.READ, (T) e);
|
||||
}
|
||||
}
|
||||
} else if (Optional.class.isAssignableFrom(method.getReturnType()) && ((Optional<?>) result)
|
||||
@@ -379,11 +385,11 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
||||
return ((Optional<T>) result).filter(
|
||||
t -> {
|
||||
// if not accessible - throws exception (as for iterables or single entities)
|
||||
accessController.assertOperationAllowed(AccessController.Operation.READ, t);
|
||||
accessController.assertOperationAllowed(Operation.READ, t);
|
||||
return true;
|
||||
});
|
||||
} else if (repository.getDomainClass().isAssignableFrom(method.getReturnType())) {
|
||||
accessController.assertOperationAllowed(AccessController.Operation.READ, (T) result);
|
||||
accessController.assertOperationAllowed(Operation.READ, (T) result);
|
||||
}
|
||||
return result;
|
||||
} else if ("toString".equals(method.getName()) && method.getParameterCount() == 0) {
|
||||
|
||||
@@ -19,7 +19,9 @@ import jakarta.persistence.NoResultException;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController.Operation;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -30,8 +32,6 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
|
||||
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -61,7 +61,7 @@ public final class HawkbitBaseRepository<T, ID extends Serializable> extends Sim
|
||||
|
||||
@Override
|
||||
public Slice<T> findAllWithoutCount(final Pageable pageable) {
|
||||
return findAllWithoutCount(null, pageable);
|
||||
return findAllWithoutCount(Specification.unrestricted(), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,38 +74,36 @@ public final class HawkbitBaseRepository<T, ID extends Serializable> extends Sim
|
||||
@Transactional
|
||||
@NonNull
|
||||
@SuppressWarnings("java:S6809") // this method already has a transactional annotation witch shall be applied
|
||||
public <S extends T> S save(@Nullable AccessController.Operation operation, @NonNull final S entity) {
|
||||
public <S extends T> S save(final Operation operation, @NonNull final S entity) {
|
||||
return save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@SuppressWarnings("java:S6809") // this method already has a transactional annotation witch shall be applied
|
||||
public <S extends T> List<S> saveAll(@Nullable AccessController.Operation operation, final Iterable<S> entities) {
|
||||
public <S extends T> List<S> saveAll(final Operation operation, final Iterable<S> entities) {
|
||||
return saveAll(entities);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Optional<T> findOne(@Nullable AccessController.Operation operation, @NonNull Specification<T> spec) {
|
||||
public Optional<T> findOne(final Operation operation, @NonNull Specification<T> spec) {
|
||||
return findOne(spec);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@SuppressWarnings("java:S4449") // find all accepts null
|
||||
public List<T> findAll(@Nullable final AccessController.Operation operation, @Nullable final Specification<T> spec) {
|
||||
public List<T> findAll(@Nullable final Operation operation, @Nullable final Specification<T> spec) {
|
||||
return findAll(spec);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public boolean exists(@Nullable AccessController.Operation operation, Specification<T> spec) {
|
||||
public boolean exists(@Nullable Operation operation, Specification<T> spec) {
|
||||
return exists(spec);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public long count(@Nullable final AccessController.Operation operation, @Nullable final Specification<T> spec) {
|
||||
public long count(@Nullable final Operation operation, @Nullable final Specification<T> spec) {
|
||||
return count(spec);
|
||||
}
|
||||
|
||||
@@ -137,7 +135,7 @@ public final class HawkbitBaseRepository<T, ID extends Serializable> extends Sim
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Slice<T> findAllWithoutCount(@Nullable final AccessController.Operation operation, @Nullable Specification<T> spec,
|
||||
public Slice<T> findAllWithoutCount(@Nullable final Operation operation, @Nullable Specification<T> spec,
|
||||
Pageable pageable) {
|
||||
return findAllWithoutCount(spec, pageable);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Repository interface that offers JpaSpecificationExecutor#findOne/All methods with entity graph loading
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Repository interface that offers findAll with disabled count query.
|
||||
|
||||
@@ -12,8 +12,12 @@ package org.eclipse.hawkbit.repository.jpa.repository;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup_;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
@@ -72,10 +76,20 @@ public interface RolloutGroupRepository extends BaseEntityRepository<JpaRolloutG
|
||||
* Retrieves all {@link RolloutGroup}s for a specific rollout and status not having.
|
||||
*
|
||||
* @param rollout the rollout the rolloutgroup belong to
|
||||
* @param status the status which the rolloutgroup should not have
|
||||
* @param statuses the status which the rolloutgroup should not have
|
||||
* @return rolloutgroup referring to a rollout and not having a specific status.
|
||||
*/
|
||||
List<JpaRolloutGroup> findByRolloutAndStatusNotIn(JpaRollout rollout, Collection<RolloutGroupStatus> status);
|
||||
// TODO (Spring Boot 4 Migration): Eclipse link (5 beta) doesn't handle correctly the IN clause generated by Spring Data JPA
|
||||
// List<JpaRolloutGroup> findByRolloutAndStatusNotIn(JpaRollout rollout, Collection<RolloutGroupStatus> statuses);
|
||||
default List<JpaRolloutGroup> findByRolloutAndStatusNotIn(final JpaRollout rollout, final Collection<RolloutGroupStatus> statuses) {
|
||||
return findAll((root, query, cb) -> {
|
||||
final CriteriaBuilder.In<RolloutGroupStatus> in = cb.in(root.get(JpaRolloutGroup_.status));
|
||||
statuses.forEach(in::value);
|
||||
return cb.and(
|
||||
cb.equal(root.get(JpaRolloutGroup_.rollout).get(AbstractJpaBaseEntity_.id), rollout.getId()),
|
||||
cb.not(in));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup} for a specific rollout.
|
||||
|
||||
@@ -14,12 +14,18 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
@@ -37,6 +43,9 @@ public interface RolloutRepository extends BaseEntityRepository<JpaRollout> {
|
||||
* @param status the status of the rollouts to find
|
||||
* @return the list of {@link Rollout} for specific status
|
||||
*/
|
||||
// TODO (Spring Boot 4 Migration): seems native queries are fine with collections. However, this is used only
|
||||
// in handleAll and maybe the caller method RolloutManagement#findActiveRollouts could be moved in handler
|
||||
// less visibility
|
||||
@Query("SELECT sm.id FROM JpaRollout sm WHERE sm.status IN ?1 ORDER BY sm.id ASC")
|
||||
List<Long> findByStatusIn(Collection<RolloutStatus> status);
|
||||
|
||||
@@ -65,18 +74,36 @@ public interface RolloutRepository extends BaseEntityRepository<JpaRollout> {
|
||||
* Retrieves all {@link Rollout}s for a specific {@link DistributionSet} in a given {@link RolloutStatus}.
|
||||
*
|
||||
* @param set the distribution set
|
||||
* @param status the status of the rollout
|
||||
* @param statuses the status of the rollout
|
||||
* @return {@link Rollout} for specific distribution set
|
||||
*/
|
||||
List<Rollout> findByDistributionSetAndStatusIn(DistributionSet set, Collection<RolloutStatus> status);
|
||||
// TODO (Spring Boot 4 Migration): Eclipse link (5 beta) doesn't handle correctly the IN clause generated by Spring Data JPA
|
||||
// List<Rollout> findByDistributionSetAndStatusIn(DistributionSet set, Collection<RolloutStatus> statusese);
|
||||
default List<JpaRollout> findByDistributionSetAndStatusIn(final DistributionSet set, final Collection<RolloutStatus> statuses) {
|
||||
return findAll(byDsAndStatuses(set.getId(), statuses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts all {@link Rollout}s for a specific {@link DistributionSet} in a
|
||||
* given {@link RolloutStatus}.
|
||||
*
|
||||
* @param distributionSetId the distribution set
|
||||
* @param status the status of the rollout
|
||||
* @param statuses the status of the rollout
|
||||
* @return the count
|
||||
*/
|
||||
long countByDistributionSetIdAndStatusIn(long distributionSetId, Collection<RolloutStatus> status);
|
||||
// TODO (Spring Boot 4 Migration): Eclipse link (5 beta) doesn't handle correctly the IN clause generated by Spring Data JPA
|
||||
// long countByDistributionSetIdAndStatusIn(long distributionSetId, Collection<RolloutStatus> statuses);
|
||||
default long countByDistributionSetIdAndStatusIn(final long distributionSetId, final Collection<RolloutStatus> statuses) {
|
||||
return count(byDsAndStatuses(distributionSetId, statuses));
|
||||
}
|
||||
|
||||
private static Specification<JpaRollout> byDsAndStatuses(final long distributionSetId, final Collection<RolloutStatus> statuses) {
|
||||
return (root, query, cb) -> {
|
||||
final CriteriaBuilder.In<RolloutStatus> in = cb.in(root.get(JpaRollout_.status));
|
||||
statuses.forEach(in::value);
|
||||
return cb.and(
|
||||
cb.equal(root.get(JpaRollout_.distributionSet).get(AbstractJpaBaseEntity_.id), distributionSetId),
|
||||
in);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -44,5 +44,4 @@ public abstract class AbstractPauseRolloutGroupAction<T extends Enum<T>> impleme
|
||||
asSystem(() -> rolloutManagement.pauseRollout(rollout.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -246,7 +246,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
log.debug("handleDeleteRollout called for {}", rollout.getId());
|
||||
|
||||
// check if there are actions beyond schedule
|
||||
boolean hardDeleteRolloutGroups = !actionRepository.existsByRolloutIdAndStatusNotIn(rollout.getId(),
|
||||
boolean hardDeleteRolloutGroups = !actionRepository.existsByRolloutIdAndStatusNot(rollout.getId(),
|
||||
Status.SCHEDULED);
|
||||
if (hardDeleteRolloutGroups) {
|
||||
log.debug("Rollout {} has no actions other than scheduled -> hard delete", rollout.getId());
|
||||
@@ -321,7 +321,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
}
|
||||
|
||||
private void handleReadyRollout(final Rollout rollout) {
|
||||
if (rollout.getStartAt() != null && rollout.getStartAt() <= java.lang.System.currentTimeMillis()) {
|
||||
if (rollout.getStartAt() != null && rollout.getStartAt() <= System.currentTimeMillis()) {
|
||||
log.debug("handleReadyRollout called for rollout {} with autostart beyond define time. Switch to STARTING", rollout.getId());
|
||||
rolloutManagement.start(rollout.getId());
|
||||
}
|
||||
@@ -627,7 +627,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
// return if group change is made
|
||||
private boolean fillDynamicRolloutGroupsWithTargets(final JpaRollout rollout) {
|
||||
final AtomicLong lastFill = lastDynamicGroupFill.computeIfAbsent(rollout.getId(), id -> new AtomicLong(0));
|
||||
final long now = java.lang.System.currentTimeMillis();
|
||||
final long now = System.currentTimeMillis();
|
||||
if (now - lastFill.get() < repositoryProperties.getDynamicRolloutsMinInvolvePeriodMS()) {
|
||||
// too early to make another dynamic involvement attempt
|
||||
return false;
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.springframework.data.jpa.domain.DeleteSpecification;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
@@ -102,15 +103,14 @@ public final class ActionSpecifications {
|
||||
);
|
||||
}
|
||||
|
||||
public static Specification<JpaAction> byControllerIdAndIdIn(final String controllerId, final List<Long> actionIds) {
|
||||
return ((root, query, cb) -> {
|
||||
final Join<JpaAction, JpaTarget> targetJoin = root.join(JpaAction_.target);
|
||||
return cb.and(
|
||||
cb.equal(targetJoin.get(JpaTarget_.controllerId), controllerId),
|
||||
root.get(AbstractJpaBaseEntity_.id).in(actionIds)
|
||||
);
|
||||
});
|
||||
|
||||
public static DeleteSpecification<JpaAction> byControllerIdAndIdIn(final String controllerId, final List<Long> actionIds) {
|
||||
return (root, cd, cb) -> {
|
||||
final Join<JpaAction, JpaTarget> targetJoin = root.join(JpaAction_.target);
|
||||
return cb.and(
|
||||
cb.equal(targetJoin.get(JpaTarget_.controllerId), controllerId),
|
||||
root.get(AbstractJpaBaseEntity_.id).in(actionIds)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public static Specification<JpaAction> byDistributionSetIdAndActiveAndStatusIsNot(final Long distributionSetId, final Action.Status status) {
|
||||
|
||||
@@ -9,15 +9,17 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.event.remote;
|
||||
|
||||
import static org.springframework.messaging.MessageHeaders.CONTENT_TYPE;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.hawkbit.event.EventJacksonMessageConverter;
|
||||
import org.eclipse.hawkbit.event.EventProtoStuffMessageConverter;
|
||||
import org.eclipse.hawkbit.repository.event.TenantAwareEvent;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.support.MutableMessageHeaders;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
/**
|
||||
* Test the remote entity events.
|
||||
@@ -25,33 +27,27 @@ import org.springframework.messaging.MessageHeaders;
|
||||
@SuppressWarnings("java:S6813") // constructor injects are not possible for test classes
|
||||
public abstract class AbstractRemoteEventTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
private EventProtoStuffMessageConverter eventProtoStuffMessageConverter = new EventProtoStuffMessageConverter();
|
||||
|
||||
private EventJacksonMessageConverter jacksonMessageConverter = new EventJacksonMessageConverter();
|
||||
private final EventProtoStuffMessageConverter eventProtoStuffMessageConverter = new EventProtoStuffMessageConverter();
|
||||
private EventJacksonMessageConverter jacksonMessageConverter;
|
||||
|
||||
@Autowired
|
||||
void setJsonMapper(final JsonMapper jsonMapper) {
|
||||
jacksonMessageConverter = new EventJacksonMessageConverter(jsonMapper);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends TenantAwareEvent> T createJacksonEvent(final T event) {
|
||||
final Message<?> message = createJsonMessage(event);
|
||||
return (T) jacksonMessageConverter.fromMessage(message, event.getClass());
|
||||
return (T) jacksonMessageConverter.fromMessage(
|
||||
jacksonMessageConverter.toMessage
|
||||
(event, new MutableMessageHeaders(Map.of(CONTENT_TYPE, EventJacksonMessageConverter.APPLICATION_REMOTE_EVENT_JSON))),
|
||||
event.getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends TenantAwareEvent> T createProtoStuffEvent(final T event) {
|
||||
final Message<?> message = createProtoStuffMessage(event);
|
||||
return (T) eventProtoStuffMessageConverter.fromMessage(message, event.getClass());
|
||||
return (T) eventProtoStuffMessageConverter.fromMessage(
|
||||
eventProtoStuffMessageConverter.toMessage(
|
||||
event, new MutableMessageHeaders(Map.of(CONTENT_TYPE, EventProtoStuffMessageConverter.APPLICATION_BINARY_PROTOSTUFF))),
|
||||
event.getClass());
|
||||
}
|
||||
|
||||
private Message<?> createProtoStuffMessage(final TenantAwareEvent event) {
|
||||
return eventProtoStuffMessageConverter.toMessage(
|
||||
event, new MutableMessageHeaders(Map.of(MessageHeaders.CONTENT_TYPE,
|
||||
EventProtoStuffMessageConverter.APPLICATION_BINARY_PROTOSTUFF))
|
||||
);
|
||||
}
|
||||
|
||||
private Message<?> createJsonMessage(final Object event) {
|
||||
return jacksonMessageConverter.toMessage(event, new MutableMessageHeaders(Map.of(MessageHeaders.CONTENT_TYPE,
|
||||
EventJacksonMessageConverter.APPLICATION_REMOTE_EVENT_JSON)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -58,7 +58,7 @@ import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
|
||||
@@ -109,7 +109,7 @@ class SystemExecutionTest extends AbstractAccessControllerManagementTest {
|
||||
for (final Operation operation : Operation.values()) {
|
||||
accessController.appendAccessRules(operation, mock);
|
||||
}
|
||||
verify(mock, times(times)).and(any()); // once for every access controller is scoped only
|
||||
verify(mock, times(times)).and(any(Specification.class)); // once for every access controller is scoped only
|
||||
|
||||
final Specification mockAsSystem = mock(Specification.class);
|
||||
for (final Operation operation : Operation.values()) {
|
||||
|
||||
@@ -11,11 +11,12 @@ package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
@@ -25,7 +26,6 @@ import java.util.concurrent.Callable;
|
||||
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.eclipse.hawkbit.artifact.exception.ArtifactBinaryNotFoundException;
|
||||
import org.eclipse.hawkbit.artifact.exception.FileSizeQuotaExceededException;
|
||||
import org.eclipse.hawkbit.artifact.exception.StorageQuotaExceededException;
|
||||
@@ -81,12 +81,12 @@ class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final int artifactSize = artifactData.length();
|
||||
verifyThrownExceptionBy(
|
||||
() -> artifactManagement.create(new ArtifactUpload(
|
||||
IOUtils.toInputStream(artifactData, "UTF-8"),
|
||||
new ByteArrayInputStream(artifactData.getBytes(StandardCharsets.UTF_8)),
|
||||
null, artifactSize, null, NOT_EXIST_IDL, "xxx", false)), "SoftwareModule");
|
||||
|
||||
verifyThrownExceptionBy(
|
||||
() -> artifactManagement.create(new ArtifactUpload(
|
||||
IOUtils.toInputStream(artifactData, "UTF-8"),
|
||||
new ByteArrayInputStream(artifactData.getBytes(StandardCharsets.UTF_8)),
|
||||
null, artifactSize, null, NOT_EXIST_IDL, "xxx", false)), "SoftwareModule");
|
||||
|
||||
verifyThrownExceptionBy(() -> artifactManagement.delete(NOT_EXIST_IDL), "Artifact");
|
||||
@@ -143,7 +143,8 @@ class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
final long smID = softwareModuleRepository.save(new JpaSoftwareModule(osType, "smIllegalFilenameTest", "1.0")).getId();
|
||||
final ArtifactUpload artifactUpload = new ArtifactUpload(
|
||||
IOUtils.toInputStream(artifactData, "UTF-8"), null, artifactSize, null, smID, illegalFilename, false);
|
||||
new ByteArrayInputStream(artifactData.getBytes(StandardCharsets.UTF_8)), null, artifactSize, null, smID, illegalFilename,
|
||||
false);
|
||||
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> artifactManagement.create(artifactUpload));
|
||||
assertThat(softwareModuleManagement.get(smID).getArtifacts()).isEmpty();
|
||||
}
|
||||
@@ -542,9 +543,7 @@ class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
private void assertEqualFileContents(final ArtifactStream artifact, final byte[] randomBytes) {
|
||||
try (final InputStream inputStream = artifact) {
|
||||
assertTrue(
|
||||
IOUtils.contentEquals(new ByteArrayInputStream(randomBytes), inputStream),
|
||||
"The stored binary matches the given binary");
|
||||
assertArrayEquals(inputStream.readAllBytes(), randomBytes);
|
||||
} catch (final IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.eclipse.hawkbit.auth.SpRole.CONTROLLER_ROLE;
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
|
||||
import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY;
|
||||
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs;
|
||||
import static org.eclipse.hawkbit.repository.test.util.TestdataFactory.DEFAULT_CONTROLLER_ID;
|
||||
@@ -37,6 +36,7 @@ import jakarta.validation.ConstraintViolationException;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.ql.jpa.SpecificationBuilder;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.TargetTypeManagement;
|
||||
import org.eclipse.hawkbit.repository.UpdateMode;
|
||||
@@ -85,6 +85,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* Feature: Component Tests - Repository<br/>
|
||||
@@ -203,12 +204,12 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Long actionId = getFirstAssignedActionId(assignDistributionSet(testDs, testTarget));
|
||||
|
||||
controllerManagement.addUpdateActionStatus(ActionStatusCreate.builder().actionId(actionId)
|
||||
.status(Action.Status.RUNNING).timestamp(java.lang.System.currentTimeMillis()).messages(List.of("proceeding message 1"))
|
||||
.status(Action.Status.RUNNING).timestamp(System.currentTimeMillis()).messages(List.of("proceeding message 1"))
|
||||
.build());
|
||||
|
||||
waitNextMillis();
|
||||
controllerManagement.addUpdateActionStatus(ActionStatusCreate.builder().actionId(actionId)
|
||||
.status(Action.Status.RUNNING).timestamp(java.lang.System.currentTimeMillis()).messages(List.of("proceeding message 2"))
|
||||
.status(Action.Status.RUNNING).timestamp(System.currentTimeMillis()).messages(List.of("proceeding message 2"))
|
||||
.build());
|
||||
|
||||
final List<String> messages = controllerManagement.getActionHistoryMessages(actionId, 2);
|
||||
@@ -242,7 +243,7 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(actionId1).isNotNull();
|
||||
final ActionStatusCreateBuilder status = ActionStatusCreate.builder().actionId(actionId1).status(Status.WARNING);
|
||||
for (int i = 0; i < maxStatusEntries; i++) {
|
||||
controllerManagement.addInformationalActionStatus(status.messages(List.of("Msg " + i)).timestamp(java.lang.System.currentTimeMillis()).build());
|
||||
controllerManagement.addInformationalActionStatus(status.messages(List.of("Msg " + i)).timestamp(System.currentTimeMillis()).build());
|
||||
}
|
||||
final ActionStatusCreate actionStatusCreate = status.build();
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
|
||||
@@ -254,7 +255,7 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(actionId2).isNotEqualTo(actionId1);
|
||||
final ActionStatusCreateBuilder statusWarning = ActionStatusCreate.builder().actionId(actionId2).status(Status.WARNING);
|
||||
for (int i = 0; i < maxStatusEntries; i++) {
|
||||
controllerManagement.addUpdateActionStatus(statusWarning.messages(List.of("Msg " + i)).timestamp(java.lang.System.currentTimeMillis()).build());
|
||||
controllerManagement.addUpdateActionStatus(statusWarning.messages(List.of("Msg " + i)).timestamp(System.currentTimeMillis()).build());
|
||||
}
|
||||
final ActionStatusCreate actionStatusCreateQE = statusWarning.build();
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
|
||||
@@ -858,7 +859,7 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Test
|
||||
void findOrRegisterTargetIfItDoesNotExistThrowsExceptionAfterMaxRetries() {
|
||||
final TargetRepository mockTargetRepository = Mockito.mock(TargetRepository.class);
|
||||
when(mockTargetRepository.findOne(any())).thenThrow(ConcurrencyFailureException.class);
|
||||
when(mockTargetRepository.findOne(any(Specification.class))).thenThrow(ConcurrencyFailureException.class);
|
||||
((JpaControllerManagement) controllerManagement).setTargetRepository(mockTargetRepository);
|
||||
|
||||
try {
|
||||
@@ -866,7 +867,7 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
.as("Expected an ConcurrencyFailureException to be thrown!")
|
||||
.isThrownBy(() -> controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST));
|
||||
|
||||
verify(mockTargetRepository, times(TX_RT_MAX)).findOne(any());
|
||||
verify(mockTargetRepository, times(10 /* default retry max */+ 1)).findOne(any(Specification.class));
|
||||
} finally {
|
||||
// revert
|
||||
((JpaControllerManagement) controllerManagement).setTargetRepository(targetRepository);
|
||||
@@ -887,14 +888,14 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
((JpaControllerManagement) controllerManagement).setTargetRepository(mockTargetRepository);
|
||||
final Target target = testdataFactory.createTarget();
|
||||
|
||||
when(mockTargetRepository.findOne(any())).thenThrow(ConcurrencyFailureException.class)
|
||||
when(mockTargetRepository.findOne(any(Specification.class))).thenThrow(ConcurrencyFailureException.class)
|
||||
.thenThrow(ConcurrencyFailureException.class).thenReturn(Optional.of((JpaTarget) target));
|
||||
when(mockTargetRepository.save(any())).thenReturn(target);
|
||||
|
||||
try {
|
||||
final Target targetFromControllerManagement = controllerManagement
|
||||
.findOrRegisterTargetIfItDoesNotExist(target.getControllerId(), LOCALHOST);
|
||||
verify(mockTargetRepository, times(3)).findOne(any());
|
||||
verify(mockTargetRepository, times(3)).findOne(any(Specification.class));
|
||||
verify(mockTargetRepository, times(1)).save(any());
|
||||
assertThat(target).isEqualTo(targetFromControllerManagement);
|
||||
} finally {
|
||||
@@ -937,14 +938,14 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final TargetRepository mockTargetRepository = Mockito.mock(TargetRepository.class);
|
||||
((JpaControllerManagement) controllerManagement).setTargetRepository(mockTargetRepository);
|
||||
|
||||
when(mockTargetRepository.findOne(any())).thenReturn(Optional.empty());
|
||||
when(mockTargetRepository.findOne(any(Specification.class))).thenReturn(Optional.empty());
|
||||
when(mockTargetRepository.save(any())).thenThrow(EntityAlreadyExistsException.class);
|
||||
|
||||
try {
|
||||
assertThatExceptionOfType(EntityAlreadyExistsException.class)
|
||||
.as("Expected an EntityAlreadyExistsException to be thrown!")
|
||||
.isThrownBy(() -> controllerManagement.findOrRegisterTargetIfItDoesNotExist("1234", LOCALHOST));
|
||||
verify(mockTargetRepository, times(1)).findOne(any());
|
||||
verify(mockTargetRepository, times(1)).findOne(any(Specification.class));
|
||||
verify(mockTargetRepository, times(1)).save(any());
|
||||
} finally {
|
||||
// revert
|
||||
@@ -962,13 +963,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final TargetRepository mockTargetRepository = Mockito.mock(TargetRepository.class);
|
||||
((JpaControllerManagement) controllerManagement).setTargetRepository(mockTargetRepository);
|
||||
|
||||
when(mockTargetRepository.findOne(any())).thenThrow(RuntimeException.class);
|
||||
when(mockTargetRepository.findOne(any(Specification.class))).thenThrow(RuntimeException.class);
|
||||
|
||||
try {
|
||||
assertThatExceptionOfType(RuntimeException.class).as("Expected a RuntimeException to be thrown!")
|
||||
.isThrownBy(() -> controllerManagement.findOrRegisterTargetIfItDoesNotExist("aControllerId",
|
||||
LOCALHOST));
|
||||
verify(mockTargetRepository, times(1)).findOne(any());
|
||||
verify(mockTargetRepository, times(1)).findOne(any(Specification.class));
|
||||
} finally {
|
||||
// revert
|
||||
((JpaControllerManagement) controllerManagement).setTargetRepository(targetRepository);
|
||||
|
||||
@@ -211,7 +211,6 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
*/
|
||||
@Test
|
||||
void assertMaxActionsPerTargetQuotaIsEnforced() {
|
||||
|
||||
final int maxActions = quotaManagement.getMaxActionsPerTarget();
|
||||
final Target testTarget = testdataFactory.createTarget();
|
||||
final List<Long> dsIds = new ArrayList<>();
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.util.stream.Stream;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import jakarta.validation.ValidationException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
@@ -118,7 +117,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
testdataFactory.createTargets(targetPrefix, 0, amountGroups * 2);
|
||||
final Rollout dynamicRollout = testdataFactory.createRolloutByVariables("dynamic", "static rollout", amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "0", RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP, "30", ActionType.FORCED, 1000, false, true);
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "0", RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP, "30",
|
||||
ActionType.FORCED, 1000, false, true);
|
||||
rolloutManagement.start(dynamicRollout.getId());
|
||||
rolloutHandler.handleAll();
|
||||
assertRollout(dynamicRollout, true, RolloutStatus.RUNNING, amountGroups + 1, amountGroups * 2);
|
||||
@@ -153,7 +153,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
testdataFactory.createTargets(targetPrefix, amountGroups * 2, amountGroups);
|
||||
final Rollout staticRollout = testdataFactory.createRolloutByVariables("static", "static rollout", amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "0", RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP,"30", ActionType.FORCED, 0, false, false);
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "0", RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP, "30",
|
||||
ActionType.FORCED, 0, false, false);
|
||||
rolloutManagement.start(staticRollout.getId());
|
||||
rolloutHandler.handleAll();
|
||||
assertRollout(staticRollout, false, RolloutStatus.RUNNING, amountGroups, amountGroups * 3);
|
||||
|
||||
@@ -217,7 +217,7 @@ class TargetManagementTest extends AbstractRepositoryManagementWithMetadataTest<
|
||||
|
||||
createTargetWithAttributes("4711");
|
||||
|
||||
final long current = java.lang.System.currentTimeMillis();
|
||||
final long current = System.currentTimeMillis();
|
||||
controllerManagement.findOrRegisterTargetIfItDoesNotExist("4711", LOCALHOST);
|
||||
|
||||
final DistributionSetAssignmentResult result = assignDistributionSet(testDs1.getId(), "4711");
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
@@ -185,23 +186,20 @@ class AutoAssignHandlerIntTest extends AbstractJpaIntegrationTest {
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("confirmationOptions")
|
||||
void checkAutoAssignWithConfirmationOptions(final boolean confirmationFlowActive, final boolean confirmationRequired,
|
||||
final Action.Status expectedStatus) {
|
||||
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsA");
|
||||
|
||||
void checkAutoAssignWithConfirmationOptions(
|
||||
final boolean confirmationFlowActive, final boolean confirmationRequired, final Action.Status expectedStatus) {
|
||||
if (confirmationFlowActive) {
|
||||
enableConfirmationFlow();
|
||||
}
|
||||
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsA");
|
||||
targetFilterQueryManagement.updateAutoAssignDS(
|
||||
new AutoAssignDistributionSetUpdate(targetFilterQueryManagement
|
||||
.create(Create.builder().name("filterA").query("name==*").build()).getId())
|
||||
.ds(distributionSet.getId()).confirmationRequired(confirmationRequired));
|
||||
|
||||
final String targetDsAIdPref = "targ";
|
||||
final List<Target> targets = testdataFactory.createTargets(20, targetDsAIdPref,
|
||||
targetDsAIdPref.concat(SPACE_AND_DESCRIPTION));
|
||||
final List<Target> targets = testdataFactory.createTargets(20, targetDsAIdPref, targetDsAIdPref.concat(SPACE_AND_DESCRIPTION));
|
||||
|
||||
// Run the check
|
||||
autoAssignChecker.handleAll();
|
||||
@@ -214,15 +212,13 @@ class AutoAssignHandlerIntTest extends AbstractJpaIntegrationTest {
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("confirmationOptions")
|
||||
void checkAutoAssignmentForDeviceWithConfirmationRequired(final boolean confirmationFlowActive,
|
||||
final boolean confirmationRequired, final Action.Status expectedStatus) {
|
||||
|
||||
final DistributionSet toAssignDs = testdataFactory.createDistributionSet();
|
||||
|
||||
void checkAutoAssignmentForDeviceWithConfirmationRequired(
|
||||
final boolean confirmationFlowActive, final boolean confirmationRequired, final Action.Status expectedStatus) {
|
||||
if (confirmationFlowActive) {
|
||||
enableConfirmationFlow();
|
||||
}
|
||||
|
||||
final DistributionSet toAssignDs = testdataFactory.createDistributionSet();
|
||||
// target filter query that matches all targets
|
||||
targetFilterQueryManagement.updateAutoAssignDS(
|
||||
new AutoAssignDistributionSetUpdate(targetFilterQueryManagement
|
||||
@@ -243,7 +239,6 @@ class AutoAssignHandlerIntTest extends AbstractJpaIntegrationTest {
|
||||
*/
|
||||
@Test
|
||||
void checkAutoAssignWithFailures() {
|
||||
|
||||
// incomplete distribution set that will be assigned
|
||||
final DistributionSet setF = distributionSetManagement.create(DistributionSetManagement.Create.builder()
|
||||
.type(testdataFactory.findOrCreateDefaultTestDsType())
|
||||
@@ -255,23 +250,21 @@ class AutoAssignHandlerIntTest extends AbstractJpaIntegrationTest {
|
||||
final String targetDsAIdPref = "targA";
|
||||
final String targetDsFIdPref = "targB";
|
||||
|
||||
final Long filterId = targetFilterQueryManagement.create(
|
||||
Create.builder().name("filterA").query("id==" + targetDsFIdPref + "*").build())
|
||||
.getId();
|
||||
final Long filterId = targetFilterQueryManagement
|
||||
.create(Create.builder().name("filterA").query("id==" + targetDsFIdPref + "*").build()).getId();
|
||||
final AutoAssignDistributionSetUpdate targetFilterQuery = new AutoAssignDistributionSetUpdate(filterId).ds(setF.getId());
|
||||
// target filter query that matches first bunch of targets, that should fail
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class).isThrownBy(
|
||||
() -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery));
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class)
|
||||
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery));
|
||||
// target filter query that matches failed bunch of targets
|
||||
targetFilterQueryManagement.create(Create.builder().name("filterB")
|
||||
.query("id==" + targetDsAIdPref + "*").autoAssignDistributionSet(setA).build());
|
||||
targetFilterQueryManagement.create(Create.builder()
|
||||
.name("filterB").query("id==" + targetDsAIdPref + "*").autoAssignDistributionSet(setA)
|
||||
.build());
|
||||
implicitLock(setA);
|
||||
|
||||
final List<Target> targetsF = testdataFactory.createTargets(10, targetDsFIdPref,
|
||||
targetDsFIdPref.concat(SPACE_AND_DESCRIPTION));
|
||||
final List<Target> targetsF = testdataFactory.createTargets(10, targetDsFIdPref, targetDsFIdPref.concat(SPACE_AND_DESCRIPTION));
|
||||
|
||||
final List<Target> targetsA = testdataFactory.createTargets(10, targetDsAIdPref,
|
||||
targetDsAIdPref.concat(SPACE_AND_DESCRIPTION));
|
||||
final List<Target> targetsA = testdataFactory.createTargets(10, targetDsAIdPref, targetDsAIdPref.concat(SPACE_AND_DESCRIPTION));
|
||||
|
||||
final int targetsCount = targetsA.size() + targetsF.size();
|
||||
|
||||
@@ -288,7 +281,6 @@ class AutoAssignHandlerIntTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
// all targets of A group should have received setA
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setA, targetsA, targetsCount);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -448,16 +440,13 @@ class AutoAssignHandlerIntTest extends AbstractJpaIntegrationTest {
|
||||
.isEqualTo(targetFilterQuery.getAutoAssignInitiatedBy()));
|
||||
}
|
||||
|
||||
private List<Target> createTargetsAndAutoAssignDistSet(final String prefix, final int targetCount,
|
||||
final DistributionSet distributionSet, final ActionType actionType) {
|
||||
|
||||
final List<Target> targets = testdataFactory.createTargets(targetCount, "target" + prefix,
|
||||
prefix.concat(SPACE_AND_DESCRIPTION));
|
||||
private List<Target> createTargetsAndAutoAssignDistSet(
|
||||
final String prefix, final int targetCount, final DistributionSet distributionSet, final ActionType actionType) {
|
||||
final List<Target> targets = testdataFactory.createTargets(targetCount, "target" + prefix, prefix.concat(SPACE_AND_DESCRIPTION));
|
||||
targetFilterQueryManagement.create(Create.builder()
|
||||
.name("filter" + prefix).query("id==target" + prefix + "*")
|
||||
.autoAssignDistributionSet(distributionSet).autoAssignActionType(actionType)
|
||||
.build());
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
@@ -466,12 +455,12 @@ class AutoAssignHandlerIntTest extends AbstractJpaIntegrationTest {
|
||||
.map(Target::getControllerId)
|
||||
.flatMap(controllerId -> deploymentManagement.findActionsByTarget(controllerId, PAGE).getContent().stream())
|
||||
.toList();
|
||||
|
||||
assertThat(actions).hasSize(targets.size());
|
||||
assertThat(actions).allMatch(action -> action.getActionType().equals(actionType));
|
||||
}
|
||||
|
||||
private Slice<Action> findActionsByDistributionSet(final Pageable pageable, final long distributionSetId) {
|
||||
|
||||
private Slice<@NonNull Action> findActionsByDistributionSet(final Pageable pageable, final long distributionSetId) {
|
||||
return actionRepository
|
||||
.findAll(byDistributionSetId(distributionSetId), pageable)
|
||||
.map(Action.class::cast);
|
||||
|
||||
@@ -58,8 +58,8 @@ logging.level.org.eclipse.persistence=${NOISE_SUPPRESS_LEVEL}
|
||||
## enable / disable case sensitiveness of the DB when playing around
|
||||
#hawkbit.ql.case-insensitive-db=true
|
||||
|
||||
hawkbit.repository.cluster.lock.ttl=3000
|
||||
hawkbit.repository.cluster.lock.refreshOnRemainMS=2000
|
||||
hawkbit.repository.cluster.lock.ttl=1000
|
||||
hawkbit.repository.cluster.lock.refreshOnRemainMS=200
|
||||
hawkbit.repository.cluster.lock.refreshOnRemainPercent=10
|
||||
## reduce scheduler tic period to speed up tests
|
||||
hawkbit.repository.cluster.lock.ticPeriodMS=10
|
||||
|
||||
Reference in New Issue
Block a user