Code format hawkbit-repository-jpa (#1928)
Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# hawkBit JPA implementation
|
||||
|
||||
JPA implementation of the repository. Based on [EclipseLink](http://www.eclipse.org/eclipselink/) and [Spring Data Jpa](http://projects.spring.io/spring-data-jpa/).
|
||||
JPA implementation of the repository. Based on [EclipseLink](http://www.eclipse.org/eclipselink/)
|
||||
and [Spring Data Jpa](http://projects.spring.io/spring-data-jpa/).
|
||||
@@ -9,155 +9,155 @@
|
||||
SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<version>${revision}</version>
|
||||
<artifactId>hawkbit-repository</artifactId>
|
||||
</parent>
|
||||
<artifactId>hawkbit-repository-jpa</artifactId>
|
||||
<name>hawkBit :: Repository :: JPA Implementation</name>
|
||||
|
||||
<properties>
|
||||
<apt.source.dir>${project.build.directory}/generated-sources/apt/</apt.source.dir>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Hawkbit -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-artifact-repository-filesystem</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-mysql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cz.jirutka.rsql</groupId>
|
||||
<artifactId>rsql-parser</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.el</groupId>
|
||||
<artifactId>javax.el-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.qameta.allure</groupId>
|
||||
<artifactId>allure-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Static weaver for EclipseLink -->
|
||||
<plugin>
|
||||
<groupId>com.ethlo.persistence.tools</groupId>
|
||||
<artifactId>eclipselink-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>modelgen</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>modelgen</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>weave</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<basePackage>org.eclipse.hawkbit.repository.jpa.model</basePackage>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
<version>${eclipselink.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!-- Should be placed here below 'eclipselink-maven-plugin' as the plugin should be executed -->
|
||||
<!-- after the code generation and the order of the plugins in the pom is relevant for maven -->
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>${apt.source.dir}</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<version>${revision}</version>
|
||||
<artifactId>hawkbit-repository</artifactId>
|
||||
</parent>
|
||||
<artifactId>hawkbit-repository-jpa</artifactId>
|
||||
<name>hawkBit :: Repository :: JPA Implementation</name>
|
||||
|
||||
<properties>
|
||||
<apt.source.dir>${project.build.directory}/generated-sources/apt/</apt.source.dir>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Hawkbit -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-artifact-repository-filesystem</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-mysql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cz.jirutka.rsql</groupId>
|
||||
<artifactId>rsql-parser</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.el</groupId>
|
||||
<artifactId>javax.el-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.qameta.allure</groupId>
|
||||
<artifactId>allure-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Static weaver for EclipseLink -->
|
||||
<plugin>
|
||||
<groupId>com.ethlo.persistence.tools</groupId>
|
||||
<artifactId>eclipselink-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>modelgen</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>modelgen</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>weave</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<basePackage>org.eclipse.hawkbit.repository.jpa.model</basePackage>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
<version>${eclipselink.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!-- Should be placed here below 'eclipselink-maven-plugin' as the plugin should be executed -->
|
||||
<!-- after the code generation and the order of the plugins in the pom is relevant for maven -->
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>${apt.source.dir}</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -19,13 +19,12 @@ import org.springframework.stereotype.Service;
|
||||
* used by spring caching framework to resolve the key-generator. The
|
||||
* key-generator must registered as bean so spring can resolve the key-generator
|
||||
* by its name.
|
||||
*
|
||||
*
|
||||
* When using the {@link Service} annotation e.g. by {@link JpaSystemManagement}
|
||||
* the bean registration must be declared by the interface due spring registers
|
||||
* the bean by the implemented interfaces. So introduce a single interface for
|
||||
* the {@link JpaSystemManagement} implementation to allow it to register the
|
||||
* key-generator bean.
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CurrentTenantCacheKeyGenerator {
|
||||
@@ -33,7 +32,7 @@ public interface CurrentTenantCacheKeyGenerator {
|
||||
/**
|
||||
* Bean declaration to register a {@code currentTenantKeyGenerator} bean
|
||||
* which is used by the caching framework.
|
||||
*
|
||||
*
|
||||
* @return the {@link KeyGenerator} to be used to cache the values of the
|
||||
* current used tenant in the {@link JpaSystemManagement}
|
||||
*/
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.springframework.data.repository.core.support.RepositoryFactorySupport
|
||||
* A {@link JpaRepositoryFactoryBean} extension that allow injection of custom
|
||||
* repository factories by using a {@link BaseRepositoryTypeProvider}
|
||||
* implementation, allows injecting different base repository implementations based on repository type
|
||||
*
|
||||
*
|
||||
* @param <T>
|
||||
* @param <S>
|
||||
* @param <ID>
|
||||
@@ -36,8 +36,7 @@ public class CustomBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
|
||||
* Creates a new {@link JpaRepositoryFactoryBean} for the given repository
|
||||
* interface.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
* must not be {@literal null}.
|
||||
* @param repositoryInterface must not be {@literal null}.
|
||||
*/
|
||||
public CustomBaseRepositoryFactoryBean(final Class<? extends T> repositoryInterface) {
|
||||
super(repositoryInterface);
|
||||
|
||||
@@ -57,6 +57,30 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
|
||||
return isApprovalEnabled() && hasNoApproveRolloutPermission(getActorAuthorities(rollout));
|
||||
}
|
||||
|
||||
/***
|
||||
* Per default do nothing.
|
||||
*
|
||||
* @param rollout
|
||||
* rollout to create approval task for.
|
||||
*/
|
||||
@Override
|
||||
public void onApprovalRequired(final Rollout rollout) {
|
||||
// do nothing per default, can be extended by further implementations.
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApprovalUser(final Rollout rollout) {
|
||||
return getCurrentAuthentication().getName();
|
||||
}
|
||||
|
||||
private static Authentication getCurrentAuthentication() {
|
||||
return SecurityContextHolder.getContext().getAuthentication();
|
||||
}
|
||||
|
||||
private static boolean hasNoApproveRolloutPermission(final Collection<String> authorities) {
|
||||
return authorities.stream().noneMatch(SpPermission.APPROVE_ROLLOUT::equals);
|
||||
}
|
||||
|
||||
private boolean isApprovalEnabled() {
|
||||
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
|
||||
.getConfigurationValue(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue());
|
||||
@@ -78,28 +102,4 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
|
||||
return ((User) getCurrentAuthentication().getPrincipal()).getAuthorities().stream()
|
||||
.map(GrantedAuthority::getAuthority).toList();
|
||||
}
|
||||
|
||||
private static Authentication getCurrentAuthentication() {
|
||||
return SecurityContextHolder.getContext().getAuthentication();
|
||||
}
|
||||
|
||||
private static boolean hasNoApproveRolloutPermission(final Collection<String> authorities) {
|
||||
return authorities.stream().noneMatch(SpPermission.APPROVE_ROLLOUT::equals);
|
||||
}
|
||||
|
||||
/***
|
||||
* Per default do nothing.
|
||||
*
|
||||
* @param rollout
|
||||
* rollout to create approval task for.
|
||||
*/
|
||||
@Override
|
||||
public void onApprovalRequired(final Rollout rollout) {
|
||||
// do nothing per default, can be extended by further implementations.
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApprovalUser(final Rollout rollout) {
|
||||
return getCurrentAuthentication().getName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,63 +27,56 @@ public interface EntityInterceptor {
|
||||
|
||||
/**
|
||||
* Callback for the {@link PrePersist} lifecycle event.
|
||||
*
|
||||
* @param entity
|
||||
* the model entity
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void prePersist(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PostPersist} lifecycle event.
|
||||
*
|
||||
* @param entity
|
||||
* the model entity
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void postPersist(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PostRemove} lifecycle event.
|
||||
*
|
||||
* @param entity
|
||||
* the model entity
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void postRemove(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PreRemove} lifecycle event.
|
||||
*
|
||||
* @param entity
|
||||
* the model entity
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void preRemove(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PostLoad} lifecycle event.
|
||||
*
|
||||
* @param entity
|
||||
* the model entity
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void postLoad(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PreUpdate} lifecycle event.
|
||||
*
|
||||
* @param entity
|
||||
* the model entity
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void preUpdate(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PostUpdate} lifecycle event.
|
||||
*
|
||||
* @param entity
|
||||
* the model entity
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void postUpdate(final Object entity) {
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
|
||||
/**
|
||||
* {@link EclipseLinkJpaDialect} with additional exception translation
|
||||
* mechanisms based on {@link SQLStateSQLExceptionTranslator}.
|
||||
*
|
||||
*
|
||||
* There are multiple variations of exceptions coming out of persistence
|
||||
* provider:
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* 1) {@link PersistenceException}s that can be mapped by
|
||||
* {@link EclipseLinkJpaDialect} into corresponding {@link DataAccessException}.
|
||||
@@ -47,9 +47,9 @@ import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
|
||||
* <p>
|
||||
* 3.b.) the cause is not an {@link SQLException} and as a result cannot be
|
||||
* mapped.
|
||||
*
|
||||
*/
|
||||
public class HawkBitEclipseLinkJpaDialect extends EclipseLinkJpaDialect {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final SQLStateSQLExceptionTranslator SQLSTATE_EXCEPTION_TRANSLATOR = new SQLStateSQLExceptionTranslator();
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
@@ -36,7 +35,6 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* JPA Implementation of {@link EntityFactory}.
|
||||
*
|
||||
*/
|
||||
@Validated
|
||||
public class JpaEntityFactory implements EntityFactory {
|
||||
@@ -65,6 +63,16 @@ public class JpaEntityFactory implements EntityFactory {
|
||||
@Autowired
|
||||
private TargetTypeBuilder targetTypeBuilder;
|
||||
|
||||
@Override
|
||||
public ActionStatusBuilder actionStatus() {
|
||||
return new JpaActionStatusBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetBuilder distributionSet() {
|
||||
return distributionSetBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaData generateDsMetadata(final String key, final String value) {
|
||||
return new JpaDistributionSetMetadata(key, StringUtils.trimWhitespace(value));
|
||||
@@ -76,23 +84,8 @@ public class JpaEntityFactory implements EntityFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetTypeBuilder distributionSetType() {
|
||||
return distributionSetTypeBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetBuilder distributionSet() {
|
||||
return distributionSetBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetBuilder target() {
|
||||
return targetBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetTypeBuilder targetType() {
|
||||
return targetTypeBuilder;
|
||||
public SoftwareModuleMetadataBuilder softwareModuleMetadata() {
|
||||
return softwareModuleMetadataBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,8 +94,18 @@ public class JpaEntityFactory implements EntityFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetFilterQueryBuilder targetFilterQuery() {
|
||||
return targetFilterQueryBuilder;
|
||||
public RolloutGroupBuilder rolloutGroup() {
|
||||
return new JpaRolloutGroupBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetTypeBuilder distributionSetType() {
|
||||
return distributionSetTypeBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutBuilder rollout() {
|
||||
return rolloutBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,23 +119,18 @@ public class JpaEntityFactory implements EntityFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionStatusBuilder actionStatus() {
|
||||
return new JpaActionStatusBuilder();
|
||||
public TargetBuilder target() {
|
||||
return targetBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutBuilder rollout() {
|
||||
return rolloutBuilder;
|
||||
public TargetTypeBuilder targetType() {
|
||||
return targetTypeBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutGroupBuilder rolloutGroup() {
|
||||
return new JpaRolloutGroupBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleMetadataBuilder softwareModuleMetadata() {
|
||||
return softwareModuleMetadataBuilder;
|
||||
public TargetFilterQueryBuilder targetFilterQuery() {
|
||||
return targetFilterQueryBuilder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.NoCountSliceRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -36,6 +33,7 @@ import org.springframework.util.StringUtils;
|
||||
* A collection of static helper methods for the management classes
|
||||
*/
|
||||
public final class JpaManagementHelper {
|
||||
|
||||
private JpaManagementHelper() {
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
@@ -73,8 +75,6 @@ import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions;
|
||||
|
||||
/**
|
||||
* A Jpa implementation of {@link RolloutExecutor}
|
||||
*/
|
||||
@@ -101,28 +101,25 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
*/
|
||||
private static final List<Status> DOWNLOAD_ONLY_ACTION_TERMINATION_STATUSES = Arrays.asList(Status.ERROR,
|
||||
Status.FINISHED, Status.CANCELED, Status.DOWNLOADED);
|
||||
|
||||
private static final Comparator<RolloutGroup> DESC_COMP = Comparator.comparingLong(RolloutGroup::getId).reversed();
|
||||
private final ActionRepository actionRepository;
|
||||
private final RolloutGroupRepository rolloutGroupRepository;
|
||||
private final RolloutTargetGroupRepository rolloutTargetGroupRepository;
|
||||
private final RolloutRepository rolloutRepository;
|
||||
|
||||
private final TargetManagement targetManagement;
|
||||
private final DeploymentManagement deploymentManagement;
|
||||
private final RolloutGroupManagement rolloutGroupManagement;
|
||||
private final RolloutManagement rolloutManagement;
|
||||
private final QuotaManagement quotaManagement;
|
||||
|
||||
private final RolloutGroupEvaluationManager evaluationManager;
|
||||
private final RolloutApprovalStrategy rolloutApprovalStrategy;
|
||||
|
||||
private final EntityManager entityManager;
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final AfterTransactionCommitExecutor afterCommit;
|
||||
private final EventPublisherHolder eventPublisherHolder;
|
||||
|
||||
private final TenantAware tenantAware;
|
||||
private final RepositoryProperties repositoryProperties;
|
||||
private final Map<Long, AtomicLong> lastDynamicGroupFill = new ConcurrentHashMap<>();
|
||||
|
||||
public JpaRolloutExecutor(
|
||||
final ActionRepository actionRepository, final RolloutGroupRepository rolloutGroupRepository,
|
||||
@@ -158,48 +155,48 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
log.debug("Processing rollout {}", rollout.getId());
|
||||
|
||||
switch (rollout.getStatus()) {
|
||||
case CREATING:
|
||||
handleCreateRollout((JpaRollout) rollout);
|
||||
break;
|
||||
case READY:
|
||||
handleReadyRollout(rollout);
|
||||
break;
|
||||
case STARTING:
|
||||
// the lastModifiedBy user is probably the user that has actually called the rollout start (unless overridden) - not the creator
|
||||
SpringSecurityAuditorAware.setAuditorOverride(rollout.getLastModifiedBy());
|
||||
try {
|
||||
handleStartingRollout(rollout);
|
||||
} finally {
|
||||
// clear, ALWAYS, the set auditor override
|
||||
SpringSecurityAuditorAware.clearAuditorOverride();
|
||||
}
|
||||
break;
|
||||
case RUNNING:
|
||||
handleRunningRollout((JpaRollout) rollout);
|
||||
break;
|
||||
case STOPPING:
|
||||
// the lastModifiedBy user is probably the user that has actually called the rollout stop (unless overridden) - not the creator
|
||||
SpringSecurityAuditorAware.setAuditorOverride(rollout.getLastModifiedBy());
|
||||
try {
|
||||
handleStopRollout((JpaRollout) rollout);
|
||||
} finally {
|
||||
// clear, ALWAYS, the set auditor override
|
||||
SpringSecurityAuditorAware.clearAuditorOverride();
|
||||
}
|
||||
break;
|
||||
case DELETING:
|
||||
// the lastModifiedBy user is probably the user that has actually called the rollout delete (unless overridden) - not the creator
|
||||
SpringSecurityAuditorAware.setAuditorOverride(rollout.getLastModifiedBy());
|
||||
try {
|
||||
handleDeleteRollout((JpaRollout) rollout);
|
||||
} finally {
|
||||
// clear, ALWAYS, the set auditor override
|
||||
SpringSecurityAuditorAware.clearAuditorOverride();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log.error("Rollout in status {} not supposed to be handled!", rollout.getStatus());
|
||||
break;
|
||||
case CREATING:
|
||||
handleCreateRollout((JpaRollout) rollout);
|
||||
break;
|
||||
case READY:
|
||||
handleReadyRollout(rollout);
|
||||
break;
|
||||
case STARTING:
|
||||
// the lastModifiedBy user is probably the user that has actually called the rollout start (unless overridden) - not the creator
|
||||
SpringSecurityAuditorAware.setAuditorOverride(rollout.getLastModifiedBy());
|
||||
try {
|
||||
handleStartingRollout(rollout);
|
||||
} finally {
|
||||
// clear, ALWAYS, the set auditor override
|
||||
SpringSecurityAuditorAware.clearAuditorOverride();
|
||||
}
|
||||
break;
|
||||
case RUNNING:
|
||||
handleRunningRollout((JpaRollout) rollout);
|
||||
break;
|
||||
case STOPPING:
|
||||
// the lastModifiedBy user is probably the user that has actually called the rollout stop (unless overridden) - not the creator
|
||||
SpringSecurityAuditorAware.setAuditorOverride(rollout.getLastModifiedBy());
|
||||
try {
|
||||
handleStopRollout((JpaRollout) rollout);
|
||||
} finally {
|
||||
// clear, ALWAYS, the set auditor override
|
||||
SpringSecurityAuditorAware.clearAuditorOverride();
|
||||
}
|
||||
break;
|
||||
case DELETING:
|
||||
// the lastModifiedBy user is probably the user that has actually called the rollout delete (unless overridden) - not the creator
|
||||
SpringSecurityAuditorAware.setAuditorOverride(rollout.getLastModifiedBy());
|
||||
try {
|
||||
handleDeleteRollout((JpaRollout) rollout);
|
||||
} finally {
|
||||
// clear, ALWAYS, the set auditor override
|
||||
SpringSecurityAuditorAware.clearAuditorOverride();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log.error("Rollout in status {} not supposed to be handled!", rollout.getStatus());
|
||||
break;
|
||||
}
|
||||
|
||||
log.debug("Rollout {} processed", rollout.getId());
|
||||
@@ -312,9 +309,9 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
|
||||
rolloutGroupRepository.findByRolloutAndStatusNotIn(rollout,
|
||||
Arrays.asList(RolloutGroupStatus.FINISHED, RolloutGroupStatus.ERROR)).forEach(rolloutGroup -> {
|
||||
rolloutGroup.setStatus(RolloutGroupStatus.FINISHED);
|
||||
rolloutGroupRepository.save(rolloutGroup);
|
||||
});
|
||||
rolloutGroup.setStatus(RolloutGroupStatus.FINISHED);
|
||||
rolloutGroupRepository.save(rolloutGroup);
|
||||
});
|
||||
|
||||
rollout.setStatus(RolloutStatus.FINISHED);
|
||||
rolloutRepository.save(rollout);
|
||||
@@ -417,7 +414,6 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
return groupsActiveLeft == 0;
|
||||
}
|
||||
|
||||
private static final Comparator<RolloutGroup> DESC_COMP = Comparator.comparingLong(RolloutGroup::getId).reversed();
|
||||
private void executeLatestRolloutGroup(final JpaRollout rollout) {
|
||||
// was - rolloutGroupRepository.findByRolloutAndStatusNotOrderByIdDesc(rollout, RolloutGroupStatus.SCHEDULED);
|
||||
final List<JpaRolloutGroup> latestRolloutGroup = rollout.getRolloutGroups().stream()
|
||||
@@ -435,10 +431,10 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
// so the evaluation to use total targets to properly
|
||||
private RolloutGroup evalProxy(final RolloutGroup group) {
|
||||
if (group.isDynamic()) {
|
||||
final int expected = Math.max((int)group.getTargetPercentage(), 1);
|
||||
final int expected = Math.max((int) group.getTargetPercentage(), 1);
|
||||
return (RolloutGroup) Proxy.newProxyInstance(
|
||||
RolloutGroup.class.getClassLoader(),
|
||||
new Class<?>[] {RolloutGroup.class},
|
||||
new Class<?>[] { RolloutGroup.class },
|
||||
(proxy, method, args) -> {
|
||||
if ("getTotalTargets".equals(method.getName())) {
|
||||
return expected;
|
||||
@@ -601,14 +597,14 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
long targetsInGroupFilter;
|
||||
if (!RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) {
|
||||
targetsInGroupFilter = DeploymentHelper.runInNewTransaction(txManager,
|
||||
"countAllTargetsByTargetFilterQueryAndNotInRolloutGroups",
|
||||
count -> targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(readyGroups,
|
||||
groupTargetFilter, rollout.getDistributionSet().getType()));
|
||||
"countAllTargetsByTargetFilterQueryAndNotInRolloutGroups",
|
||||
count -> targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(readyGroups,
|
||||
groupTargetFilter, rollout.getDistributionSet().getType()));
|
||||
} else {
|
||||
targetsInGroupFilter = DeploymentHelper.runInNewTransaction(txManager,
|
||||
"countByFailedRolloutAndNotInRolloutGroupsAndCompatible",
|
||||
count -> targetManagement.countByFailedRolloutAndNotInRolloutGroups(readyGroups,
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(rollout.getTargetFilterQuery())));
|
||||
"countByFailedRolloutAndNotInRolloutGroupsAndCompatible",
|
||||
count -> targetManagement.countByFailedRolloutAndNotInRolloutGroups(readyGroups,
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(rollout.getTargetFilterQuery())));
|
||||
}
|
||||
|
||||
final double percentFromTheRest;
|
||||
@@ -677,7 +673,6 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
});
|
||||
}
|
||||
|
||||
private final Map<Long, AtomicLong> lastDynamicGroupFill = new ConcurrentHashMap<>();
|
||||
// return if group change is made
|
||||
private boolean fillDynamicRolloutGroupsWithTargets(final JpaRollout rollout) {
|
||||
final AtomicLong lastFill = lastDynamicGroupFill.computeIfAbsent(rollout.getId(), id -> new AtomicLong(0));
|
||||
@@ -690,9 +685,9 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.RUNNING);
|
||||
final List<RolloutGroup> rolloutGroups = rollout.getRolloutGroups();
|
||||
|
||||
final JpaRolloutGroup group = (JpaRolloutGroup)rolloutGroups.get(rolloutGroups.size() - 1);
|
||||
final JpaRolloutGroup group = (JpaRolloutGroup) rolloutGroups.get(rolloutGroups.size() - 1);
|
||||
|
||||
final long expectedInGroup = Math.max((int)group.getTargetPercentage(), 1);
|
||||
final long expectedInGroup = Math.max((int) group.getTargetPercentage(), 1);
|
||||
final long currentlyInGroup = group.getTotalTargets();
|
||||
if (currentlyInGroup >= expectedInGroup || group.getStatus() == RolloutGroupStatus.FINISHED) {
|
||||
// the last one is full. create new and start filling it on the next iteration
|
||||
@@ -741,7 +736,8 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void createDynamicGroup(final JpaRollout rollout, final RolloutGroup lastGroup, final int groupCount, final RolloutGroupStatus status) {
|
||||
private void createDynamicGroup(final JpaRollout rollout, final RolloutGroup lastGroup, final int groupCount,
|
||||
final RolloutGroupStatus status) {
|
||||
try {
|
||||
RolloutHelper.verifyRolloutGroupAmount(groupCount + 1, quotaManagement);
|
||||
} catch (final AssignmentQuotaExceededException e) {
|
||||
@@ -755,7 +751,8 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
|
||||
final JpaRolloutGroup group = new JpaRolloutGroup();
|
||||
final String lastGroupWithoutSuffix = "group-" + groupCount;
|
||||
final String suffix = lastGroup.getName().startsWith(lastGroupWithoutSuffix) ? lastGroup.getName().substring(lastGroupWithoutSuffix.length()) : "";
|
||||
final String suffix = lastGroup.getName().startsWith(lastGroupWithoutSuffix) ? lastGroup.getName()
|
||||
.substring(lastGroupWithoutSuffix.length()) : "";
|
||||
final String nameAndDesc = "group-" + (groupCount + 1) + suffix;
|
||||
group.setName(nameAndDesc);
|
||||
group.setDescription(nameAndDesc);
|
||||
|
||||
@@ -37,17 +37,12 @@ public class JpaRolloutHandler implements RolloutHandler {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param tenantAware
|
||||
* the {@link TenantAware} bean holding the tenant information
|
||||
* @param rolloutManagement
|
||||
* to fetch rollout related information from the datasource
|
||||
* @param rolloutExecutor
|
||||
* to trigger executions for a specific rollout
|
||||
* @param lockRegistry
|
||||
* to lock processes
|
||||
* @param txManager
|
||||
* transaction manager interface
|
||||
*
|
||||
* @param tenantAware the {@link TenantAware} bean holding the tenant information
|
||||
* @param rolloutManagement to fetch rollout related information from the datasource
|
||||
* @param rolloutExecutor to trigger executions for a specific rollout
|
||||
* @param lockRegistry to lock processes
|
||||
* @param txManager transaction manager interface
|
||||
*/
|
||||
public JpaRolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement,
|
||||
final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry,
|
||||
@@ -83,8 +78,9 @@ public class JpaRolloutHandler implements RolloutHandler {
|
||||
try {
|
||||
handleRolloutInNewTransaction(rolloutId, handlerId);
|
||||
} catch (final Throwable throwable) {
|
||||
log.error("Failed to process rollout with id {}", rolloutId , throwable);
|
||||
}});
|
||||
log.error("Failed to process rollout with id {}", rolloutId, throwable);
|
||||
}
|
||||
});
|
||||
log.debug("Finished handling of the rollouts.");
|
||||
} finally {
|
||||
if (log.isTraceEnabled()) {
|
||||
@@ -107,17 +103,17 @@ public class JpaRolloutHandler implements RolloutHandler {
|
||||
// auditor is retrieved and set on transaction commit
|
||||
// if not overridden, the system user will be the auditor
|
||||
rollout.getAccessControlContext().ifPresentOrElse(
|
||||
context -> // has stored context - executes it with it
|
||||
contextAware.runInContext(
|
||||
context,
|
||||
() -> rolloutExecutor.execute(rollout)),
|
||||
() -> // has no stored context - executes it in the tenant & user scope
|
||||
contextAware.runAsTenantAsUser(
|
||||
contextAware.getCurrentTenant(),
|
||||
rollout.getCreatedBy(), () -> {
|
||||
rolloutExecutor.execute(rollout);
|
||||
return null;
|
||||
})
|
||||
context -> // has stored context - executes it with it
|
||||
contextAware.runInContext(
|
||||
context,
|
||||
() -> rolloutExecutor.execute(rollout)),
|
||||
() -> // has no stored context - executes it in the tenant & user scope
|
||||
contextAware.runAsTenantAsUser(
|
||||
contextAware.getCurrentTenant(),
|
||||
rollout.getCreatedBy(), () -> {
|
||||
rolloutExecutor.execute(rollout);
|
||||
return null;
|
||||
})
|
||||
);
|
||||
},
|
||||
() -> log.error("Could not retrieve rollout with id {}. Will not continue with execution.",
|
||||
|
||||
@@ -14,8 +14,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.validation.Validation;
|
||||
|
||||
import org.eclipse.hawkbit.ContextAware;
|
||||
@@ -120,11 +121,11 @@ import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetMetadataRepo
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.HawkBitBaseRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.LocalArtifactRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutTargetGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.HawkBitBaseRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
|
||||
@@ -155,9 +156,9 @@ import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder;
|
||||
import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle;
|
||||
import org.eclipse.hawkbit.repository.rsql.RsqlVisitorFactory;
|
||||
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder;
|
||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||
@@ -202,7 +203,6 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
|
||||
|
||||
/**
|
||||
* General configuration for hawkBit's Repository.
|
||||
*
|
||||
*/
|
||||
@EnableJpaRepositories(value = "org.eclipse.hawkbit.repository.jpa.repository", repositoryFactoryBeanClass = CustomBaseRepositoryFactoryBean.class)
|
||||
@EnableTransactionManagement
|
||||
@@ -223,10 +223,110 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
super(dataSource, properties, jtaTransactionManagerProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the validation processor bean.
|
||||
*
|
||||
* @return the {@link MethodValidationPostProcessor}
|
||||
*/
|
||||
@Bean
|
||||
public MethodValidationPostProcessor methodValidationPostProcessor() {
|
||||
final MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
|
||||
// ValidatorFactory shall NOT be closed because after closing the generated Validator
|
||||
// methods shall not be called - we need the validator in future
|
||||
processor.setValidator(Validation.byDefaultProvider().configure()
|
||||
.addProperty(BaseHibernateValidatorConfiguration.ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS,
|
||||
"true")
|
||||
.buildValidatorFactory().getValidator());
|
||||
return processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MultiTenantJpaTransactionManager} bean.
|
||||
*
|
||||
* @return a new {@link PlatformTransactionManager}
|
||||
* @see org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration#transactionManager(ObjectProvider)
|
||||
*/
|
||||
@Override
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager(
|
||||
final ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
|
||||
return new MultiTenantJpaTransactionManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
|
||||
return new EclipseLinkJpaVendorAdapter() {
|
||||
|
||||
private final HawkBitEclipseLinkJpaDialect jpaDialect = new HawkBitEclipseLinkJpaDialect();
|
||||
|
||||
@Override
|
||||
public EclipseLinkJpaDialect getJpaDialect() {
|
||||
return jpaDialect;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> getVendorProperties() {
|
||||
final Map<String, Object> properties = new HashMap<>(7);
|
||||
// Turn off dynamic weaving to disable LTW lookup in static weaving mode
|
||||
properties.put(PersistenceUnitProperties.WEAVING, "false");
|
||||
// needed for reports
|
||||
properties.put(PersistenceUnitProperties.ALLOW_NATIVE_SQL_QUERIES, "true");
|
||||
// flyway
|
||||
properties.put(PersistenceUnitProperties.DDL_GENERATION, "none");
|
||||
// Embed into hawkBit logging
|
||||
properties.put(PersistenceUnitProperties.LOGGING_LOGGER, "JavaLogger");
|
||||
// Ensure that we flush only at the end of the transaction
|
||||
properties.put(PersistenceUnitProperties.PERSISTENCE_CONTEXT_FLUSH_MODE, "COMMIT");
|
||||
// Enable batch writing
|
||||
properties.put(PersistenceUnitProperties.BATCH_WRITING, "JDBC");
|
||||
// Batch size
|
||||
properties.put(PersistenceUnitProperties.BATCH_WRITING_SIZE, "500");
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor entityManagerBeanPostProcessor(
|
||||
@Autowired(required = false) final AccessController<JpaArtifact> artifactAccessController,
|
||||
@Autowired(required = false) final AccessController<JpaSoftwareModuleType> softwareModuleTypeAccessController,
|
||||
@Autowired(required = false) final AccessController<JpaSoftwareModule> softwareModuleAccessController,
|
||||
@Autowired(required = false) final AccessController<JpaDistributionSetType> distributionSetTypeAccessController,
|
||||
@Autowired(required = false) final AccessController<JpaDistributionSet> distributionSetAccessController,
|
||||
@Autowired(required = false) final AccessController<JpaTargetType> targetTypeAccessControlManager,
|
||||
@Autowired(required = false) final AccessController<JpaTarget> targetAccessControlManager,
|
||||
@Autowired(required = false) final AccessController<JpaAction> actionAccessController) {
|
||||
return new BeanPostProcessor() {
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof LocalArtifactRepository repo) {
|
||||
return repo.withACM(artifactAccessController);
|
||||
} else if (bean instanceof SoftwareModuleTypeRepository repo) {
|
||||
return repo.withACM(softwareModuleTypeAccessController);
|
||||
} else if (bean instanceof SoftwareModuleRepository repo) {
|
||||
return repo.withACM(softwareModuleAccessController);
|
||||
} else if (bean instanceof DistributionSetTypeRepository repo) {
|
||||
return repo.withACM(distributionSetTypeAccessController);
|
||||
} else if (bean instanceof DistributionSetRepository repo) {
|
||||
return repo.withACM(distributionSetAccessController);
|
||||
} else if (bean instanceof TargetTypeRepository repo) {
|
||||
return repo.withACM(targetTypeAccessControlManager);
|
||||
} else if (bean instanceof TargetRepository repo) {
|
||||
return repo.withACM(targetAccessControlManager);
|
||||
} else if (bean instanceof ActionRepository repo) {
|
||||
return repo.withACM(actionAccessController);
|
||||
}
|
||||
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
PauseRolloutGroupAction pauseRolloutGroupAction(final RolloutManagement rolloutManagement,
|
||||
final RolloutGroupRepository rolloutGroupRepository, final SystemSecurityContext systemSecurityContext) {
|
||||
final RolloutGroupRepository rolloutGroupRepository, final SystemSecurityContext systemSecurityContext) {
|
||||
return new PauseRolloutGroupAction(rolloutManagement, rolloutGroupRepository, systemSecurityContext);
|
||||
}
|
||||
|
||||
@@ -299,10 +399,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param distributionSetTypeManagement
|
||||
* to loading the {@link DistributionSetType}
|
||||
* @param softwareManagement
|
||||
* for loading {@link DistributionSet#getModules()}
|
||||
* @param distributionSetTypeManagement to loading the {@link DistributionSetType}
|
||||
* @param softwareManagement for loading {@link DistributionSet#getModules()}
|
||||
* @return DistributionSetBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -317,8 +415,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dsTypeManagement
|
||||
* for loading {@link TargetType#getCompatibleDistributionSetTypes()}
|
||||
* @param dsTypeManagement for loading {@link TargetType#getCompatibleDistributionSetTypes()}
|
||||
* @return TargetTypeBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -333,9 +430,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param softwareModuleTypeManagement
|
||||
* for loading {@link DistributionSetType#getMandatoryModuleTypes()}
|
||||
* and {@link DistributionSetType#getOptionalModuleTypes()}
|
||||
* @param softwareModuleTypeManagement for loading {@link DistributionSetType#getMandatoryModuleTypes()}
|
||||
* and {@link DistributionSetType#getOptionalModuleTypes()}
|
||||
* @return DistributionSetTypeBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -345,8 +441,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param softwareModuleTypeManagement
|
||||
* for loading {@link SoftwareModule#getType()}
|
||||
* @param softwareModuleTypeManagement for loading {@link SoftwareModule#getType()}
|
||||
* @return SoftwareModuleBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -355,8 +450,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param distributionSetManagement
|
||||
* for loading {@link Rollout#getDistributionSet()}
|
||||
* @param distributionSetManagement for loading {@link Rollout#getDistributionSet()}
|
||||
* @return RolloutBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -365,9 +459,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param distributionSetManagement
|
||||
* for loading
|
||||
* {@link TargetFilterQuery#getAutoAssignDistributionSet()}
|
||||
* @param distributionSetManagement for loading
|
||||
* {@link TargetFilterQuery#getAutoAssignDistributionSet()}
|
||||
* @return TargetFilterQueryBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -434,7 +527,6 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the singleton instance of the
|
||||
* {@link AfterTransactionCommitExecutorHolder}
|
||||
*/
|
||||
@@ -443,23 +535,6 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
return AfterTransactionCommitExecutorHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the validation processor bean.
|
||||
*
|
||||
* @return the {@link MethodValidationPostProcessor}
|
||||
*/
|
||||
@Bean
|
||||
public MethodValidationPostProcessor methodValidationPostProcessor() {
|
||||
final MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
|
||||
// ValidatorFactory shall NOT be closed because after closing the generated Validator
|
||||
// methods shall not be called - we need the validator in future
|
||||
processor.setValidator(Validation.byDefaultProvider().configure()
|
||||
.addProperty(BaseHibernateValidatorConfiguration.ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS,
|
||||
"true")
|
||||
.buildValidatorFactory().getValidator());
|
||||
return processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link ExceptionMappingAspectHandler} aspect bean
|
||||
*/
|
||||
@@ -468,51 +543,6 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
return new ExceptionMappingAspectHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
|
||||
return new EclipseLinkJpaVendorAdapter() {
|
||||
private final HawkBitEclipseLinkJpaDialect jpaDialect = new HawkBitEclipseLinkJpaDialect();
|
||||
|
||||
@Override
|
||||
public EclipseLinkJpaDialect getJpaDialect() {
|
||||
return jpaDialect;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> getVendorProperties() {
|
||||
final Map<String, Object> properties = new HashMap<>(7);
|
||||
// Turn off dynamic weaving to disable LTW lookup in static weaving mode
|
||||
properties.put(PersistenceUnitProperties.WEAVING, "false");
|
||||
// needed for reports
|
||||
properties.put(PersistenceUnitProperties.ALLOW_NATIVE_SQL_QUERIES, "true");
|
||||
// flyway
|
||||
properties.put(PersistenceUnitProperties.DDL_GENERATION, "none");
|
||||
// Embed into hawkBit logging
|
||||
properties.put(PersistenceUnitProperties.LOGGING_LOGGER, "JavaLogger");
|
||||
// Ensure that we flush only at the end of the transaction
|
||||
properties.put(PersistenceUnitProperties.PERSISTENCE_CONTEXT_FLUSH_MODE, "COMMIT");
|
||||
// Enable batch writing
|
||||
properties.put(PersistenceUnitProperties.BATCH_WRITING, "JDBC");
|
||||
// Batch size
|
||||
properties.put(PersistenceUnitProperties.BATCH_WRITING_SIZE, "500");
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MultiTenantJpaTransactionManager} bean.
|
||||
*
|
||||
* @see org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration#transactionManager(ObjectProvider)
|
||||
* @return a new {@link PlatformTransactionManager}
|
||||
*/
|
||||
@Override
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager(
|
||||
final ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
|
||||
return new MultiTenantJpaTransactionManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link JpaSystemManagement} bean.
|
||||
*
|
||||
@@ -614,13 +644,13 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
TargetManagement targetManagement(final EntityManager entityManager, final QuotaManagement quotaManagement,
|
||||
final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository,
|
||||
final RolloutGroupRepository rolloutGroupRepository,
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository,
|
||||
final TargetTypeRepository targetTypeRepository, final TargetTagRepository targetTagRepository,
|
||||
final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||
final JpaProperties properties, final DistributionSetManagement distributionSetManagement) {
|
||||
final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository,
|
||||
final RolloutGroupRepository rolloutGroupRepository,
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository,
|
||||
final TargetTypeRepository targetTypeRepository, final TargetTagRepository targetTagRepository,
|
||||
final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||
final JpaProperties properties, final DistributionSetManagement distributionSetManagement) {
|
||||
return new JpaTargetManagement(entityManager, distributionSetManagement, quotaManagement, targetRepository,
|
||||
targetTypeRepository, targetMetadataRepository, rolloutGroupRepository, targetFilterQueryRepository,
|
||||
targetTagRepository, eventPublisherHolder, tenantAware, virtualPropertyReplacer,
|
||||
@@ -630,19 +660,12 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
/**
|
||||
* {@link JpaTargetFilterQueryManagement} bean.
|
||||
*
|
||||
* @param targetFilterQueryRepository
|
||||
* holding {@link TargetFilterQuery} entities
|
||||
* @param targetManagement
|
||||
* managing {@link Target} entities
|
||||
* @param virtualPropertyReplacer
|
||||
* for RSQL handling
|
||||
* @param distributionSetManagement
|
||||
* for auto assign DS access
|
||||
* @param quotaManagement
|
||||
* to access quotas
|
||||
* @param properties
|
||||
* JPA properties
|
||||
*
|
||||
* @param targetFilterQueryRepository holding {@link TargetFilterQuery} entities
|
||||
* @param targetManagement managing {@link Target} entities
|
||||
* @param virtualPropertyReplacer for RSQL handling
|
||||
* @param distributionSetManagement for auto assign DS access
|
||||
* @param quotaManagement to access quotas
|
||||
* @param properties JPA properties
|
||||
* @return a new {@link TargetFilterQueryManagement}
|
||||
*/
|
||||
@Bean
|
||||
@@ -659,7 +682,6 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
tenantConfigurationManagement, repositoryProperties, systemSecurityContext, contextAware, auditorAware);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link JpaTargetTagManagement} bean.
|
||||
*
|
||||
@@ -792,10 +814,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
RolloutGroupManagement rolloutGroupManagement(final RolloutGroupRepository rolloutGroupRepository,
|
||||
final RolloutRepository rolloutRepository, final ActionRepository actionRepository,
|
||||
final TargetRepository targetRepository, final EntityManager entityManager,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final RolloutStatusCache rolloutStatusCache,
|
||||
final JpaProperties properties) {
|
||||
final RolloutRepository rolloutRepository, final ActionRepository actionRepository,
|
||||
final TargetRepository targetRepository, final EntityManager entityManager,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final RolloutStatusCache rolloutStatusCache,
|
||||
final JpaProperties properties) {
|
||||
return new JpaRolloutGroupManagement(rolloutGroupRepository, rolloutRepository, actionRepository,
|
||||
targetRepository, entityManager, virtualPropertyReplacer, rolloutStatusCache, properties.getDatabase());
|
||||
}
|
||||
@@ -808,15 +830,16 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
DeploymentManagement deploymentManagement(final EntityManager entityManager,
|
||||
final ActionRepository actionRepository,
|
||||
final DistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
|
||||
final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider,
|
||||
final EventPublisherHolder eventPublisherHolder, final AfterTransactionCommitExecutor afterCommit,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final AuditorAware<String> auditorAware,
|
||||
final JpaProperties properties, final RepositoryProperties repositoryProperties) {
|
||||
return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetManagement, targetRepository, actionStatusRepository, auditorProvider,
|
||||
final ActionRepository actionRepository,
|
||||
final DistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
|
||||
final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider,
|
||||
final EventPublisherHolder eventPublisherHolder, final AfterTransactionCommitExecutor afterCommit,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final AuditorAware<String> auditorAware,
|
||||
final JpaProperties properties, final RepositoryProperties repositoryProperties) {
|
||||
return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetManagement, targetRepository, actionStatusRepository,
|
||||
auditorProvider,
|
||||
eventPublisherHolder, afterCommit, virtualPropertyReplacer, txManager, tenantConfigurationManagement,
|
||||
quotaManagement, systemSecurityContext, tenantAware, auditorAware, properties.getDatabase(), repositoryProperties);
|
||||
}
|
||||
@@ -880,10 +903,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
/**
|
||||
* {@link EventEntityManager} bean.
|
||||
*
|
||||
* @param aware
|
||||
* the tenant aware
|
||||
* @param entityManager
|
||||
* the entity manager
|
||||
* @param aware the tenant aware
|
||||
* @param entityManager the entity manager
|
||||
* @return a new {@link EventEntityManager}
|
||||
*/
|
||||
@Bean
|
||||
@@ -895,14 +916,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
/**
|
||||
* {@link AutoAssignChecker} bean.
|
||||
*
|
||||
* @param targetFilterQueryManagement
|
||||
* to get all target filter queries
|
||||
* @param targetManagement
|
||||
* to get targets
|
||||
* @param deploymentManagement
|
||||
* to assign distribution sets to targets
|
||||
* @param transactionManager
|
||||
* to run transactions
|
||||
* @param targetFilterQueryManagement to get all target filter queries
|
||||
* @param targetManagement to get targets
|
||||
* @param deploymentManagement to assign distribution sets to targets
|
||||
* @param transactionManager to run transactions
|
||||
* @return a new {@link AutoAssignChecker}
|
||||
*/
|
||||
@Bean
|
||||
@@ -920,14 +937,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* Note: does not activate in test profile, otherwise it is hard to test the
|
||||
* auto assign functionality.
|
||||
*
|
||||
* @param systemManagement
|
||||
* to find all tenants
|
||||
* @param systemSecurityContext
|
||||
* to run as system
|
||||
* @param autoAssignExecutor
|
||||
* to run a check as tenant
|
||||
* @param lockRegistry
|
||||
* to lock the tenant for auto assignment
|
||||
* @param systemManagement to find all tenants
|
||||
* @param systemSecurityContext to run as system
|
||||
* @param autoAssignExecutor to run a check as tenant
|
||||
* @param lockRegistry to lock the tenant for auto assignment
|
||||
* @return a new {@link AutoAssignChecker}
|
||||
*/
|
||||
@Bean
|
||||
@@ -945,11 +958,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
/**
|
||||
* {@link AutoActionCleanup} bean.
|
||||
*
|
||||
* @param deploymentManagement
|
||||
* Deployment management service
|
||||
* @param configManagement
|
||||
* Tenant configuration service
|
||||
*
|
||||
* @param deploymentManagement Deployment management service
|
||||
* @param configManagement Tenant configuration service
|
||||
* @return a new {@link AutoActionCleanup} bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -961,15 +971,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
/**
|
||||
* {@link AutoCleanupScheduler} bean.
|
||||
*
|
||||
* @param systemManagement
|
||||
* to find all tenants
|
||||
* @param systemSecurityContext
|
||||
* to run as system
|
||||
* @param lockRegistry
|
||||
* to lock the tenant for auto assignment
|
||||
* @param cleanupTasks
|
||||
* a list of cleanup tasks
|
||||
*
|
||||
* @param systemManagement to find all tenants
|
||||
* @param systemSecurityContext to run as system
|
||||
* @param lockRegistry to lock the tenant for auto assignment
|
||||
* @param cleanupTasks a list of cleanup tasks
|
||||
* @return a new {@link AutoCleanupScheduler} bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -988,12 +993,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* Note: does not activate in test profile, otherwise it is hard to test the
|
||||
* rollout handling functionality.
|
||||
*
|
||||
* @param systemManagement
|
||||
* to find all tenants
|
||||
* @param rolloutHandler
|
||||
* to run the rollout handler
|
||||
* @param systemSecurityContext
|
||||
* to run as system
|
||||
* @param systemManagement to find all tenants
|
||||
* @param rolloutHandler to run the rollout handler
|
||||
* @param systemSecurityContext to run as system
|
||||
* @return a new {@link RolloutScheduler} bean.
|
||||
*/
|
||||
@Bean
|
||||
@@ -1069,40 +1071,4 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
ArtifactEncryptionService artifactEncryptionService() {
|
||||
return ArtifactEncryptionService.getInstance();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor entityManagerBeanPostProcessor(
|
||||
@Autowired(required = false) final AccessController<JpaArtifact> artifactAccessController,
|
||||
@Autowired(required = false) final AccessController<JpaSoftwareModuleType> softwareModuleTypeAccessController,
|
||||
@Autowired(required = false) final AccessController<JpaSoftwareModule> softwareModuleAccessController,
|
||||
@Autowired(required = false) final AccessController<JpaDistributionSetType> distributionSetTypeAccessController,
|
||||
@Autowired(required = false) final AccessController<JpaDistributionSet> distributionSetAccessController,
|
||||
@Autowired(required = false) final AccessController<JpaTargetType> targetTypeAccessControlManager,
|
||||
@Autowired(required = false) final AccessController<JpaTarget> targetAccessControlManager,
|
||||
@Autowired(required = false) final AccessController<JpaAction> actionAccessController) {
|
||||
return new BeanPostProcessor() {
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof LocalArtifactRepository repo) {
|
||||
return repo.withACM(artifactAccessController);
|
||||
} else if (bean instanceof SoftwareModuleTypeRepository repo) {
|
||||
return repo.withACM(softwareModuleTypeAccessController);
|
||||
} else if (bean instanceof SoftwareModuleRepository repo) {
|
||||
return repo.withACM(softwareModuleAccessController);
|
||||
} else if (bean instanceof DistributionSetTypeRepository repo) {
|
||||
return repo.withACM(distributionSetTypeAccessController);
|
||||
} else if (bean instanceof DistributionSetRepository repo) {
|
||||
return repo.withACM(distributionSetAccessController);
|
||||
} else if (bean instanceof TargetTypeRepository repo) {
|
||||
return repo.withACM(targetTypeAccessControlManager);
|
||||
} else if (bean instanceof TargetRepository repo) {
|
||||
return repo.withACM(targetAccessControlManager);
|
||||
} else if (bean instanceof ActionRepository repo) {
|
||||
return repo.withACM(actionAccessController);
|
||||
}
|
||||
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,26 +26,10 @@ import org.springframework.context.annotation.Bean;
|
||||
*/
|
||||
public class SystemManagementCacheKeyGenerator implements CurrentTenantCacheKeyGenerator {
|
||||
|
||||
private final ThreadLocal<String> createInitialTenant = new ThreadLocal<>();
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
|
||||
private final ThreadLocal<String> createInitialTenant = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* An implementation of the {@link KeyGenerator} to generate a key based on
|
||||
* either the {@code createInitialTenant} thread local and the
|
||||
* {@link TenantAware}, but in case we are in a tenant creation with its default
|
||||
* types we need to use as the tenant the current tenant which is currently
|
||||
* created and not the one currently in the {@link TenantAware}.
|
||||
*/
|
||||
public class CurrentTenantKeyGenerator implements KeyGenerator {
|
||||
@Override
|
||||
public Object generate(final Object target, final Method method, final Object... params) {
|
||||
String tenant = getTenantInCreation().orElseGet(() -> tenantAware.getCurrentTenant()).toUpperCase();
|
||||
return SimpleKeyGenerator.generateKey(tenant, tenant);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public KeyGenerator currentTenantKeyGenerator() {
|
||||
@@ -55,7 +39,7 @@ public class SystemManagementCacheKeyGenerator implements CurrentTenantCacheKeyG
|
||||
/**
|
||||
* Get the tenant which overwrites the actual tenant used by the
|
||||
* {@linkplain #currentTenantKeyGenerator()}.
|
||||
*
|
||||
*
|
||||
* @return A present optional in case that there is a tenant in the progress of
|
||||
* creation.
|
||||
*/
|
||||
@@ -66,9 +50,8 @@ public class SystemManagementCacheKeyGenerator implements CurrentTenantCacheKeyG
|
||||
/**
|
||||
* Overwrite the tenant used by the key generator in case that the tenant is in
|
||||
* the process of creation.
|
||||
*
|
||||
* @param tenant
|
||||
* the tenant which should be used instead of the actual one.
|
||||
*
|
||||
* @param tenant the tenant which should be used instead of the actual one.
|
||||
*/
|
||||
public void setTenantInCreation(@NotNull String tenant) {
|
||||
createInitialTenant.set(Objects.requireNonNull(tenant));
|
||||
@@ -81,4 +64,20 @@ public class SystemManagementCacheKeyGenerator implements CurrentTenantCacheKeyG
|
||||
public void removeTenantInCreation() {
|
||||
createInitialTenant.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of the {@link KeyGenerator} to generate a key based on
|
||||
* either the {@code createInitialTenant} thread local and the
|
||||
* {@link TenantAware}, but in case we are in a tenant creation with its default
|
||||
* types we need to use as the tenant the current tenant which is currently
|
||||
* created and not the one currently in the {@link TenantAware}.
|
||||
*/
|
||||
public class CurrentTenantKeyGenerator implements KeyGenerator {
|
||||
|
||||
@Override
|
||||
public Object generate(final Object target, final Method method, final Object... params) {
|
||||
String tenant = getTenantInCreation().orElseGet(() -> tenantAware.getCurrentTenant()).toUpperCase();
|
||||
return SimpleKeyGenerator.generateKey(tenant, tenant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* {@link KeyGenerator} for tenant related caches.
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class TenantKeyGenerator implements KeyGenerator {
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.lang.Nullable;
|
||||
* additional restrictions (e.g. entity based) could be applied.
|
||||
* <p/>
|
||||
* <b>Note</b>: Experimental, only
|
||||
*
|
||||
*
|
||||
* @param <T> the domain type the repository manages
|
||||
*/
|
||||
public interface AccessController<T> {
|
||||
@@ -33,16 +33,15 @@ public interface AccessController<T> {
|
||||
* Introduce a new specification to limit the access to a specific entity.
|
||||
*
|
||||
* @return a new specification limiting the access, if empty no access restrictions
|
||||
* are to be applied
|
||||
* are to be applied
|
||||
*/
|
||||
Optional<Specification<T>> getAccessRules(Operation operation);
|
||||
|
||||
/**
|
||||
* Append the resource limitation on an already existing specification.
|
||||
*
|
||||
* @param specification
|
||||
* is the root specification which needs to be appended by the
|
||||
* resource limitation
|
||||
*
|
||||
* @param specification is the root specification which needs to be appended by the
|
||||
* resource limitation
|
||||
* @return a new appended specification
|
||||
*/
|
||||
@Nullable
|
||||
@@ -55,18 +54,17 @@ public interface AccessController<T> {
|
||||
/**
|
||||
* Verify if the given {@link Operation} is permitted for the provided entity.
|
||||
*
|
||||
* @throws InsufficientPermissionException
|
||||
* if operation is not permitted for given entities
|
||||
* @throws InsufficientPermissionException if operation is not permitted for given entities
|
||||
*/
|
||||
void assertOperationAllowed(final Operation operation, final T entity) throws InsufficientPermissionException;
|
||||
|
||||
/**
|
||||
* Verify if the given {@link Operation} is permitted for the provided entities.
|
||||
*
|
||||
* @throws InsufficientPermissionException
|
||||
* if operation is not permitted for given entities
|
||||
* @throws InsufficientPermissionException if operation is not permitted for given entities
|
||||
*/
|
||||
default void assertOperationAllowed(final Operation operation, final Iterable<? extends T> entities) throws InsufficientPermissionException {
|
||||
default void assertOperationAllowed(final Operation operation, final Iterable<? extends T> entities)
|
||||
throws InsufficientPermissionException {
|
||||
for (final T entity : entities) {
|
||||
assertOperationAllowed(operation, entity);
|
||||
}
|
||||
|
||||
@@ -68,8 +68,7 @@ public class ExceptionMappingAspectHandler implements Ordered {
|
||||
* catch exceptions of the {@link TransactionManager} and wrap them to
|
||||
* custom exceptions.
|
||||
*
|
||||
* @param ex
|
||||
* the thrown and catched exception
|
||||
* @param ex the thrown and catched exception
|
||||
* @throws Throwable
|
||||
*/
|
||||
@AfterThrowing(pointcut = "execution( * org.eclipse.hawkbit.repository.jpa.management.*Management.*(..))", throwing = "ex")
|
||||
@@ -103,6 +102,11 @@ public class ExceptionMappingAspectHandler implements Ordered {
|
||||
throw ex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static Exception replaceWithCauseIfConstraintViolationException(final TransactionSystemException rex) {
|
||||
Throwable exception = rex;
|
||||
do {
|
||||
@@ -115,9 +119,4 @@ public class ExceptionMappingAspectHandler implements Ordered {
|
||||
|
||||
return rex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,15 +56,11 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param targetFilterQueryManagement
|
||||
* to get all target filter queries
|
||||
* @param deploymentManagement
|
||||
* to assign distribution sets to targets
|
||||
* @param transactionManager
|
||||
* to run transactions
|
||||
* @param contextAware
|
||||
* to handle the context
|
||||
*
|
||||
* @param targetFilterQueryManagement to get all target filter queries
|
||||
* @param deploymentManagement to assign distribution sets to targets
|
||||
* @param transactionManager to run transactions
|
||||
* @param contextAware to handle the context
|
||||
*/
|
||||
protected AbstractAutoAssignExecutor(final TargetFilterQueryManagement targetFilterQueryManagement,
|
||||
final DeploymentManagement deploymentManagement, final PlatformTransactionManager transactionManager,
|
||||
@@ -75,6 +71,12 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
|
||||
this.contextAware = contextAware;
|
||||
}
|
||||
|
||||
protected static String getAutoAssignmentInitiatedBy(final TargetFilterQuery targetFilterQuery) {
|
||||
return StringUtils.hasText(targetFilterQuery.getAutoAssignInitiatedBy())
|
||||
? targetFilterQuery.getAutoAssignInitiatedBy()
|
||||
: targetFilterQuery.getCreatedBy();
|
||||
}
|
||||
|
||||
protected DeploymentManagement getDeploymentManagement() {
|
||||
return deploymentManagement;
|
||||
}
|
||||
@@ -100,17 +102,17 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
|
||||
filterQueries.forEach(filterQuery -> {
|
||||
try {
|
||||
filterQuery.getAccessControlContext().ifPresentOrElse(
|
||||
context -> // has stored context - executes it with it
|
||||
contextAware.runInContext(
|
||||
context,
|
||||
() -> consumer.accept(filterQuery)),
|
||||
() -> // has no stored context - executes it in the tenant & user scope
|
||||
contextAware.runAsTenantAsUser(
|
||||
contextAware.getCurrentTenant(),
|
||||
getAutoAssignmentInitiatedBy(filterQuery), () -> {
|
||||
consumer.accept(filterQuery);
|
||||
return null;
|
||||
})
|
||||
context -> // has stored context - executes it with it
|
||||
contextAware.runInContext(
|
||||
context,
|
||||
() -> consumer.accept(filterQuery)),
|
||||
() -> // has no stored context - executes it in the tenant & user scope
|
||||
contextAware.runAsTenantAsUser(
|
||||
contextAware.getCurrentTenant(),
|
||||
getAutoAssignmentInitiatedBy(filterQuery), () -> {
|
||||
consumer.accept(filterQuery);
|
||||
return null;
|
||||
})
|
||||
);
|
||||
} catch (final RuntimeException ex) {
|
||||
log.debug(
|
||||
@@ -128,11 +130,9 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
|
||||
/**
|
||||
* Runs target assignments within a dedicated transaction for a given list of
|
||||
* controllerIDs
|
||||
*
|
||||
* @param targetFilterQuery
|
||||
* the target filter query
|
||||
* @param controllerIds
|
||||
* the controllerIDs
|
||||
*
|
||||
* @param targetFilterQuery the target filter query
|
||||
* @param controllerIds the controllerIDs
|
||||
* @return count of targets
|
||||
*/
|
||||
protected int runTransactionalAssignment(final TargetFilterQuery targetFilterQuery,
|
||||
@@ -158,10 +158,8 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
|
||||
* Creates a list of {@link DeploymentRequest} for given list of controllerIds
|
||||
* and {@link TargetFilterQuery}
|
||||
*
|
||||
* @param controllerIds
|
||||
* list of controllerIds
|
||||
* @param filterQuery
|
||||
* the query the targets have to match
|
||||
* @param controllerIds list of controllerIds
|
||||
* @param filterQuery the query the targets have to match
|
||||
* @return list of deployment request
|
||||
*/
|
||||
protected List<DeploymentRequest> mapToDeploymentRequests(final List<String> controllerIds,
|
||||
@@ -179,10 +177,4 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
|
||||
.setConfirmationRequired(filterQuery.isConfirmationRequired()).build())
|
||||
.toList();
|
||||
}
|
||||
|
||||
protected static String getAutoAssignmentInitiatedBy(final TargetFilterQuery targetFilterQuery) {
|
||||
return StringUtils.hasText(targetFilterQuery.getAutoAssignInitiatedBy())
|
||||
? targetFilterQuery.getAutoAssignInitiatedBy()
|
||||
: targetFilterQuery.getCreatedBy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,16 +43,11 @@ public class AutoAssignChecker extends AbstractAutoAssignExecutor {
|
||||
/**
|
||||
* Instantiates a new auto assign checker
|
||||
*
|
||||
* @param targetFilterQueryManagement
|
||||
* to get all target filter queries
|
||||
* @param targetManagement
|
||||
* to get targets
|
||||
* @param deploymentManagement
|
||||
* to assign distribution sets to targets
|
||||
* @param transactionManager
|
||||
* to run transactions
|
||||
* @param contextAware
|
||||
* to handle the context
|
||||
* @param targetFilterQueryManagement to get all target filter queries
|
||||
* @param targetManagement to get targets
|
||||
* @param deploymentManagement to assign distribution sets to targets
|
||||
* @param transactionManager to run transactions
|
||||
* @param contextAware to handle the context
|
||||
*/
|
||||
public AutoAssignChecker(final TargetFilterQueryManagement targetFilterQueryManagement,
|
||||
final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
|
||||
@@ -83,8 +78,7 @@ public class AutoAssignChecker extends AbstractAutoAssignExecutor {
|
||||
* them. Catches PersistenceException and own exceptions derived from
|
||||
* AbstractServerRtException
|
||||
*
|
||||
* @param targetFilterQuery
|
||||
* the target filter query
|
||||
* @param targetFilterQuery the target filter query
|
||||
*/
|
||||
private void checkByTargetFilterQueryAndAssignDS(final TargetFilterQuery targetFilterQuery) {
|
||||
log.debug("Auto assign check call for tenant {} and target filter query id {} started",
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.springframework.scheduling.annotation.Scheduled;
|
||||
*/
|
||||
@Slf4j
|
||||
public class AutoAssignScheduler {
|
||||
|
||||
|
||||
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.autoassign.scheduler.fixedDelay:2000}";
|
||||
|
||||
private final SystemManagement systemManagement;
|
||||
@@ -36,15 +36,11 @@ public class AutoAssignScheduler {
|
||||
|
||||
/**
|
||||
* Instantiates a new AutoAssignScheduler
|
||||
*
|
||||
* @param systemManagement
|
||||
* to find all tenants
|
||||
* @param systemSecurityContext
|
||||
* to run as system
|
||||
* @param autoAssignExecutor
|
||||
* to run a check as tenant
|
||||
* @param lockRegistry
|
||||
* to acquire a lock per tenant
|
||||
*
|
||||
* @param systemManagement to find all tenants
|
||||
* @param systemSecurityContext to run as system
|
||||
* @param autoAssignExecutor to run a check as tenant
|
||||
* @param lockRegistry to acquire a lock per tenant
|
||||
*/
|
||||
public AutoAssignScheduler(final SystemManagement systemManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final AutoAssignExecutor autoAssignExecutor,
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
|
||||
* actions which are in a certain {@link Action.Status}. It is recommended to
|
||||
* only clean up actions which have terminated already (i.e. actions in status
|
||||
* CANCELLED or ERROR).
|
||||
*
|
||||
*
|
||||
* The cleanup task can be enabled /disabled and configured on a per tenant
|
||||
* basis.
|
||||
*/
|
||||
@@ -49,11 +49,9 @@ public class AutoActionCleanup implements CleanupTask {
|
||||
|
||||
/**
|
||||
* Constructs the action cleanup handler.
|
||||
*
|
||||
* @param deploymentMgmt
|
||||
* The {@link DeploymentManagement} to operate on.
|
||||
* @param configMgmt
|
||||
* The {@link TenantConfigurationManagement} service.
|
||||
*
|
||||
* @param deploymentMgmt The {@link DeploymentManagement} to operate on.
|
||||
* @param configMgmt The {@link TenantConfigurationManagement} service.
|
||||
*/
|
||||
public AutoActionCleanup(final DeploymentManagement deploymentMgmt,
|
||||
final TenantConfigurationManagement configMgmt) {
|
||||
|
||||
@@ -36,15 +36,11 @@ public class AutoCleanupScheduler {
|
||||
/**
|
||||
* Constructs the cleanup schedulers and initializes it with a set of
|
||||
* cleanup handlers.
|
||||
*
|
||||
* @param systemManagement
|
||||
* Management APIs to invoke actions in a certain tenant context.
|
||||
* @param systemSecurityContext
|
||||
* The system security context.
|
||||
* @param lockRegistry
|
||||
* A registry for shared locks.
|
||||
* @param cleanupTasks
|
||||
* A list of cleanup tasks.
|
||||
*
|
||||
* @param systemManagement Management APIs to invoke actions in a certain tenant context.
|
||||
* @param systemSecurityContext The system security context.
|
||||
* @param lockRegistry A registry for shared locks.
|
||||
* @param cleanupTasks A list of cleanup tasks.
|
||||
*/
|
||||
public AutoCleanupScheduler(final SystemManagement systemManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final LockRegistry lockRegistry,
|
||||
|
||||
@@ -15,7 +15,6 @@ import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link ActionStatus}.
|
||||
*
|
||||
*/
|
||||
public class JpaActionStatusBuilder implements ActionStatusBuilder {
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*
|
||||
*/
|
||||
public class JpaActionStatusCreate extends AbstractActionStatusCreate<ActionStatusCreate>
|
||||
implements ActionStatusCreate {
|
||||
|
||||
@@ -19,7 +19,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link DistributionSet}.
|
||||
*
|
||||
*/
|
||||
public class JpaDistributionSetBuilder implements DistributionSetBuilder {
|
||||
|
||||
|
||||
@@ -27,16 +27,14 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*
|
||||
*/
|
||||
public class JpaDistributionSetCreate extends AbstractDistributionSetUpdateCreate<DistributionSetCreate>
|
||||
implements DistributionSetCreate {
|
||||
|
||||
@ValidString
|
||||
private String type;
|
||||
|
||||
private final DistributionSetTypeManagement distributionSetTypeManagement;
|
||||
private final SoftwareModuleManagement softwareModuleManagement;
|
||||
@ValidString
|
||||
private String type;
|
||||
|
||||
JpaDistributionSetCreate(final DistributionSetTypeManagement distributionSetTypeManagement,
|
||||
final SoftwareModuleManagement softwareManagement) {
|
||||
@@ -44,6 +42,12 @@ public class JpaDistributionSetCreate extends AbstractDistributionSetUpdateCreat
|
||||
this.softwareModuleManagement = softwareManagement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetCreate type(final String type) {
|
||||
this.type = StringUtils.trimWhitespace(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaDistributionSet build() {
|
||||
return new JpaDistributionSet(name, version, description,
|
||||
@@ -52,12 +56,6 @@ public class JpaDistributionSetCreate extends AbstractDistributionSetUpdateCreat
|
||||
Optional.ofNullable(requiredMigrationStep).orElse(Boolean.FALSE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetCreate type(final String type) {
|
||||
this.type = StringUtils.trimWhitespace(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link DistributionSetType}.
|
||||
*
|
||||
*/
|
||||
public class JpaDistributionSetTypeBuilder implements DistributionSetTypeBuilder {
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*
|
||||
*/
|
||||
public class JpaDistributionSetTypeCreate extends AbstractDistributionSetTypeUpdateCreate<DistributionSetTypeCreate>
|
||||
implements DistributionSetTypeCreate {
|
||||
|
||||
@@ -18,9 +18,9 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link Rollout}.
|
||||
*
|
||||
*/
|
||||
public class JpaRolloutBuilder implements RolloutBuilder {
|
||||
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
|
||||
public JpaRolloutBuilder(final DistributionSetManagement distributionSetManagement) {
|
||||
|
||||
@@ -9,8 +9,11 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.builder;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.ValidString;
|
||||
import org.eclipse.hawkbit.repository.builder.AbstractNamedEntityBuilder;
|
||||
@@ -20,12 +23,8 @@ import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class JpaRolloutCreate extends AbstractNamedEntityBuilder<RolloutCreate> implements RolloutCreate {
|
||||
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
|
||||
protected Long distributionSetId;
|
||||
@ValidString
|
||||
protected String targetFilterQuery;
|
||||
@@ -35,12 +34,13 @@ public class JpaRolloutCreate extends AbstractNamedEntityBuilder<RolloutCreate>
|
||||
@Min(Action.WEIGHT_MIN)
|
||||
@Max(Action.WEIGHT_MAX)
|
||||
protected Integer weight;
|
||||
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
private boolean dynamic;
|
||||
|
||||
JpaRolloutCreate(final DistributionSetManagement distributionSetManagement) {
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link DistributionSet} of rollout
|
||||
*
|
||||
@@ -62,6 +62,7 @@ public class JpaRolloutCreate extends AbstractNamedEntityBuilder<RolloutCreate>
|
||||
this.targetFilterQuery = StringUtils.trimWhitespace(targetFilterQuery);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Action.ActionType} used for {@link Action}s
|
||||
*
|
||||
@@ -95,6 +96,11 @@ public class JpaRolloutCreate extends AbstractNamedEntityBuilder<RolloutCreate>
|
||||
return this;
|
||||
}
|
||||
|
||||
public RolloutCreate dynamic(final boolean dynamic) {
|
||||
this.dynamic = dynamic;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set start of the Rollout
|
||||
*
|
||||
@@ -106,31 +112,6 @@ public class JpaRolloutCreate extends AbstractNamedEntityBuilder<RolloutCreate>
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<Long> getDistributionSetId() {
|
||||
return Optional.ofNullable(distributionSetId);
|
||||
}
|
||||
|
||||
public Optional<Action.ActionType> getActionType() {
|
||||
return Optional.ofNullable(actionType);
|
||||
}
|
||||
|
||||
public Optional<Long> getForcedTime() {
|
||||
return Optional.ofNullable(forcedTime);
|
||||
}
|
||||
|
||||
public Optional<Integer> getWeight() {
|
||||
return Optional.ofNullable(weight);
|
||||
}
|
||||
|
||||
public Optional<Long> getStartAt() {
|
||||
return Optional.ofNullable(startAt);
|
||||
}
|
||||
|
||||
public RolloutCreate dynamic(final boolean dynamic) {
|
||||
this.dynamic = dynamic;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaRollout build() {
|
||||
final JpaRollout rollout = new JpaRollout();
|
||||
@@ -153,4 +134,24 @@ public class JpaRolloutCreate extends AbstractNamedEntityBuilder<RolloutCreate>
|
||||
|
||||
return rollout;
|
||||
}
|
||||
|
||||
public Optional<Long> getDistributionSetId() {
|
||||
return Optional.ofNullable(distributionSetId);
|
||||
}
|
||||
|
||||
public Optional<Action.ActionType> getActionType() {
|
||||
return Optional.ofNullable(actionType);
|
||||
}
|
||||
|
||||
public Optional<Long> getForcedTime() {
|
||||
return Optional.ofNullable(forcedTime);
|
||||
}
|
||||
|
||||
public Optional<Integer> getWeight() {
|
||||
return Optional.ofNullable(weight);
|
||||
}
|
||||
|
||||
public Optional<Long> getStartAt() {
|
||||
return Optional.ofNullable(startAt);
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,9 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link RolloutGroup}.
|
||||
*
|
||||
*/
|
||||
public class JpaRolloutGroupBuilder implements RolloutGroupBuilder {
|
||||
|
||||
@Override
|
||||
public RolloutGroupCreate create() {
|
||||
return new JpaRolloutGroupCreate();
|
||||
|
||||
@@ -18,6 +18,51 @@ import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
|
||||
public class JpaRolloutGroupCreate extends AbstractRolloutGroupCreate<RolloutGroupCreate>
|
||||
implements RolloutGroupCreate {
|
||||
|
||||
/**
|
||||
* Set the Success And Error conditions for the rollout group
|
||||
*
|
||||
* @param group The Rollout group
|
||||
* @param conditions The Rollout Success and Error Conditions
|
||||
*/
|
||||
public static void addSuccessAndErrorConditionsAndActions(final JpaRolloutGroup group,
|
||||
final RolloutGroupConditions conditions) {
|
||||
addSuccessAndErrorConditionsAndActions(group, conditions.getSuccessCondition(),
|
||||
conditions.getSuccessConditionExp(), conditions.getSuccessAction(), conditions.getSuccessActionExp(),
|
||||
conditions.getErrorCondition(), conditions.getErrorConditionExp(), conditions.getErrorAction(),
|
||||
conditions.getErrorActionExp());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Success And Error conditions for the rollout group
|
||||
*
|
||||
* @param group The Rollout group
|
||||
* @param successCondition The Rollout group success condition
|
||||
* @param successConditionExp The Rollout group success expression
|
||||
* @param successAction The Rollout group success action
|
||||
* @param successActionExp The Rollout group success action expression
|
||||
* @param errorCondition The Rollout group error condition
|
||||
* @param errorConditionExp The Rollout group error expression
|
||||
* @param errorAction The Rollout group error action
|
||||
* @param errorActionExp The Rollout group error action expression
|
||||
*/
|
||||
public static void addSuccessAndErrorConditionsAndActions(final JpaRolloutGroup group,
|
||||
final RolloutGroup.RolloutGroupSuccessCondition successCondition, final String successConditionExp,
|
||||
final RolloutGroup.RolloutGroupSuccessAction successAction, final String successActionExp,
|
||||
final RolloutGroup.RolloutGroupErrorCondition errorCondition, final String errorConditionExp,
|
||||
final RolloutGroup.RolloutGroupErrorAction errorAction, final String errorActionExp) {
|
||||
group.setSuccessCondition(successCondition);
|
||||
group.setSuccessConditionExp(successConditionExp);
|
||||
|
||||
group.setSuccessAction(successAction);
|
||||
group.setSuccessActionExp(successActionExp);
|
||||
|
||||
group.setErrorCondition(errorCondition);
|
||||
group.setErrorConditionExp(errorConditionExp);
|
||||
|
||||
group.setErrorAction(errorAction);
|
||||
group.setErrorActionExp(errorActionExp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaRolloutGroup build() {
|
||||
final JpaRolloutGroup group = new JpaRolloutGroup();
|
||||
@@ -40,60 +85,4 @@ public class JpaRolloutGroupCreate extends AbstractRolloutGroupCreate<RolloutGro
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Success And Error conditions for the rollout group
|
||||
*
|
||||
* @param group
|
||||
* The Rollout group
|
||||
* @param conditions
|
||||
* The Rollout Success and Error Conditions
|
||||
*/
|
||||
public static void addSuccessAndErrorConditionsAndActions(final JpaRolloutGroup group,
|
||||
final RolloutGroupConditions conditions) {
|
||||
addSuccessAndErrorConditionsAndActions(group, conditions.getSuccessCondition(),
|
||||
conditions.getSuccessConditionExp(), conditions.getSuccessAction(), conditions.getSuccessActionExp(),
|
||||
conditions.getErrorCondition(), conditions.getErrorConditionExp(), conditions.getErrorAction(),
|
||||
conditions.getErrorActionExp());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Success And Error conditions for the rollout group
|
||||
*
|
||||
* @param group
|
||||
* The Rollout group
|
||||
* @param successCondition
|
||||
* The Rollout group success condition
|
||||
* @param successConditionExp
|
||||
* The Rollout group success expression
|
||||
* @param successAction
|
||||
* The Rollout group success action
|
||||
* @param successActionExp
|
||||
* The Rollout group success action expression
|
||||
* @param errorCondition
|
||||
* The Rollout group error condition
|
||||
* @param errorConditionExp
|
||||
* The Rollout group error expression
|
||||
* @param errorAction
|
||||
* The Rollout group error action
|
||||
* @param errorActionExp
|
||||
* The Rollout group error action expression
|
||||
*/
|
||||
public static void addSuccessAndErrorConditionsAndActions(final JpaRolloutGroup group,
|
||||
final RolloutGroup.RolloutGroupSuccessCondition successCondition, final String successConditionExp,
|
||||
final RolloutGroup.RolloutGroupSuccessAction successAction, final String successActionExp,
|
||||
final RolloutGroup.RolloutGroupErrorCondition errorCondition, final String errorConditionExp,
|
||||
final RolloutGroup.RolloutGroupErrorAction errorAction, final String errorActionExp) {
|
||||
group.setSuccessCondition(successCondition);
|
||||
group.setSuccessConditionExp(successConditionExp);
|
||||
|
||||
group.setSuccessAction(successAction);
|
||||
group.setSuccessActionExp(successActionExp);
|
||||
|
||||
group.setErrorCondition(errorCondition);
|
||||
group.setErrorConditionExp(errorConditionExp);
|
||||
|
||||
group.setErrorAction(errorAction);
|
||||
group.setErrorActionExp(errorActionExp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link SoftwareModule}.
|
||||
*
|
||||
*/
|
||||
public class JpaSoftwareModuleBuilder implements SoftwareModuleBuilder {
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*
|
||||
*/
|
||||
public class JpaSoftwareModuleCreate extends AbstractSoftwareModuleUpdateCreate<SoftwareModuleCreate>
|
||||
implements SoftwareModuleCreate {
|
||||
@@ -39,16 +38,16 @@ public class JpaSoftwareModuleCreate extends AbstractSoftwareModuleUpdateCreate<
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaSoftwareModule build() {
|
||||
return new JpaSoftwareModule(getSoftwareModuleTypeFromKeyString(type), name, version, description, vendor,
|
||||
encrypted);
|
||||
}
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
private SoftwareModuleType getSoftwareModuleTypeFromKeyString(final String type) {
|
||||
if (type == null) {
|
||||
throw new ValidationException("type cannot be null");
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link SoftwareModuleMetadata}.
|
||||
*
|
||||
*/
|
||||
public class JpaSoftwareModuleMetadataBuilder implements SoftwareModuleMetadataBuilder {
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*
|
||||
*/
|
||||
public class JpaSoftwareModuleMetadataCreate
|
||||
extends AbstractSoftwareModuleMetadataUpdateCreate<SoftwareModuleMetadataCreate>
|
||||
|
||||
@@ -17,7 +17,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link SoftwareModuleType}.
|
||||
*
|
||||
*/
|
||||
public class JpaSoftwareModuleTypeBuilder implements SoftwareModuleTypeBuilder {
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*
|
||||
*/
|
||||
public class JpaSoftwareModuleTypeCreate extends AbstractSoftwareModuleTypeUpdateCreate<SoftwareModuleTypeCreate>
|
||||
implements SoftwareModuleTypeCreate {
|
||||
|
||||
@@ -17,7 +17,6 @@ import org.eclipse.hawkbit.repository.model.Tag;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link Tag}.
|
||||
*
|
||||
*/
|
||||
public class JpaTagBuilder implements TagBuilder {
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ import org.eclipse.hawkbit.repository.model.Tag;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*
|
||||
*/
|
||||
public class JpaTagCreate extends AbstractTagUpdateCreate<TagCreate> implements TagCreate {
|
||||
|
||||
JpaTagCreate() {
|
||||
|
||||
}
|
||||
|
||||
@@ -17,14 +17,13 @@ import org.eclipse.hawkbit.repository.model.Target;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link Target}.
|
||||
*
|
||||
*/
|
||||
public class JpaTargetBuilder implements TargetBuilder {
|
||||
|
||||
final private TargetTypeManagement targetTypeManagement;
|
||||
|
||||
/**
|
||||
* @param targetTypeManagement
|
||||
* Target type management
|
||||
* @param targetTypeManagement Target type management
|
||||
*/
|
||||
public JpaTargetBuilder(TargetTypeManagement targetTypeManagement) {
|
||||
this.targetTypeManagement = targetTypeManagement;
|
||||
|
||||
@@ -20,7 +20,6 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*
|
||||
*/
|
||||
public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> implements TargetCreate {
|
||||
|
||||
@@ -29,8 +28,7 @@ public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> im
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param targetTypeManagement
|
||||
* Target type management
|
||||
* @param targetTypeManagement Target type management
|
||||
*/
|
||||
JpaTargetCreate(final TargetTypeManagement targetTypeManagement) {
|
||||
super(null);
|
||||
@@ -51,7 +49,7 @@ public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> im
|
||||
target.setName(name);
|
||||
}
|
||||
|
||||
if (targetTypeId != null){
|
||||
if (targetTypeId != null) {
|
||||
TargetType targetType = targetTypeManagement.get(targetTypeId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, targetTypeId));
|
||||
target.setTargetType(targetType);
|
||||
|
||||
@@ -19,9 +19,9 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link TargetFilterQuery}.
|
||||
*
|
||||
*/
|
||||
public class JpaTargetFilterQueryBuilder implements TargetFilterQueryBuilder {
|
||||
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
|
||||
public JpaTargetFilterQueryBuilder(final DistributionSetManagement distributionSetManagement) {
|
||||
@@ -33,14 +33,14 @@ public class JpaTargetFilterQueryBuilder implements TargetFilterQueryBuilder {
|
||||
return new GenericTargetFilterQueryUpdate(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetFilterQueryCreate create() {
|
||||
return new JpaTargetFilterQueryCreate(distributionSetManagement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoAssignDistributionSetUpdate updateAutoAssign(final long id) {
|
||||
return new AutoAssignDistributionSetUpdate(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetFilterQueryCreate create() {
|
||||
return new JpaTargetFilterQueryCreate(distributionSetManagement);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*
|
||||
*/
|
||||
public class JpaTargetFilterQueryCreate extends AbstractTargetFilterQueryUpdateCreate<TargetFilterQueryCreate>
|
||||
implements TargetFilterQueryCreate {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.builder;
|
||||
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetTypeManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.GenericTargetTypeUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
|
||||
@@ -19,16 +18,15 @@ import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link TargetType}.
|
||||
*
|
||||
*/
|
||||
public class JpaTargetTypeBuilder implements TargetTypeBuilder {
|
||||
|
||||
private final DistributionSetTypeManagement distributionSetTypeManagement;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param distributionSetTypeManagement
|
||||
* Distribution set type management
|
||||
* @param distributionSetTypeManagement Distribution set type management
|
||||
*/
|
||||
public JpaTargetTypeBuilder(DistributionSetTypeManagement distributionSetTypeManagement) {
|
||||
this.distributionSetTypeManagement = distributionSetTypeManagement;
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.builder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.AbstractTargetTypeUpdateCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
|
||||
@@ -19,12 +22,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*
|
||||
*/
|
||||
public class JpaTargetTypeCreate extends AbstractTargetTypeUpdateCreate<TargetTypeCreate>
|
||||
implements TargetTypeCreate {
|
||||
@@ -34,8 +33,7 @@ public class JpaTargetTypeCreate extends AbstractTargetTypeUpdateCreate<TargetTy
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param distributionSetTypeManagement
|
||||
* Distribution set type management
|
||||
* @param distributionSetTypeManagement Distribution set type management
|
||||
*/
|
||||
JpaTargetTypeCreate(final DistributionSetTypeManagement distributionSetTypeManagement) {
|
||||
this.distributionSetTypeManagement = distributionSetTypeManagement;
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
* {@link JpaTransactionManager} that sets the
|
||||
* {@link TenantAware#getCurrentTenant()} in the eclipselink session. This has
|
||||
* to be done in eclipselink after a {@link Transaction} has been started.
|
||||
*
|
||||
*/
|
||||
public class MultiTenantJpaTransactionManager extends JpaTransactionManager {
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
@Slf4j
|
||||
public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSynchronizationAdapter
|
||||
implements AfterTransactionCommitExecutor {
|
||||
|
||||
|
||||
private static final ThreadLocal<List<Runnable>> THREAD_LOCAL_RUNNABLES = new ThreadLocal<>();
|
||||
|
||||
@Override
|
||||
@@ -42,6 +42,14 @@ public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSyn
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "squid:S1217" })
|
||||
public void afterCompletion(final int status) {
|
||||
final String transactionStatus = status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK";
|
||||
log.debug("Transaction completed after commit with status {}", transactionStatus);
|
||||
THREAD_LOCAL_RUNNABLES.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
// Exception squid:S1217 - we want to run this synchronous
|
||||
@SuppressWarnings("squid:S1217")
|
||||
@@ -62,12 +70,4 @@ public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSyn
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "squid:S1217" })
|
||||
public void afterCompletion(final int status) {
|
||||
final String transactionStatus = status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK";
|
||||
log.debug("Transaction completed after commit with status {}", transactionStatus);
|
||||
THREAD_LOCAL_RUNNABLES.remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,10 +10,8 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.executor;
|
||||
|
||||
/**
|
||||
*
|
||||
* A interface to register a runnable, which will be executed after a successful
|
||||
* spring transaction.
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AfterTransactionCommitExecutor {
|
||||
@@ -21,9 +19,8 @@ public interface AfterTransactionCommitExecutor {
|
||||
/**
|
||||
* Register a runnable which will be executed after a successful spring
|
||||
* transaction.
|
||||
*
|
||||
* @param runnable
|
||||
* the after commit runnable
|
||||
*
|
||||
* @param runnable the after commit runnable
|
||||
*/
|
||||
void afterCommit(Runnable runnable);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import java.util.Set;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
@@ -48,8 +50,6 @@ import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
|
||||
/**
|
||||
* {@link DistributionSet} to {@link Target} assignment strategy as utility for
|
||||
* {@link JpaDeploymentManagement}.
|
||||
@@ -68,10 +68,10 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
private final RepositoryProperties repositoryProperties;
|
||||
|
||||
AbstractDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
||||
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties) {
|
||||
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
||||
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties) {
|
||||
this.targetRepository = targetRepository;
|
||||
this.afterCommit = afterCommit;
|
||||
this.eventPublisherHolder = eventPublisherHolder;
|
||||
@@ -83,63 +83,47 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
this.repositoryProperties = repositoryProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find targets to be considered for assignment.
|
||||
*
|
||||
* @param controllerIDs
|
||||
* as provided by repository caller
|
||||
* @param distributionSetId
|
||||
* to assign
|
||||
* @return list of targets up to {@link Constants#MAX_ENTRIES_IN_STATEMENT}
|
||||
*/
|
||||
abstract List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long distributionSetId);
|
||||
public JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType,
|
||||
final List<JpaTarget> targets, final JpaDistributionSet set) {
|
||||
final Optional<JpaTarget> optTarget = targets.stream()
|
||||
.filter(t -> t.getControllerId().equals(targetWithActionType.getControllerId())).findFirst();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param set
|
||||
* @param targets
|
||||
*/
|
||||
abstract void sendTargetUpdatedEvents(final DistributionSet set, final List<JpaTarget> targets);
|
||||
// create the action
|
||||
return optTarget.map(target -> {
|
||||
assertActionsPerTargetQuota(target, 1);
|
||||
final JpaAction actionForTarget = new JpaAction();
|
||||
actionForTarget.setActionType(targetWithActionType.getActionType());
|
||||
actionForTarget.setForcedTime(targetWithActionType.getForceTime());
|
||||
actionForTarget.setWeight(
|
||||
targetWithActionType.getWeight() == null ?
|
||||
repositoryProperties.getActionWeightIfAbsent() : targetWithActionType.getWeight());
|
||||
actionForTarget.setActive(true);
|
||||
actionForTarget.setTarget(target);
|
||||
actionForTarget.setDistributionSet(set);
|
||||
actionForTarget.setMaintenanceWindowSchedule(targetWithActionType.getMaintenanceSchedule());
|
||||
actionForTarget.setMaintenanceWindowDuration(targetWithActionType.getMaintenanceWindowDuration());
|
||||
actionForTarget.setMaintenanceWindowTimeZone(targetWithActionType.getMaintenanceWindowTimeZone());
|
||||
actionForTarget.setInitiatedBy(initiatedBy);
|
||||
return actionForTarget;
|
||||
}).orElseGet(() -> {
|
||||
log.warn("Cannot find target for targetWithActionType '{}'.", targetWithActionType.getControllerId());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update status and DS fields of given target.
|
||||
*
|
||||
* @param distributionSet
|
||||
* to set
|
||||
* @param targetIds
|
||||
* to change
|
||||
* @param currentUser
|
||||
* for auditing
|
||||
*/
|
||||
abstract void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet distributionSet,
|
||||
final List<List<Long>> targetIds, final String currentUser);
|
||||
public JpaActionStatus createActionStatus(final JpaAction action, final String actionMessage) {
|
||||
final JpaActionStatus actionStatus = new JpaActionStatus();
|
||||
actionStatus.setAction(action);
|
||||
actionStatus.setOccurredAt(action.getCreatedAt());
|
||||
|
||||
/**
|
||||
* Cancels actions that can be canceled (i.e.
|
||||
* {@link DistributionSet#isRequiredMigrationStep() is <code>false</code>})
|
||||
* as a result of the new assignment and returns all {@link Target}s where
|
||||
* such actions existed.
|
||||
*
|
||||
* @param targetIds
|
||||
* to cancel actions for
|
||||
* @return {@link Set} of {@link Target#getId()}s
|
||||
*/
|
||||
abstract Set<Long> cancelActiveActions(List<List<Long>> targetIds);
|
||||
if (StringUtils.hasText(actionMessage)) {
|
||||
actionStatus.addMessage(actionMessage);
|
||||
} else {
|
||||
actionStatus.addMessage(getActionMessage(action));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels actions that can be canceled (i.e.
|
||||
* {@link DistributionSet#isRequiredMigrationStep() is <code>false</code>})
|
||||
* as a result of the new assignment and returns all {@link Target}s where
|
||||
* such actions existed.
|
||||
*
|
||||
* @param targetIds
|
||||
* to cancel actions for
|
||||
*/
|
||||
abstract void closeActiveActions(List<List<Long>> targetIds);
|
||||
|
||||
abstract void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult);
|
||||
|
||||
abstract void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults);
|
||||
return actionStatus;
|
||||
}
|
||||
|
||||
protected void sendTargetUpdatedEvent(final JpaTarget target) {
|
||||
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher()
|
||||
@@ -172,7 +156,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "cancel obsolete action due to new update"));
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "cancel obsolete action due to new update"));
|
||||
actionRepository.save(action);
|
||||
|
||||
return action.getTarget().getId();
|
||||
@@ -223,65 +207,70 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
* Sends the {@link CancelTargetAssignmentEvent} for a specific action to
|
||||
* the eventPublisher.
|
||||
*
|
||||
* @param action
|
||||
* the action of the assignment
|
||||
* @param action the action of the assignment
|
||||
*/
|
||||
protected void cancelAssignDistributionSetEvent(final Action action) {
|
||||
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher().publishEvent(
|
||||
new CancelTargetAssignmentEvent(action, eventPublisherHolder.getApplicationId())));
|
||||
new CancelTargetAssignmentEvent(action, eventPublisherHolder.getApplicationId())));
|
||||
}
|
||||
|
||||
private void cancelAssignDistributionSetEvent(final List<Action> actions) {
|
||||
if (CollectionUtils.isEmpty(actions)) {
|
||||
return;
|
||||
}
|
||||
final String tenant = actions.get(0).getTenant();
|
||||
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher()
|
||||
.publishEvent(new CancelTargetAssignmentEvent(tenant,
|
||||
actions, eventPublisherHolder.getApplicationId())));
|
||||
protected boolean isMultiAssignmentsEnabled() {
|
||||
return multiAssignmentsConfig.getAsBoolean();
|
||||
}
|
||||
|
||||
public JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType,
|
||||
final List<JpaTarget> targets, final JpaDistributionSet set) {
|
||||
final Optional<JpaTarget> optTarget = targets.stream()
|
||||
.filter(t -> t.getControllerId().equals(targetWithActionType.getControllerId())).findFirst();
|
||||
|
||||
// create the action
|
||||
return optTarget.map(target -> {
|
||||
assertActionsPerTargetQuota(target, 1);
|
||||
final JpaAction actionForTarget = new JpaAction();
|
||||
actionForTarget.setActionType(targetWithActionType.getActionType());
|
||||
actionForTarget.setForcedTime(targetWithActionType.getForceTime());
|
||||
actionForTarget.setWeight(
|
||||
targetWithActionType.getWeight() == null ?
|
||||
repositoryProperties.getActionWeightIfAbsent() : targetWithActionType.getWeight());
|
||||
actionForTarget.setActive(true);
|
||||
actionForTarget.setTarget(target);
|
||||
actionForTarget.setDistributionSet(set);
|
||||
actionForTarget.setMaintenanceWindowSchedule(targetWithActionType.getMaintenanceSchedule());
|
||||
actionForTarget.setMaintenanceWindowDuration(targetWithActionType.getMaintenanceWindowDuration());
|
||||
actionForTarget.setMaintenanceWindowTimeZone(targetWithActionType.getMaintenanceWindowTimeZone());
|
||||
actionForTarget.setInitiatedBy(initiatedBy);
|
||||
return actionForTarget;
|
||||
}).orElseGet(() -> {
|
||||
log.warn("Cannot find target for targetWithActionType '{}'.", targetWithActionType.getControllerId());
|
||||
return null;
|
||||
});
|
||||
protected boolean isConfirmationFlowEnabled() {
|
||||
return confirmationFlowConfig.getAsBoolean();
|
||||
}
|
||||
|
||||
public JpaActionStatus createActionStatus(final JpaAction action, final String actionMessage) {
|
||||
final JpaActionStatus actionStatus = new JpaActionStatus();
|
||||
actionStatus.setAction(action);
|
||||
actionStatus.setOccurredAt(action.getCreatedAt());
|
||||
/**
|
||||
* Find targets to be considered for assignment.
|
||||
*
|
||||
* @param controllerIDs as provided by repository caller
|
||||
* @param distributionSetId to assign
|
||||
* @return list of targets up to {@link Constants#MAX_ENTRIES_IN_STATEMENT}
|
||||
*/
|
||||
abstract List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long distributionSetId);
|
||||
|
||||
if (StringUtils.hasText(actionMessage)) {
|
||||
actionStatus.addMessage(actionMessage);
|
||||
} else {
|
||||
actionStatus.addMessage(getActionMessage(action));
|
||||
}
|
||||
/**
|
||||
* @param set
|
||||
* @param targets
|
||||
*/
|
||||
abstract void sendTargetUpdatedEvents(final DistributionSet set, final List<JpaTarget> targets);
|
||||
|
||||
return actionStatus;
|
||||
}
|
||||
/**
|
||||
* Update status and DS fields of given target.
|
||||
*
|
||||
* @param distributionSet to set
|
||||
* @param targetIds to change
|
||||
* @param currentUser for auditing
|
||||
*/
|
||||
abstract void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet distributionSet,
|
||||
final List<List<Long>> targetIds, final String currentUser);
|
||||
|
||||
/**
|
||||
* Cancels actions that can be canceled (i.e.
|
||||
* {@link DistributionSet#isRequiredMigrationStep() is <code>false</code>})
|
||||
* as a result of the new assignment and returns all {@link Target}s where
|
||||
* such actions existed.
|
||||
*
|
||||
* @param targetIds to cancel actions for
|
||||
* @return {@link Set} of {@link Target#getId()}s
|
||||
*/
|
||||
abstract Set<Long> cancelActiveActions(List<List<Long>> targetIds);
|
||||
|
||||
/**
|
||||
* Cancels actions that can be canceled (i.e.
|
||||
* {@link DistributionSet#isRequiredMigrationStep() is <code>false</code>})
|
||||
* as a result of the new assignment and returns all {@link Target}s where
|
||||
* such actions existed.
|
||||
*
|
||||
* @param targetIds to cancel actions for
|
||||
*/
|
||||
abstract void closeActiveActions(List<List<Long>> targetIds);
|
||||
|
||||
abstract void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult);
|
||||
|
||||
abstract void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults);
|
||||
|
||||
private static String getActionMessage(final Action action) {
|
||||
final RolloutGroup rolloutGroup = action.getRolloutGroup();
|
||||
@@ -292,18 +281,20 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
}
|
||||
return String.format("Assignment initiated by user '%s'", action.getInitiatedBy());
|
||||
}
|
||||
|
||||
|
||||
private void cancelAssignDistributionSetEvent(final List<Action> actions) {
|
||||
if (CollectionUtils.isEmpty(actions)) {
|
||||
return;
|
||||
}
|
||||
final String tenant = actions.get(0).getTenant();
|
||||
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher()
|
||||
.publishEvent(new CancelTargetAssignmentEvent(tenant,
|
||||
actions, eventPublisherHolder.getApplicationId())));
|
||||
}
|
||||
|
||||
private void assertActionsPerTargetQuota(final Target target, final int requested) {
|
||||
final int quota = quotaManagement.getMaxActionsPerTarget();
|
||||
QuotaHelper.assertAssignmentQuota(target.getId(), requested, quota, Action.class, Target.class,
|
||||
actionRepository::countByTargetId);
|
||||
}
|
||||
|
||||
protected boolean isMultiAssignmentsEnabled() {
|
||||
return multiAssignmentsConfig.getAsBoolean();
|
||||
}
|
||||
|
||||
protected boolean isConfirmationFlowEnabled() {
|
||||
return confirmationFlowConfig.getAsBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,15 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY;
|
||||
import static org.eclipse.hawkbit.repository.model.Action.Status.ERROR;
|
||||
import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
@@ -27,15 +36,6 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY;
|
||||
import static org.eclipse.hawkbit.repository.model.Action.Status.ERROR;
|
||||
import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED;
|
||||
|
||||
/**
|
||||
* Implements utility methods for managing {@link Action}s
|
||||
*/
|
||||
@@ -56,6 +56,54 @@ public class JpaActionManagement {
|
||||
this.repositoryProperties = repositoryProperties;
|
||||
}
|
||||
|
||||
public int getWeightConsideringDefault(final Action action) {
|
||||
return action.getWeight().orElse(repositoryProperties.getActionWeightIfAbsent());
|
||||
}
|
||||
|
||||
protected static boolean isDownloadOnly(final JpaAction action) {
|
||||
return DOWNLOAD_ONLY == action.getActionType();
|
||||
}
|
||||
|
||||
protected List<JpaAction> findActiveActionsHavingStatus(final String controllerId, final Action.Status status) {
|
||||
return actionRepository.findAll(
|
||||
ActionSpecifications.byTargetControllerIdAndIsActiveAndStatus(controllerId, status));
|
||||
}
|
||||
|
||||
protected Action addActionStatus(final JpaActionStatusCreate statusCreate) {
|
||||
final Long actionId = statusCreate.getActionId();
|
||||
final JpaActionStatus actionStatus = statusCreate.build();
|
||||
final JpaAction action = getActionAndThrowExceptionIfNotFound(actionId);
|
||||
|
||||
if (isUpdatingActionStatusAllowed(action, actionStatus)) {
|
||||
return handleAddUpdateActionStatus(actionStatus, action);
|
||||
}
|
||||
|
||||
log.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
|
||||
actionStatus.getStatus(), action.getId());
|
||||
return action;
|
||||
}
|
||||
|
||||
protected JpaAction getActionAndThrowExceptionIfNotFound(final Long actionId) {
|
||||
return actionRepository.findById(actionId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
}
|
||||
|
||||
protected void onActionStatusUpdate(final Action.Status updatedActionStatus, final JpaAction action) {
|
||||
// can be overwritten to intercept the persistence of the action status
|
||||
}
|
||||
|
||||
protected void assertActionStatusQuota(final JpaActionStatus newActionStatus, final JpaAction action) {
|
||||
if (isIntermediateStatus(newActionStatus)) {// check for quota only for intermediate statuses
|
||||
QuotaHelper.assertAssignmentQuota(action.getId(), 1, quotaManagement.getMaxStatusEntriesPerAction(),
|
||||
ActionStatus.class, Action.class, actionStatusRepository::countByActionId);
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertActionStatusMessageQuota(final JpaActionStatus actionStatus) {
|
||||
QuotaHelper.assertAssignmentQuota(actionStatus.getId(), actionStatus.getMessages().size(),
|
||||
quotaManagement.getMaxMessagesPerActionStatus(), "Message", ActionStatus.class.getSimpleName(), null);
|
||||
}
|
||||
|
||||
List<Action> findActiveActionsWithHighestWeightConsideringDefault(final String controllerId,
|
||||
final int maxActionCount) {
|
||||
final List<Action> actions = new ArrayList<>();
|
||||
@@ -84,23 +132,8 @@ public class JpaActionManagement {
|
||||
return actions.stream().sorted(actionImportance).limit(maxActionCount).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected List<JpaAction> findActiveActionsHavingStatus(final String controllerId, final Action.Status status) {
|
||||
return actionRepository.findAll(
|
||||
ActionSpecifications.byTargetControllerIdAndIsActiveAndStatus(controllerId, status));
|
||||
}
|
||||
|
||||
protected Action addActionStatus(final JpaActionStatusCreate statusCreate) {
|
||||
final Long actionId = statusCreate.getActionId();
|
||||
final JpaActionStatus actionStatus = statusCreate.build();
|
||||
final JpaAction action = getActionAndThrowExceptionIfNotFound(actionId);
|
||||
|
||||
if (isUpdatingActionStatusAllowed(action, actionStatus)) {
|
||||
return handleAddUpdateActionStatus(actionStatus, action);
|
||||
}
|
||||
|
||||
log.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
|
||||
actionStatus.getStatus(), action.getId());
|
||||
return action;
|
||||
private static boolean isIntermediateStatus(final JpaActionStatus actionStatus) {
|
||||
return FINISHED != actionStatus.getStatus() && ERROR != actionStatus.getStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,25 +150,12 @@ public class JpaActionManagement {
|
||||
final boolean isAllowedByRepositoryConfiguration = intermediateStatus && !repositoryProperties.isRejectActionStatusForClosedAction();
|
||||
|
||||
//in case of download_only action Status#DOWNLOADED is treated as 'final' already, so we accept one final status from device in case it sends
|
||||
final boolean isAllowedForDownloadOnlyActions = isDownloadOnly(action) && action.getStatus() == Action.Status.DOWNLOADED && !intermediateStatus;
|
||||
final boolean isAllowedForDownloadOnlyActions = isDownloadOnly(
|
||||
action) && action.getStatus() == Action.Status.DOWNLOADED && !intermediateStatus;
|
||||
|
||||
return action.isActive() || isAllowedByRepositoryConfiguration || isAllowedForDownloadOnlyActions;
|
||||
}
|
||||
|
||||
public int getWeightConsideringDefault(final Action action) {
|
||||
return action.getWeight().orElse(repositoryProperties.getActionWeightIfAbsent());
|
||||
}
|
||||
|
||||
protected JpaAction getActionAndThrowExceptionIfNotFound(final Long actionId) {
|
||||
return actionRepository.findById(actionId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
}
|
||||
|
||||
protected static boolean isDownloadOnly(final JpaAction action) {
|
||||
return DOWNLOAD_ONLY == action.getActionType();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}.
|
||||
*/
|
||||
@@ -152,24 +172,4 @@ public class JpaActionManagement {
|
||||
action.setLastActionStatusCode(actionStatus.getCode().orElse(null));
|
||||
return actionRepository.save(action);
|
||||
}
|
||||
|
||||
protected void onActionStatusUpdate(final Action.Status updatedActionStatus, final JpaAction action){
|
||||
// can be overwritten to intercept the persistence of the action status
|
||||
}
|
||||
|
||||
protected void assertActionStatusQuota(final JpaActionStatus newActionStatus, final JpaAction action) {
|
||||
if (isIntermediateStatus(newActionStatus)) {// check for quota only for intermediate statuses
|
||||
QuotaHelper.assertAssignmentQuota(action.getId(), 1, quotaManagement.getMaxStatusEntriesPerAction(),
|
||||
ActionStatus.class, Action.class, actionStatusRepository::countByActionId);
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertActionStatusMessageQuota(final JpaActionStatus actionStatus) {
|
||||
QuotaHelper.assertAssignmentQuota(actionStatus.getId(), actionStatus.getMessages().size(),
|
||||
quotaManagement.getMaxMessagesPerActionStatus(), "Message", ActionStatus.class.getSimpleName(), null);
|
||||
}
|
||||
|
||||
private static boolean isIntermediateStatus(final JpaActionStatus actionStatus) {
|
||||
return FINISHED != actionStatus.getStatus() && ERROR != actionStatus.getStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
|
||||
import org.eclipse.hawkbit.artifact.repository.ArtifactStoreException;
|
||||
@@ -58,8 +60,6 @@ import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
* JPA based {@link ArtifactManagement} implementation.
|
||||
*/
|
||||
@@ -92,6 +92,11 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
this.tenantAware = tenantAware;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return localArtifactRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
@@ -127,90 +132,6 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
}
|
||||
}
|
||||
|
||||
private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload, final boolean isSmEncrypted) {
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
final long smId = artifactUpload.getModuleId();
|
||||
final InputStream stream = artifactUpload.getInputStream();
|
||||
final String fileName = artifactUpload.getFilename();
|
||||
final String contentType = artifactUpload.getContentType();
|
||||
final String providedSha1 = artifactUpload.getProvidedSha1Sum();
|
||||
final String providedMd5 = artifactUpload.getProvidedMd5Sum();
|
||||
final String providedSha256 = artifactUpload.getProvidedSha256Sum();
|
||||
|
||||
try (final InputStream wrappedStream = wrapInQuotaStream(
|
||||
isSmEncrypted ? wrapInEncryptionStream(smId, stream) : stream)) {
|
||||
return artifactRepository.store(tenant, wrappedStream, fileName, contentType,
|
||||
new DbArtifactHash(providedSha1, providedMd5, providedSha256));
|
||||
} catch (final ArtifactStoreException | IOException e) {
|
||||
throw new ArtifactUploadFailedException(e);
|
||||
} catch (final HashNotMatchException e) {
|
||||
if (e.getHashFunction().equals(HashNotMatchException.SHA1)) {
|
||||
throw new InvalidSHA1HashException(e.getMessage(), e);
|
||||
} else if (e.getHashFunction().equals(HashNotMatchException.SHA256)) {
|
||||
throw new InvalidSHA256HashException(e.getMessage(), e);
|
||||
} else {
|
||||
throw new InvalidMD5HashException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream wrapInEncryptionStream(final long smId, final InputStream stream) {
|
||||
return ArtifactEncryptionService.getInstance().encryptSoftwareModuleArtifact(smId, stream);
|
||||
}
|
||||
|
||||
private void assertArtifactQuota(final long moduleId, final int requested) {
|
||||
QuotaHelper.assertAssignmentQuota(
|
||||
moduleId, requested, quotaManagement.getMaxArtifactsPerSoftwareModule(),
|
||||
Artifact.class, SoftwareModule.class,
|
||||
// get all artifacts without user context
|
||||
softwareModuleId -> localArtifactRepository
|
||||
.count(null, ArtifactSpecifications.bySoftwareModuleId(softwareModuleId)));
|
||||
}
|
||||
|
||||
private InputStream wrapInQuotaStream(final InputStream in) {
|
||||
final long maxArtifactSize = quotaManagement.getMaxArtifactSize();
|
||||
|
||||
final long currentlyUsed = localArtifactRepository.sumOfNonDeletedArtifactSize().orElse(0L);
|
||||
final long maxArtifactSizeTotal = quotaManagement.getMaxArtifactStorage();
|
||||
|
||||
return new FileSizeAndStorageQuotaCheckingInputStream(in, maxArtifactSize,
|
||||
maxArtifactSizeTotal - currentlyUsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Garbage collects artifact binaries if only referenced by given
|
||||
* {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are
|
||||
* marked as deleted.
|
||||
* <p/>
|
||||
* Software module related UPDATE permission shall be checked by the callers!
|
||||
*
|
||||
* @param sha1Hash no longer needed
|
||||
* @param softwareModuleId the garbage collection call is made for
|
||||
*/
|
||||
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
void clearArtifactBinary(final String sha1Hash) {
|
||||
// countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse will skip ACM checks and
|
||||
// will return total count as it should be
|
||||
final long count = localArtifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(
|
||||
sha1Hash,
|
||||
tenantAware.getCurrentTenant());
|
||||
if (count <= 1) { // 1 artifact is the one being deleted!
|
||||
// removes the real artifact ONLY AFTER the delete of artifact or software module
|
||||
// in local history has passed successfully (caller has permission and no errors)
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
try {
|
||||
log.debug("deleting artifact from repository {}", sha1Hash);
|
||||
artifactRepository.deleteBySha1(tenantAware.getCurrentTenant(), sha1Hash);
|
||||
} catch (final ArtifactStoreException e) {
|
||||
throw new ArtifactDeleteFailedException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
} // else there are still other artifacts that need the binary
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
@@ -222,7 +143,7 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
// clearArtifactBinary checks (unconditionally) software module UPDATE access
|
||||
softwareModuleRepository.getAccessController().ifPresent(accessController ->
|
||||
accessController.assertOperationAllowed(AccessController.Operation.UPDATE,
|
||||
(JpaSoftwareModule) toDelete.getSoftwareModule()));
|
||||
(JpaSoftwareModule) toDelete.getSoftwareModule()));
|
||||
((JpaSoftwareModule) toDelete.getSoftwareModule()).removeArtifact(toDelete);
|
||||
softwareModuleRepository.save((JpaSoftwareModule) toDelete.getSoftwareModule());
|
||||
|
||||
@@ -268,11 +189,6 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
return localArtifactRepository.count(ArtifactSpecifications.bySoftwareModuleId(softwareModuleId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return localArtifactRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DbArtifact> loadArtifactBinary(final String sha1Hash, final long softwareModuleId,
|
||||
final boolean isEncrypted) {
|
||||
@@ -293,6 +209,91 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Garbage collects artifact binaries if only referenced by given
|
||||
* {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are
|
||||
* marked as deleted.
|
||||
* <p/>
|
||||
* Software module related UPDATE permission shall be checked by the callers!
|
||||
*
|
||||
* @param sha1Hash no longer needed
|
||||
* @param softwareModuleId the garbage collection call is made for
|
||||
*/
|
||||
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
void clearArtifactBinary(final String sha1Hash) {
|
||||
// countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse will skip ACM checks and
|
||||
// will return total count as it should be
|
||||
final long count = localArtifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(
|
||||
sha1Hash,
|
||||
tenantAware.getCurrentTenant());
|
||||
if (count <= 1) { // 1 artifact is the one being deleted!
|
||||
// removes the real artifact ONLY AFTER the delete of artifact or software module
|
||||
// in local history has passed successfully (caller has permission and no errors)
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
try {
|
||||
log.debug("deleting artifact from repository {}", sha1Hash);
|
||||
artifactRepository.deleteBySha1(tenantAware.getCurrentTenant(), sha1Hash);
|
||||
} catch (final ArtifactStoreException e) {
|
||||
throw new ArtifactDeleteFailedException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
} // else there are still other artifacts that need the binary
|
||||
}
|
||||
|
||||
private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload, final boolean isSmEncrypted) {
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
final long smId = artifactUpload.getModuleId();
|
||||
final InputStream stream = artifactUpload.getInputStream();
|
||||
final String fileName = artifactUpload.getFilename();
|
||||
final String contentType = artifactUpload.getContentType();
|
||||
final String providedSha1 = artifactUpload.getProvidedSha1Sum();
|
||||
final String providedMd5 = artifactUpload.getProvidedMd5Sum();
|
||||
final String providedSha256 = artifactUpload.getProvidedSha256Sum();
|
||||
|
||||
try (final InputStream wrappedStream = wrapInQuotaStream(
|
||||
isSmEncrypted ? wrapInEncryptionStream(smId, stream) : stream)) {
|
||||
return artifactRepository.store(tenant, wrappedStream, fileName, contentType,
|
||||
new DbArtifactHash(providedSha1, providedMd5, providedSha256));
|
||||
} catch (final ArtifactStoreException | IOException e) {
|
||||
throw new ArtifactUploadFailedException(e);
|
||||
} catch (final HashNotMatchException e) {
|
||||
if (e.getHashFunction().equals(HashNotMatchException.SHA1)) {
|
||||
throw new InvalidSHA1HashException(e.getMessage(), e);
|
||||
} else if (e.getHashFunction().equals(HashNotMatchException.SHA256)) {
|
||||
throw new InvalidSHA256HashException(e.getMessage(), e);
|
||||
} else {
|
||||
throw new InvalidMD5HashException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream wrapInEncryptionStream(final long smId, final InputStream stream) {
|
||||
return ArtifactEncryptionService.getInstance().encryptSoftwareModuleArtifact(smId, stream);
|
||||
}
|
||||
|
||||
private void assertArtifactQuota(final long moduleId, final int requested) {
|
||||
QuotaHelper.assertAssignmentQuota(
|
||||
moduleId, requested, quotaManagement.getMaxArtifactsPerSoftwareModule(),
|
||||
Artifact.class, SoftwareModule.class,
|
||||
// get all artifacts without user context
|
||||
softwareModuleId -> localArtifactRepository
|
||||
.count(null, ArtifactSpecifications.bySoftwareModuleId(softwareModuleId)));
|
||||
}
|
||||
|
||||
private InputStream wrapInQuotaStream(final InputStream in) {
|
||||
final long maxArtifactSize = quotaManagement.getMaxArtifactSize();
|
||||
|
||||
final long currentlyUsed = localArtifactRepository.sumOfNonDeletedArtifactSize().orElse(0L);
|
||||
final long maxArtifactSizeTotal = quotaManagement.getMaxArtifactStorage();
|
||||
|
||||
return new FileSizeAndStorageQuotaCheckingInputStream(in, maxArtifactSize,
|
||||
maxArtifactSizeTotal - currentlyUsed);
|
||||
}
|
||||
|
||||
private DbArtifact wrapInEncryptionAwareDbArtifact(final long softwareModuleId, final DbArtifact dbArtifact) {
|
||||
if (dbArtifact == null) {
|
||||
return null;
|
||||
|
||||
@@ -65,9 +65,9 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
|
||||
* Constructor
|
||||
*/
|
||||
public JpaConfirmationManagement(final TargetRepository targetRepository,
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final RepositoryProperties repositoryProperties, final QuotaManagement quotaManagement,
|
||||
final EntityFactory entityFactory) {
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final RepositoryProperties repositoryProperties, final QuotaManagement quotaManagement,
|
||||
final EntityFactory entityFactory) {
|
||||
super(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties);
|
||||
this.targetRepository = targetRepository;
|
||||
this.entityFactory = entityFactory;
|
||||
@@ -96,7 +96,7 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
|
||||
final AutoConfirmationStatus autoConfStatus = updatedTarget.getAutoConfirmationStatus();
|
||||
if (autoConfStatus == null) {
|
||||
final String message = String.format("Persisted auto confirmation status is null. "
|
||||
+ "Cannot proceed with giving confirmations for active actions for device %s with initiator %s.",
|
||||
+ "Cannot proceed with giving confirmations for active actions for device %s with initiator %s.",
|
||||
controllerId, initiator);
|
||||
log.error("message");
|
||||
throw new IllegalStateException(message);
|
||||
@@ -159,17 +159,20 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
|
||||
return addActionStatus((JpaActionStatusCreate) statusCreate);
|
||||
}
|
||||
|
||||
private ActionStatusCreate createConfirmationActionStatus(final long actionId, final Integer code,
|
||||
final Collection<String> messages) {
|
||||
final ActionStatusCreate statusCreate = entityFactory.actionStatus().create(actionId);
|
||||
if (!CollectionUtils.isEmpty(messages)) {
|
||||
statusCreate.messages(messages);
|
||||
@Override
|
||||
@Transactional
|
||||
public void deactivateAutoConfirmation(String controllerId) {
|
||||
log.debug("Deactivate auto confirmation for controllerId '{}'", controllerId);
|
||||
final JpaTarget target = getTargetByControllerIdAndThrowIfNotFound(controllerId);
|
||||
target.setAutoConfirmationStatus(null);
|
||||
targetRepository.save(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActionStatusUpdate(final Status updatedActionStatus, final JpaAction action) {
|
||||
if (updatedActionStatus == Status.RUNNING && action.isActive()) {
|
||||
action.setStatus(Status.RUNNING);
|
||||
}
|
||||
if (code != null) {
|
||||
statusCreate.code(code);
|
||||
statusCreate.message(String.format(CONFIRMATION_CODE_MSG_PREFIX, code));
|
||||
}
|
||||
return statusCreate;
|
||||
}
|
||||
|
||||
private static void assertActionCanAcceptFeedback(final Action action) {
|
||||
@@ -188,6 +191,19 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
|
||||
}
|
||||
}
|
||||
|
||||
private ActionStatusCreate createConfirmationActionStatus(final long actionId, final Integer code,
|
||||
final Collection<String> messages) {
|
||||
final ActionStatusCreate statusCreate = entityFactory.actionStatus().create(actionId);
|
||||
if (!CollectionUtils.isEmpty(messages)) {
|
||||
statusCreate.messages(messages);
|
||||
}
|
||||
if (code != null) {
|
||||
statusCreate.code(code);
|
||||
statusCreate.message(String.format(CONFIRMATION_CODE_MSG_PREFIX, code));
|
||||
}
|
||||
return statusCreate;
|
||||
}
|
||||
|
||||
private List<Action> giveConfirmationForActiveActions(final AutoConfirmationStatus autoConfirmationStatus) {
|
||||
final Target target = autoConfirmationStatus.getTarget();
|
||||
return findActiveActionsHavingStatus(target.getControllerId(), Status.WAIT_FOR_CONFIRMATION).stream()
|
||||
@@ -218,24 +234,8 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
|
||||
return actionRepository.save(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deactivateAutoConfirmation(String controllerId) {
|
||||
log.debug("Deactivate auto confirmation for controllerId '{}'", controllerId);
|
||||
final JpaTarget target = getTargetByControllerIdAndThrowIfNotFound(controllerId);
|
||||
target.setAutoConfirmationStatus(null);
|
||||
targetRepository.save(target);
|
||||
}
|
||||
|
||||
private JpaTarget getTargetByControllerIdAndThrowIfNotFound(final String controllerId) {
|
||||
return targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId))
|
||||
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActionStatusUpdate(final Status updatedActionStatus, final JpaAction action) {
|
||||
if (updatedActionStatus == Status.RUNNING && action.isActive()) {
|
||||
action.setStatus(Status.RUNNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -127,16 +127,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
+ ACTION_PAGE_LIMIT;
|
||||
|
||||
private static final EnumMap<Database, String> QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED;
|
||||
|
||||
static {
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED = new EnumMap<>(Database.class);
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.SQL_SERVER, "DELETE TOP (" + ACTION_PAGE_LIMIT
|
||||
+ ") FROM sp_action WHERE tenant=#tenant AND status IN (%s) AND last_modified_at<#last_modified_at ");
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.POSTGRESQL,
|
||||
"DELETE FROM sp_action WHERE id IN (SELECT id FROM sp_action WHERE tenant=#tenant AND status IN (%s) AND last_modified_at<#last_modified_at LIMIT "
|
||||
+ ACTION_PAGE_LIMIT + ")");
|
||||
}
|
||||
|
||||
private final EntityManager entityManager;
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
private final TargetRepository targetRepository;
|
||||
@@ -152,6 +142,15 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
private final Database database;
|
||||
private final RetryTemplate retryTemplate;
|
||||
|
||||
static {
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED = new EnumMap<>(Database.class);
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.SQL_SERVER, "DELETE TOP (" + ACTION_PAGE_LIMIT
|
||||
+ ") FROM sp_action WHERE tenant=#tenant AND status IN (%s) AND last_modified_at<#last_modified_at ");
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.POSTGRESQL,
|
||||
"DELETE FROM sp_action WHERE id IN (SELECT id FROM sp_action WHERE tenant=#tenant AND status IN (%s) AND last_modified_at<#last_modified_at LIMIT "
|
||||
+ ACTION_PAGE_LIMIT + ")");
|
||||
}
|
||||
|
||||
public JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository,
|
||||
final DistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
|
||||
final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider,
|
||||
@@ -183,9 +182,18 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(
|
||||
final Collection<Entry<String, Long>> assignments) {
|
||||
return offlineAssignedDistributionSets(assignments,tenantAware.getCurrentUsername());
|
||||
public List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
final List<DeploymentRequest> deploymentRequests) {
|
||||
return assignDistributionSets(tenantAware.getCurrentUsername(), deploymentRequests, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public List<DistributionSetAssignmentResult> assignDistributionSets(final String initiatedBy,
|
||||
final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
|
||||
.validate(deploymentRequests);
|
||||
return assignDistributionSets(initiatedBy, deploymentRequests, actionMessage, onlineDsAssignmentStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -205,18 +213,422 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
final List<DeploymentRequest> deploymentRequests) {
|
||||
return assignDistributionSets(tenantAware.getCurrentUsername(), deploymentRequests, null);
|
||||
public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(
|
||||
final Collection<Entry<String, Long>> assignments) {
|
||||
return offlineAssignedDistributionSets(assignments, tenantAware.getCurrentUsername());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public List<DistributionSetAssignmentResult> assignDistributionSets(final String initiatedBy,
|
||||
final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
|
||||
.validate(deploymentRequests);
|
||||
return assignDistributionSets(initiatedBy, deploymentRequests, actionMessage, onlineDsAssignmentStrategy);
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Action cancelAction(final long actionId) {
|
||||
log.debug("cancelAction({})", actionId);
|
||||
|
||||
final JpaAction action = actionRepository.findById(actionId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
|
||||
if (action.isCancelingOrCanceled()) {
|
||||
throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled");
|
||||
}
|
||||
|
||||
assertTargetUpdateAllowed(action);
|
||||
|
||||
if (action.isActive()) {
|
||||
log.debug("action ({}) was still active. Change to {}.", action, Status.CANCELING);
|
||||
action.setStatus(Status.CANCELING);
|
||||
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "manual cancelation requested"));
|
||||
final Action saveAction = actionRepository.save(action);
|
||||
|
||||
onlineDsAssignmentStrategy.cancelAssignment(action);
|
||||
|
||||
return saveAction;
|
||||
} else {
|
||||
throw new CancelActionNotAllowedException(action.getId() + " is not active and cannot be canceled");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActionsByTarget(final String rsqlParam, final String controllerId) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
|
||||
final List<Specification<JpaAction>> specList = Arrays.asList(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database),
|
||||
ActionSpecifications.byTargetControllerId(controllerId));
|
||||
|
||||
return JpaManagementHelper.countBySpec(actionRepository, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActionsAll() {
|
||||
return actionRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActions(final String rsqlParam) {
|
||||
final List<Specification<JpaAction>> specList = List.of(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database));
|
||||
return JpaManagementHelper.countBySpec(actionRepository, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActionsByTarget(final String controllerId) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return actionRepository.countByTargetControllerId(controllerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Action> findAction(final long actionId) {
|
||||
return actionRepository.findById(actionId)
|
||||
.filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId())))
|
||||
.map(JpaAction.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Action> findActionsAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Action> findActions(final String rsqlParam, final Pageable pageable) {
|
||||
final List<Specification<JpaAction>> specList = List.of(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database));
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Action> findActionsByTarget(final String rsqlParam, final String controllerId,
|
||||
final Pageable pageable) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
|
||||
final List<Specification<JpaAction>> specList = Arrays.asList(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database),
|
||||
ActionSpecifications.byTargetControllerId(controllerId));
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(actionRepository, pageable, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Action> findActionsByTarget(final String controllerId, final Pageable pageable) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return actionRepository.findAll(ActionSpecifications.byTargetControllerId(controllerId), pageable)
|
||||
.map(Action.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ActionStatus> findActionStatusByAction(final Pageable pageReq, final long actionId) {
|
||||
assertActionExistsAndAccessible(actionId);
|
||||
|
||||
return actionStatusRepository.findByActionId(pageReq, actionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActionStatusByAction(final long actionId) {
|
||||
assertActionExistsAndAccessible(actionId);
|
||||
|
||||
return actionStatusRepository.countByActionId(actionId);
|
||||
}
|
||||
|
||||
// action is already got and there are checked read permissions - do not check
|
||||
// permissions
|
||||
// and UI which is to be removed
|
||||
@Override
|
||||
public Page<String> findMessagesByActionStatusId(final Pageable pageable, final long actionStatusId) {
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
|
||||
final CriteriaQuery<String> msgQuery = cb.createQuery(String.class);
|
||||
final Root<JpaActionStatus> as = msgQuery.from(JpaActionStatus.class);
|
||||
final ListJoin<JpaActionStatus, String> join = as.joinList("messages", JoinType.LEFT);
|
||||
final CriteriaQuery<String> selMsgQuery = msgQuery.select(join);
|
||||
selMsgQuery.where(cb.equal(as.get(JpaActionStatus_.id), actionStatusId));
|
||||
|
||||
final List<String> result = new ArrayList<>(entityManager.createQuery(selMsgQuery)
|
||||
.setFirstResult((int) pageable.getOffset()).setMaxResults(pageable.getPageSize()).getResultList());
|
||||
|
||||
return new PageImpl<>(result, pageable, result.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Action> findActionWithDetails(final long actionId) {
|
||||
return actionRepository.findWithDetailsById(actionId)
|
||||
.filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Action> findActiveActionsByTarget(final Pageable pageable, final String controllerId) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return actionRepository
|
||||
.findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, true), pageable)
|
||||
.map(Action.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Action> findInActiveActionsByTarget(final Pageable pageable, final String controllerId) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return actionRepository
|
||||
.findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, false), pageable)
|
||||
.map(Action.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> findActiveActionsWithHighestWeight(final String controllerId, final int maxActionCount) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Action forceQuitAction(final long actionId) {
|
||||
final JpaAction action = actionRepository.findById(actionId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
|
||||
if (!action.isCancelingOrCanceled()) {
|
||||
throw new ForceQuitActionNotAllowedException(
|
||||
action.getId() + " is not canceled yet and cannot be force quit");
|
||||
}
|
||||
|
||||
if (!action.isActive()) {
|
||||
throw new ForceQuitActionNotAllowedException(action.getId() + " is not active and cannot be force quit");
|
||||
}
|
||||
|
||||
assertTargetUpdateAllowed(action);
|
||||
|
||||
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, System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "A force quit has been performed."));
|
||||
|
||||
DeploymentHelper.successCancellation(action, actionRepository, targetRepository);
|
||||
|
||||
return actionRepository.save(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Action forceTargetAction(final long actionId) {
|
||||
final JpaAction action = actionRepository.findById(actionId).map(this::assertTargetUpdateAllowed)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
|
||||
if (!action.isForcedOrTimeForced()) {
|
||||
action.setActionType(ActionType.FORCED);
|
||||
return actionRepository.save(action);
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void cancelInactiveScheduledActionsForTargets(final List<Long> targetIds) {
|
||||
if (!isMultiAssignmentsEnabled()) {
|
||||
targetRepository.getAccessController().ifPresent(v -> {
|
||||
if (targetRepository.count(AccessController.Operation.UPDATE,
|
||||
TargetSpecifications.hasIdIn(targetIds)) != targetIds.size()) {
|
||||
throw new EntityNotFoundException(Target.class, targetIds);
|
||||
}
|
||||
});
|
||||
actionRepository.switchStatus(Status.CANCELED, targetIds, false, Status.SCHEDULED);
|
||||
} else {
|
||||
log.debug("The Multi Assignments feature is enabled: No need to cancel inactive scheduled actions.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScheduledActionsByRolloutGroupParent(final long rolloutId, final long distributionSetId,
|
||||
final Long rolloutGroupParentId) {
|
||||
while (DeploymentHelper.runInNewTransaction(
|
||||
txManager,
|
||||
"startScheduledActions-" + rolloutId,
|
||||
status -> {
|
||||
final PageRequest pageRequest = PageRequest.of(0, ACTION_PAGE_LIMIT);
|
||||
final Page<Action> groupScheduledActions;
|
||||
if (rolloutGroupParentId == null) {
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIsNullAndStatus(pageRequest, rolloutId,
|
||||
Action.Status.SCHEDULED);
|
||||
} else {
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIdAndStatus(pageRequest, rolloutId,
|
||||
rolloutGroupParentId, Action.Status.SCHEDULED);
|
||||
}
|
||||
|
||||
if (groupScheduledActions.getContent().isEmpty()) {
|
||||
return 0L;
|
||||
} else {
|
||||
// self invocation won't check @PreAuthorize but it is already checked for the method
|
||||
startScheduledActions(groupScheduledActions.getContent());
|
||||
return groupScheduledActions.getTotalElements();
|
||||
}
|
||||
}) > 0) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScheduledActions(final List<Action> rolloutGroupActions) {
|
||||
// Close actions already assigned and collect pending assignments
|
||||
final List<JpaAction> pendingTargetAssignments = rolloutGroupActions.stream()
|
||||
.map(JpaAction.class::cast)
|
||||
.map(this::closeActionIfSetWasAlreadyAssigned)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
if (pendingTargetAssignments.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// check if old actions needs to be canceled first
|
||||
final List<Action> newTargetAssignments = startScheduledActionsAndHandleOpenCancellationFirst(pendingTargetAssignments);
|
||||
if (!newTargetAssignments.isEmpty()) {
|
||||
onlineDsAssignmentStrategy.sendDeploymentEvents(newTargetAssignments.get(0).getDistributionSet().getId(), newTargetAssignments);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSet> getAssignedDistributionSet(final String controllerId) {
|
||||
return targetRepository
|
||||
.findOne(TargetSpecifications.hasControllerId(controllerId))
|
||||
.map(JpaTarget::getAssignedDistributionSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSet> getInstalledDistributionSet(final String controllerId) {
|
||||
return targetRepository
|
||||
.findOne(TargetSpecifications.hasControllerId(controllerId))
|
||||
.map(JpaTarget::getInstalledDistributionSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = false)
|
||||
public int deleteActionsByStatusAndLastModifiedBefore(final Set<Status> status, final long lastModified) {
|
||||
if (status.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* We use a native query here because Spring JPA does not support to specify a
|
||||
* LIMIT clause on a DELETE statement. However, for this specific use case
|
||||
* (action cleanup), we must specify a row limit to reduce the overall load on
|
||||
* the database.
|
||||
*/
|
||||
|
||||
final int statusCount = status.size();
|
||||
final Status[] statusArr = status.toArray(new Status[statusCount]);
|
||||
|
||||
final String queryStr = String.format(getQueryForDeleteActionsByStatusAndLastModifiedBeforeString(database),
|
||||
formatInClauseWithNumberKeys(statusCount));
|
||||
final Query deleteQuery = entityManager.createNativeQuery(queryStr);
|
||||
|
||||
IntStream.range(0, statusCount)
|
||||
.forEach(i -> deleteQuery.setParameter(String.valueOf(i), statusArr[i].ordinal()));
|
||||
deleteQuery.setParameter("tenant", tenantAware.getCurrentTenant().toUpperCase());
|
||||
deleteQuery.setParameter("last_modified_at", lastModified);
|
||||
|
||||
log.debug("Action cleanup: Executing the following (native) query: {}", deleteQuery);
|
||||
return deleteQuery.executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPendingCancellations(final Long targetId) {
|
||||
// target access checked in assertTargetReadAllowed
|
||||
assertTargetReadAllowed(targetId);
|
||||
return actionRepository
|
||||
.exists(ActionSpecifications.byTargetIdAndIsActiveAndStatus(targetId, Action.Status.CANCELING));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelActionsForDistributionSet(final CancelationType cancelationType,
|
||||
final DistributionSet distributionSet) {
|
||||
actionRepository.findAll(ActionSpecifications
|
||||
.byDistributionSetIdAndActiveAndStatusIsNot(distributionSet.getId(), Status.CANCELING))
|
||||
.forEach(action -> {
|
||||
try {
|
||||
assertTargetUpdateAllowed(action);
|
||||
cancelAction(action.getId());
|
||||
log.debug("Action {} canceled", action.getId());
|
||||
} catch (final InsufficientPermissionException e) {
|
||||
log.trace("Could not cancel action {} due to insufficient permissions.", action.getId(), e);
|
||||
} catch (final EntityNotFoundException e) {
|
||||
log.trace("Could not cancel action {} due to entity not found exception.", action.getId(), e);
|
||||
}
|
||||
});
|
||||
if (cancelationType == CancelationType.FORCE) {
|
||||
actionRepository.findAll(ActionSpecifications.byDistributionSetIdAndActive(distributionSet.getId()))
|
||||
.forEach(action -> {
|
||||
try {
|
||||
assertTargetUpdateAllowed(action);
|
||||
forceQuitAction(action.getId());
|
||||
log.debug("Action {} force canceled", action.getId());
|
||||
} catch (final InsufficientPermissionException e) {
|
||||
log.trace("Could not cancel action {} due to insufficient permissions.", action.getId(), e);
|
||||
} catch (final EntityNotFoundException e) {
|
||||
log.trace("Could not cancel action {} due to entity not found exception.", action.getId(),
|
||||
e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected ActionRepository getActionRepository() {
|
||||
return actionRepository;
|
||||
}
|
||||
|
||||
protected boolean isActionsAutocloseEnabled() {
|
||||
return getConfigValue(REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class);
|
||||
}
|
||||
|
||||
private static Map<Long, List<TargetWithActionType>> convertRequest(
|
||||
final Collection<DeploymentRequest> deploymentRequests) {
|
||||
return deploymentRequests.stream().collect(Collectors.groupingBy(DeploymentRequest::getDistributionSetId,
|
||||
Collectors.mapping(DeploymentRequest::getTargetWithActionType, Collectors.toList())));
|
||||
}
|
||||
|
||||
/**
|
||||
* split tIDs length into max entries in-statement because many database have
|
||||
* constraint of max entries in in-statements e.g. Oracle with maximum 1000
|
||||
* elements, so we need to split the entries here and execute multiple
|
||||
* statements
|
||||
*/
|
||||
private static List<List<Long>> getTargetEntitiesAsChunks(final List<JpaTarget> targetEntities) {
|
||||
return ListUtils.partition(targetEntities.stream().map(Target::getId).collect(Collectors.toList()),
|
||||
Constants.MAX_ENTRIES_IN_STATEMENT);
|
||||
}
|
||||
|
||||
private static DistributionSetAssignmentResult buildAssignmentResult(final JpaDistributionSet distributionSet,
|
||||
final List<JpaAction> assignedActions, final int totalTargetsForAssignment) {
|
||||
final int alreadyAssignedTargetsCount = totalTargetsForAssignment - assignedActions.size();
|
||||
|
||||
return new DistributionSetAssignmentResult(distributionSet, alreadyAssignedTargetsCount, assignedActions);
|
||||
}
|
||||
|
||||
private static String getQueryForDeleteActionsByStatusAndLastModifiedBeforeString(final Database database) {
|
||||
return QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.getOrDefault(database,
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED_DEFAULT);
|
||||
}
|
||||
|
||||
private static String formatInClauseWithNumberKeys(final int count) {
|
||||
return formatInClause(IntStream.range(0, count).mapToObj(String::valueOf).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static String formatInClause(final Collection<String> elements) {
|
||||
return "#" + String.join(",#", elements);
|
||||
}
|
||||
|
||||
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 String initiatedBy,
|
||||
@@ -327,12 +739,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
return deploymentRequests;
|
||||
}
|
||||
|
||||
private static Map<Long, List<TargetWithActionType>> convertRequest(
|
||||
final Collection<DeploymentRequest> deploymentRequests) {
|
||||
return deploymentRequests.stream().collect(Collectors.groupingBy(DeploymentRequest::getDistributionSetId,
|
||||
Collectors.mapping(DeploymentRequest::getTargetWithActionType, Collectors.toList())));
|
||||
}
|
||||
|
||||
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy,
|
||||
final Long dsId, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy) {
|
||||
@@ -355,21 +761,14 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
* to {@link TargetUpdateStatus#IN_SYNC} <br/>
|
||||
* D. does not send a {@link TargetAssignDistributionSetEvent}.<br/>
|
||||
*
|
||||
* @param initiatedBy
|
||||
* the username of the user who initiated the assignment
|
||||
* @param dsId
|
||||
* the ID of the distribution set to assign
|
||||
* @param targetsWithActionType
|
||||
* a list of all targets and their action type
|
||||
* @param actionMessage
|
||||
* an optional message to be written into the action status
|
||||
* @param assignmentStrategy
|
||||
* the assignment strategy (online /offline)
|
||||
* @param initiatedBy the username of the user who initiated the assignment
|
||||
* @param dsId the ID of the distribution set to assign
|
||||
* @param targetsWithActionType a list of all targets and their action type
|
||||
* @param actionMessage an optional message to be written into the action status
|
||||
* @param assignmentStrategy the assignment strategy (online /offline)
|
||||
* @return the assignment result
|
||||
*
|
||||
* @throws IncompleteDistributionSetException
|
||||
* if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
* define by the {@link DistributionSetType}.
|
||||
* @throws IncompleteDistributionSetException if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
* define by the {@link DistributionSetType}.
|
||||
*/
|
||||
private DistributionSetAssignmentResult assignDistributionSetToTargets(final String initiatedBy, final Long dsId,
|
||||
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
@@ -377,7 +776,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
final JpaDistributionSet distributionSet =
|
||||
(JpaDistributionSet) distributionSetManagement.getValidAndComplete(dsId);
|
||||
|
||||
if (((JpaDistributionSetManagement)distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
|
||||
if (((JpaDistributionSetManagement) distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
|
||||
// without new transaction DS changed event is not thrown
|
||||
DeploymentHelper.runInNewTransaction(txManager, "Implicit lock", status -> {
|
||||
distributionSetManagement.lock(distributionSet.getId());
|
||||
@@ -446,24 +845,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
return new ArrayList<>(assignedActions.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* split tIDs length into max entries in-statement because many database have
|
||||
* constraint of max entries in in-statements e.g. Oracle with maximum 1000
|
||||
* elements, so we need to split the entries here and execute multiple
|
||||
* statements
|
||||
*/
|
||||
private static List<List<Long>> getTargetEntitiesAsChunks(final List<JpaTarget> targetEntities) {
|
||||
return ListUtils.partition(targetEntities.stream().map(Target::getId).collect(Collectors.toList()),
|
||||
Constants.MAX_ENTRIES_IN_STATEMENT);
|
||||
}
|
||||
|
||||
private static DistributionSetAssignmentResult buildAssignmentResult(final JpaDistributionSet distributionSet,
|
||||
final List<JpaAction> assignedActions, final int totalTargetsForAssignment) {
|
||||
final int alreadyAssignedTargetsCount = totalTargetsForAssignment - assignedActions.size();
|
||||
|
||||
return new DistributionSetAssignmentResult(distributionSet, alreadyAssignedTargetsCount, assignedActions);
|
||||
}
|
||||
|
||||
private void enforceMaxAssignmentsPerRequest(final int requestedActions) {
|
||||
QuotaHelper.assertAssignmentRequestSizeQuota(requestedActions,
|
||||
quotaManagement.getMaxTargetDistributionSetAssignmentsPerManualAssignment());
|
||||
@@ -489,24 +870,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void cancelInactiveScheduledActionsForTargets(final List<Long> targetIds) {
|
||||
if (!isMultiAssignmentsEnabled()) {
|
||||
targetRepository.getAccessController().ifPresent(v -> {
|
||||
if (targetRepository.count(AccessController.Operation.UPDATE,
|
||||
TargetSpecifications.hasIdIn(targetIds)) != targetIds.size()) {
|
||||
throw new EntityNotFoundException(Target.class, targetIds);
|
||||
}
|
||||
});
|
||||
actionRepository.switchStatus(Status.CANCELED, targetIds, false, Status.SCHEDULED);
|
||||
} else {
|
||||
log.debug("The Multi Assignments feature is enabled: No need to cancel inactive scheduled actions.");
|
||||
}
|
||||
}
|
||||
|
||||
private void setAssignedDistributionSetAndTargetUpdateStatus(final AbstractDsAssignmentStrategy assignmentStrategy,
|
||||
final JpaDistributionSet set, final List<List<Long>> targetIdsChunks) {
|
||||
final String currentUser = auditorProvider.getCurrentAuditor().orElse(null);
|
||||
@@ -581,112 +944,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
assignmentStrategy.sendTargetUpdatedEvents(set, targets);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Action cancelAction(final long actionId) {
|
||||
log.debug("cancelAction({})", actionId);
|
||||
|
||||
final JpaAction action = actionRepository.findById(actionId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
|
||||
if (action.isCancelingOrCanceled()) {
|
||||
throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled");
|
||||
}
|
||||
|
||||
assertTargetUpdateAllowed(action);
|
||||
|
||||
if (action.isActive()) {
|
||||
log.debug("action ({}) was still active. Change to {}.", action, Status.CANCELING);
|
||||
action.setStatus(Status.CANCELING);
|
||||
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "manual cancelation requested"));
|
||||
final Action saveAction = actionRepository.save(action);
|
||||
|
||||
onlineDsAssignmentStrategy.cancelAssignment(action);
|
||||
|
||||
return saveAction;
|
||||
} else {
|
||||
throw new CancelActionNotAllowedException(action.getId() + " is not active and cannot be canceled");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Action forceQuitAction(final long actionId) {
|
||||
final JpaAction action = actionRepository.findById(actionId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
|
||||
if (!action.isCancelingOrCanceled()) {
|
||||
throw new ForceQuitActionNotAllowedException(
|
||||
action.getId() + " is not canceled yet and cannot be force quit");
|
||||
}
|
||||
|
||||
if (!action.isActive()) {
|
||||
throw new ForceQuitActionNotAllowedException(action.getId() + " is not active and cannot be force quit");
|
||||
}
|
||||
|
||||
assertTargetUpdateAllowed(action);
|
||||
|
||||
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, System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "A force quit has been performed."));
|
||||
|
||||
DeploymentHelper.successCancellation(action, actionRepository, targetRepository);
|
||||
|
||||
return actionRepository.save(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScheduledActionsByRolloutGroupParent(final long rolloutId, final long distributionSetId,
|
||||
final Long rolloutGroupParentId) {
|
||||
while (DeploymentHelper.runInNewTransaction(
|
||||
txManager,
|
||||
"startScheduledActions-" + rolloutId,
|
||||
status -> {
|
||||
final PageRequest pageRequest = PageRequest.of(0, ACTION_PAGE_LIMIT);
|
||||
final Page<Action> groupScheduledActions;
|
||||
if (rolloutGroupParentId == null) {
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIsNullAndStatus(pageRequest, rolloutId, Action.Status.SCHEDULED);
|
||||
} else {
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIdAndStatus(pageRequest, rolloutId, rolloutGroupParentId, Action.Status.SCHEDULED);
|
||||
}
|
||||
|
||||
if (groupScheduledActions.getContent().isEmpty()) {
|
||||
return 0L;
|
||||
} else {
|
||||
// self invocation won't check @PreAuthorize but it is already checked for the method
|
||||
startScheduledActions(groupScheduledActions.getContent());
|
||||
return groupScheduledActions.getTotalElements();
|
||||
}
|
||||
}) > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScheduledActions(final List<Action> rolloutGroupActions) {
|
||||
// Close actions already assigned and collect pending assignments
|
||||
final List<JpaAction> pendingTargetAssignments = rolloutGroupActions.stream()
|
||||
.map(JpaAction.class::cast)
|
||||
.map(this::closeActionIfSetWasAlreadyAssigned)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
if (pendingTargetAssignments.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// check if old actions needs to be canceled first
|
||||
final List<Action> newTargetAssignments = startScheduledActionsAndHandleOpenCancellationFirst(pendingTargetAssignments);
|
||||
if (!newTargetAssignments.isEmpty()) {
|
||||
onlineDsAssignmentStrategy.sendDeploymentEvents(newTargetAssignments.get(0).getDistributionSet().getId(), newTargetAssignments);
|
||||
}
|
||||
}
|
||||
|
||||
private JpaAction closeActionIfSetWasAlreadyAssigned(final JpaAction action) {
|
||||
if (isMultiAssignmentsEnabled()) {
|
||||
return action;
|
||||
@@ -764,221 +1021,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
actionStatusRepository.save(actionStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Action> findAction(final long actionId) {
|
||||
return actionRepository.findById(actionId)
|
||||
.filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId())))
|
||||
.map(JpaAction.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Action> findActionWithDetails(final long actionId) {
|
||||
return actionRepository.findWithDetailsById(actionId)
|
||||
.filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Action> findActionsByTarget(final String controllerId, final Pageable pageable) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return actionRepository.findAll(ActionSpecifications.byTargetControllerId(controllerId), pageable)
|
||||
.map(Action.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Action> findActionsByTarget(final String rsqlParam, final String controllerId,
|
||||
final Pageable pageable) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
|
||||
final List<Specification<JpaAction>> specList = Arrays.asList(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database),
|
||||
ActionSpecifications.byTargetControllerId(controllerId));
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(actionRepository, pageable, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Action> findActiveActionsByTarget(final Pageable pageable, final String controllerId) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return actionRepository
|
||||
.findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, true), pageable)
|
||||
.map(Action.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Action> findInActiveActionsByTarget(final Pageable pageable, final String controllerId) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return actionRepository
|
||||
.findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, false), pageable)
|
||||
.map(Action.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> findActiveActionsWithHighestWeight(final String controllerId, final int maxActionCount) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActionsByTarget(final String controllerId) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return actionRepository.countByTargetControllerId(controllerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActionsByTarget(final String rsqlParam, final String controllerId) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
|
||||
final List<Specification<JpaAction>> specList = Arrays.asList(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database),
|
||||
ActionSpecifications.byTargetControllerId(controllerId));
|
||||
|
||||
return JpaManagementHelper.countBySpec(actionRepository, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Action forceTargetAction(final long actionId) {
|
||||
final JpaAction action = actionRepository.findById(actionId).map(this::assertTargetUpdateAllowed)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
|
||||
if (!action.isForcedOrTimeForced()) {
|
||||
action.setActionType(ActionType.FORCED);
|
||||
return actionRepository.save(action);
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ActionStatus> findActionStatusByAction(final Pageable pageReq, final long actionId) {
|
||||
assertActionExistsAndAccessible(actionId);
|
||||
|
||||
return actionStatusRepository.findByActionId(pageReq, actionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActionStatusByAction(final long actionId) {
|
||||
assertActionExistsAndAccessible(actionId);
|
||||
|
||||
return actionStatusRepository.countByActionId(actionId);
|
||||
}
|
||||
|
||||
// action is already got and there are checked read permissions - do not check
|
||||
// permissions
|
||||
// and UI which is to be removed
|
||||
@Override
|
||||
public Page<String> findMessagesByActionStatusId(final Pageable pageable, final long actionStatusId) {
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
|
||||
final CriteriaQuery<String> msgQuery = cb.createQuery(String.class);
|
||||
final Root<JpaActionStatus> as = msgQuery.from(JpaActionStatus.class);
|
||||
final ListJoin<JpaActionStatus, String> join = as.joinList("messages", JoinType.LEFT);
|
||||
final CriteriaQuery<String> selMsgQuery = msgQuery.select(join);
|
||||
selMsgQuery.where(cb.equal(as.get(JpaActionStatus_.id), actionStatusId));
|
||||
|
||||
final List<String> result = new ArrayList<>(entityManager.createQuery(selMsgQuery)
|
||||
.setFirstResult((int) pageable.getOffset()).setMaxResults(pageable.getPageSize()).getResultList());
|
||||
|
||||
return new PageImpl<>(result, pageable, result.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActionsAll() {
|
||||
return actionRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActions(final String rsqlParam) {
|
||||
final List<Specification<JpaAction>> specList = List.of(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database));
|
||||
return JpaManagementHelper.countBySpec(actionRepository, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Action> findActionsAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Action> findActions(final String rsqlParam, final Pageable pageable) {
|
||||
final List<Specification<JpaAction>> specList = List.of(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database));
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSet> getAssignedDistributionSet(final String controllerId) {
|
||||
return targetRepository
|
||||
.findOne(TargetSpecifications.hasControllerId(controllerId))
|
||||
.map(JpaTarget::getAssignedDistributionSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSet> getInstalledDistributionSet(final String controllerId) {
|
||||
return targetRepository
|
||||
.findOne(TargetSpecifications.hasControllerId(controllerId))
|
||||
.map(JpaTarget::getInstalledDistributionSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = false)
|
||||
public int deleteActionsByStatusAndLastModifiedBefore(final Set<Status> status, final long lastModified) {
|
||||
if (status.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* We use a native query here because Spring JPA does not support to specify a
|
||||
* LIMIT clause on a DELETE statement. However, for this specific use case
|
||||
* (action cleanup), we must specify a row limit to reduce the overall load on
|
||||
* the database.
|
||||
*/
|
||||
|
||||
final int statusCount = status.size();
|
||||
final Status[] statusArr = status.toArray(new Status[statusCount]);
|
||||
|
||||
final String queryStr = String.format(getQueryForDeleteActionsByStatusAndLastModifiedBeforeString(database),
|
||||
formatInClauseWithNumberKeys(statusCount));
|
||||
final Query deleteQuery = entityManager.createNativeQuery(queryStr);
|
||||
|
||||
IntStream.range(0, statusCount)
|
||||
.forEach(i -> deleteQuery.setParameter(String.valueOf(i), statusArr[i].ordinal()));
|
||||
deleteQuery.setParameter("tenant", tenantAware.getCurrentTenant().toUpperCase());
|
||||
deleteQuery.setParameter("last_modified_at", lastModified);
|
||||
|
||||
log.debug("Action cleanup: Executing the following (native) query: {}", deleteQuery);
|
||||
return deleteQuery.executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPendingCancellations(final Long targetId) {
|
||||
// target access checked in assertTargetReadAllowed
|
||||
assertTargetReadAllowed(targetId);
|
||||
return actionRepository
|
||||
.exists(ActionSpecifications.byTargetIdAndIsActiveAndStatus(targetId, Action.Status.CANCELING));
|
||||
}
|
||||
|
||||
private static String getQueryForDeleteActionsByStatusAndLastModifiedBeforeString(final Database database) {
|
||||
return QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.getOrDefault(database,
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED_DEFAULT);
|
||||
}
|
||||
|
||||
private static String formatInClauseWithNumberKeys(final int count) {
|
||||
return formatInClause(IntStream.range(0, count).mapToObj(String::valueOf).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static String formatInClause(final Collection<String> elements) {
|
||||
return "#" + String.join(",#", elements);
|
||||
}
|
||||
|
||||
protected ActionRepository getActionRepository() {
|
||||
return actionRepository;
|
||||
}
|
||||
|
||||
protected boolean isActionsAutocloseEnabled() {
|
||||
return getConfigValue(REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class);
|
||||
}
|
||||
|
||||
private boolean isMultiAssignmentsEnabled() {
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
|
||||
.isMultiAssignmentsEnabled();
|
||||
@@ -994,54 +1036,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
.runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType).getValue());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelActionsForDistributionSet(final CancelationType cancelationType,
|
||||
final DistributionSet distributionSet) {
|
||||
actionRepository.findAll(ActionSpecifications
|
||||
.byDistributionSetIdAndActiveAndStatusIsNot(distributionSet.getId(), Status.CANCELING))
|
||||
.forEach(action -> {
|
||||
try {
|
||||
assertTargetUpdateAllowed(action);
|
||||
cancelAction(action.getId());
|
||||
log.debug("Action {} canceled", action.getId());
|
||||
} catch (final InsufficientPermissionException e) {
|
||||
log.trace("Could not cancel action {} due to insufficient permissions.", action.getId(), e);
|
||||
} catch (final EntityNotFoundException e) {
|
||||
log.trace("Could not cancel action {} due to entity not found exception.", action.getId(), e);
|
||||
}
|
||||
});
|
||||
if (cancelationType == CancelationType.FORCE) {
|
||||
actionRepository.findAll(ActionSpecifications.byDistributionSetIdAndActive(distributionSet.getId()))
|
||||
.forEach(action -> {
|
||||
try {
|
||||
assertTargetUpdateAllowed(action);
|
||||
forceQuitAction(action.getId());
|
||||
log.debug("Action {} force canceled", action.getId());
|
||||
} catch (final InsufficientPermissionException e) {
|
||||
log.trace("Could not cancel action {} due to insufficient permissions.", action.getId(), e);
|
||||
} catch (final EntityNotFoundException e) {
|
||||
log.trace("Could not cancel action {} due to entity not found exception.", action.getId(),
|
||||
e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTargetReadAllowed(final Long targetId) {
|
||||
if (!targetRepository.existsById(targetId)) {
|
||||
throw new EntityNotFoundException(Target.class, targetId);
|
||||
|
||||
@@ -97,6 +97,26 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetInvalidationCount countEntitiesForInvalidation(
|
||||
final DistributionSetInvalidation distributionSetInvalidation) {
|
||||
return systemSecurityContext.runAsSystem(() -> {
|
||||
final Collection<Long> setIds = distributionSetInvalidation.getDistributionSetIds();
|
||||
final long rolloutsCount = shouldRolloutsBeCanceled(distributionSetInvalidation.getCancelationType(),
|
||||
distributionSetInvalidation.isCancelRollouts()) ? countRolloutsForInvalidation(setIds) : 0;
|
||||
final long autoAssignmentsCount = countAutoAssignmentsForInvalidation(setIds);
|
||||
final long actionsCount = countActionsForInvalidation(setIds,
|
||||
distributionSetInvalidation.getCancelationType());
|
||||
|
||||
return new DistributionSetInvalidationCount(rolloutsCount, autoAssignmentsCount, actionsCount);
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean shouldRolloutsBeCanceled(final CancelationType cancelationType,
|
||||
final boolean cancelRollouts) {
|
||||
return cancelationType != CancelationType.NONE || cancelRollouts;
|
||||
}
|
||||
|
||||
private void invalidateDistributionSetsInTransaction(final DistributionSetInvalidation distributionSetInvalidation,
|
||||
final String tenant) {
|
||||
DeploymentHelper.runInNewTransaction(txManager, tenant + "-invalidateDS", status -> {
|
||||
@@ -135,26 +155,6 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean shouldRolloutsBeCanceled(final CancelationType cancelationType,
|
||||
final boolean cancelRollouts) {
|
||||
return cancelationType != CancelationType.NONE || cancelRollouts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetInvalidationCount countEntitiesForInvalidation(
|
||||
final DistributionSetInvalidation distributionSetInvalidation) {
|
||||
return systemSecurityContext.runAsSystem(() -> {
|
||||
final Collection<Long> setIds = distributionSetInvalidation.getDistributionSetIds();
|
||||
final long rolloutsCount = shouldRolloutsBeCanceled(distributionSetInvalidation.getCancelationType(),
|
||||
distributionSetInvalidation.isCancelRollouts()) ? countRolloutsForInvalidation(setIds) : 0;
|
||||
final long autoAssignmentsCount = countAutoAssignmentsForInvalidation(setIds);
|
||||
final long actionsCount = countActionsForInvalidation(setIds,
|
||||
distributionSetInvalidation.getCancelationType());
|
||||
|
||||
return new DistributionSetInvalidationCount(rolloutsCount, autoAssignmentsCount, actionsCount);
|
||||
});
|
||||
}
|
||||
|
||||
private long countRolloutsForInvalidation(final Collection<Long> setIds) {
|
||||
return setIds.stream().mapToLong(rolloutManagement::countByDistributionSetIdAndRolloutIsStoppable).sum();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,7 +27,6 @@ import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagCreate;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||
@@ -49,7 +48,6 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link TargetTagManagement}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Validated
|
||||
@@ -76,22 +74,11 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetTag update(final TagUpdate u) {
|
||||
final GenericTagUpdate update = (GenericTagUpdate) u;
|
||||
|
||||
final JpaDistributionSetTag tag = distributionSetTagRepository.findById(update.getId())
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, update.getId()));
|
||||
|
||||
update.getName().ifPresent(tag::setName);
|
||||
update.getDescription().ifPresent(tag::setDescription);
|
||||
update.getColour().ifPresent(tag::setColour);
|
||||
|
||||
return distributionSetTagRepository.save(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSetTag> getByName(final String name) {
|
||||
return distributionSetTagRepository.findByNameEquals(name);
|
||||
public List<DistributionSetTag> create(final Collection<TagCreate> dst) {
|
||||
final List<JpaDistributionSetTag> toCreate = dst.stream().map(JpaTagCreate.class::cast)
|
||||
.map(JpaTagCreate::buildDistributionSetTag).toList();
|
||||
return Collections
|
||||
.unmodifiableList(distributionSetTagRepository.saveAll(AccessController.Operation.CREATE, toCreate));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -107,47 +94,30 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public List<DistributionSetTag> create(final Collection<TagCreate> dst) {
|
||||
final List<JpaDistributionSetTag> toCreate = dst.stream().map(JpaTagCreate.class::cast)
|
||||
.map(JpaTagCreate::buildDistributionSetTag).toList();
|
||||
return Collections
|
||||
.unmodifiableList(distributionSetTagRepository.saveAll(AccessController.Operation.CREATE, toCreate));
|
||||
public DistributionSetTag update(final TagUpdate u) {
|
||||
final GenericTagUpdate update = (GenericTagUpdate) u;
|
||||
|
||||
final JpaDistributionSetTag tag = distributionSetTagRepository.findById(update.getId())
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, update.getId()));
|
||||
|
||||
update.getName().ifPresent(tag::setName);
|
||||
update.getDescription().ifPresent(tag::setDescription);
|
||||
update.getColour().ifPresent(tag::setColour);
|
||||
|
||||
return distributionSetTagRepository.save(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return distributionSetTagRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void delete(final String tagName) {
|
||||
final JpaDistributionSetTag dsTag = distributionSetTagRepository
|
||||
.findOne(DistributionSetTagSpecifications.byName(tagName))
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, tagName));
|
||||
|
||||
distributionSetTagRepository.delete(dsTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<DistributionSetTag> findAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetTagRepository, pageable, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSetTag> findByRsql(final Pageable pageable, final String rsqlParam) {
|
||||
final Specification<JpaDistributionSetTag> spec = RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetTagFields.class,
|
||||
virtualPropertyReplacer, database);
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(distributionSetTagRepository, pageable,
|
||||
Collections.singletonList(spec));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSetTag> findByDistributionSet(final Pageable pageable, final long distributionSetId) {
|
||||
if (!distributionSetRepository.existsById(distributionSetId)) {
|
||||
throw new EntityNotFoundException(DistributionSet.class, distributionSetId);
|
||||
}
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(distributionSetTagRepository, pageable,
|
||||
Collections.singletonList(TagSpecification.ofDistributionSet(distributionSetId)));
|
||||
public void delete(final long id) {
|
||||
distributionSetTagRepository.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -170,26 +140,54 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage
|
||||
return Collections.unmodifiableList(distributionSetTagRepository.findAllById(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final long id) {
|
||||
return distributionSetTagRepository.existsById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSetTag> get(final long id) {
|
||||
return distributionSetTagRepository.findById(id).map(DistributionSetTag.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<DistributionSetTag> findAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetTagRepository, pageable, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSetTag> findByRsql(final Pageable pageable, final String rsqlParam) {
|
||||
final Specification<JpaDistributionSetTag> spec = RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetTagFields.class,
|
||||
virtualPropertyReplacer, database);
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(distributionSetTagRepository, pageable,
|
||||
Collections.singletonList(spec));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void delete(final long id) {
|
||||
distributionSetTagRepository.deleteById(id);
|
||||
public void delete(final String tagName) {
|
||||
final JpaDistributionSetTag dsTag = distributionSetTagRepository
|
||||
.findOne(DistributionSetTagSpecifications.byName(tagName))
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, tagName));
|
||||
|
||||
distributionSetTagRepository.delete(dsTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final long id) {
|
||||
return distributionSetTagRepository.existsById(id);
|
||||
public Optional<DistributionSetTag> getByName(final String name) {
|
||||
return distributionSetTagRepository.findByNameEquals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return distributionSetTagRepository.count();
|
||||
public Page<DistributionSetTag> findByDistributionSet(final Pageable pageable, final long distributionSetId) {
|
||||
if (!distributionSetRepository.existsById(distributionSetId)) {
|
||||
throw new EntityNotFoundException(DistributionSet.class, distributionSetId);
|
||||
}
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(distributionSetTagRepository, pageable,
|
||||
Collections.singletonList(TagSpecification.ofDistributionSet(distributionSetId)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link DistributionSetTypeManagement}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Validated
|
||||
@@ -92,6 +91,72 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
|
||||
this.quotaManagement = quotaManagement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSetType> getByKey(final String key) {
|
||||
return distributionSetTypeRepository
|
||||
.findOne(DistributionSetTypeSpecification.byKey(key)).map(DistributionSetType.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSetType> getByName(final String name) {
|
||||
return distributionSetTypeRepository
|
||||
.findOne(DistributionSetTypeSpecification.byName(name)).map(DistributionSetType.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetType assignOptionalSoftwareModuleTypes(final long id,
|
||||
final Collection<Long> softwareModulesTypeIds) {
|
||||
return assignSoftwareModuleTypes(id, softwareModulesTypeIds, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetType assignMandatorySoftwareModuleTypes(final long id,
|
||||
final Collection<Long> softwareModuleTypeIds) {
|
||||
return assignSoftwareModuleTypes(id, softwareModuleTypeIds, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetType unassignSoftwareModuleType(final long id, final long softwareModuleTypeId) {
|
||||
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(id);
|
||||
|
||||
checkDistributionSetTypeNotAssigned(id);
|
||||
|
||||
type.removeModuleType(softwareModuleTypeId);
|
||||
|
||||
return distributionSetTypeRepository.save(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public List<DistributionSetType> create(final Collection<DistributionSetTypeCreate> types) {
|
||||
final List<JpaDistributionSetType> typesToCreate = types.stream().map(JpaDistributionSetTypeCreate.class::cast)
|
||||
.map(JpaDistributionSetTypeCreate::build).toList();
|
||||
|
||||
return Collections.unmodifiableList(
|
||||
distributionSetTypeRepository.saveAll(AccessController.Operation.CREATE, typesToCreate));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetType create(final DistributionSetTypeCreate c) {
|
||||
final JpaDistributionSetType distributionSetType = ((JpaDistributionSetTypeCreate) c).build();
|
||||
|
||||
return distributionSetTypeRepository.save(AccessController.Operation.CREATE, distributionSetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
@@ -130,15 +195,66 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
|
||||
return distributionSetTypeRepository.save(type);
|
||||
}
|
||||
|
||||
private void addModuleTypes(final Collection<Long> currentSmTypeIds, final Collection<Long> updatedSmTypeIds,
|
||||
final Function<SoftwareModuleType, JpaDistributionSetType> addModuleTypeCallback) {
|
||||
final Set<Long> smTypeIdsToAdd = updatedSmTypeIds.stream().filter(id -> !currentSmTypeIds.contains(id))
|
||||
.collect(Collectors.toSet());
|
||||
if (!CollectionUtils.isEmpty(smTypeIdsToAdd)) {
|
||||
softwareModuleTypeRepository.findAllById(smTypeIdsToAdd).forEach(addModuleTypeCallback::apply);
|
||||
@Override
|
||||
public long count() {
|
||||
return distributionSetTypeRepository.count(DistributionSetTypeSpecification.isNotDeleted());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void delete(final long id) {
|
||||
final JpaDistributionSetType toDelete = distributionSetTypeRepository.findById(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, id));
|
||||
|
||||
unassignDsTypeFromTargetTypes(id);
|
||||
|
||||
if (distributionSetRepository.countByTypeId(id) > 0) {
|
||||
toDelete.setDeleted(true);
|
||||
distributionSetTypeRepository.save(AccessController.Operation.DELETE, toDelete);
|
||||
} else {
|
||||
distributionSetTypeRepository.deleteById(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void delete(final Collection<Long> ids) {
|
||||
distributionSetTypeRepository.deleteAllById(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DistributionSetType> get(final Collection<Long> ids) {
|
||||
return Collections.unmodifiableList(distributionSetTypeRepository.findAllById(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final long id) {
|
||||
return distributionSetTypeRepository.existsById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSetType> get(final long id) {
|
||||
return distributionSetTypeRepository.findById(id).map(DistributionSetType.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<DistributionSetType> findAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetTypeRepository, pageable, List.of(
|
||||
DistributionSetTypeSpecification.isNotDeleted()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSetType> findByRsql(final Pageable pageable, final String rsqlParam) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(distributionSetTypeRepository, pageable, List.of(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetTypeFields.class, virtualPropertyReplacer,
|
||||
database),
|
||||
DistributionSetTypeSpecification.isNotDeleted()));
|
||||
}
|
||||
|
||||
private static void removeModuleTypes(final Collection<Long> currentSmTypeIds,
|
||||
final Collection<Long> updatedSmTypeIds,
|
||||
final LongFunction<JpaDistributionSetType> removeModuleTypeCallback) {
|
||||
@@ -149,22 +265,17 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetType assignMandatorySoftwareModuleTypes(final long id,
|
||||
final Collection<Long> softwareModuleTypeIds) {
|
||||
return assignSoftwareModuleTypes(id, softwareModuleTypeIds, true);
|
||||
private static boolean hasModuleChanges(final GenericDistributionSetTypeUpdate update) {
|
||||
return update.getOptional().isPresent() || update.getMandatory().isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetType assignOptionalSoftwareModuleTypes(final long id,
|
||||
final Collection<Long> softwareModulesTypeIds) {
|
||||
return assignSoftwareModuleTypes(id, softwareModulesTypeIds, false);
|
||||
private void addModuleTypes(final Collection<Long> currentSmTypeIds, final Collection<Long> updatedSmTypeIds,
|
||||
final Function<SoftwareModuleType, JpaDistributionSetType> addModuleTypeCallback) {
|
||||
final Set<Long> smTypeIdsToAdd = updatedSmTypeIds.stream().filter(id -> !currentSmTypeIds.contains(id))
|
||||
.collect(Collectors.toSet());
|
||||
if (!CollectionUtils.isEmpty(smTypeIdsToAdd)) {
|
||||
softwareModuleTypeRepository.findAllById(smTypeIdsToAdd).forEach(addModuleTypeCallback::apply);
|
||||
}
|
||||
}
|
||||
|
||||
private DistributionSetType assignSoftwareModuleTypes(
|
||||
@@ -189,14 +300,10 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
|
||||
/**
|
||||
* Enforces the quota specifying the maximum number of
|
||||
* {@link SoftwareModuleType}s per {@link DistributionSetType}.
|
||||
*
|
||||
* @param id
|
||||
* of the distribution set type
|
||||
* @param requested
|
||||
* number of software module types to check
|
||||
*
|
||||
* @throws AssignmentQuotaExceededException
|
||||
* if the software module type quota is exceeded
|
||||
*
|
||||
* @param id of the distribution set type
|
||||
* @param requested number of software module types to check
|
||||
* @throws AssignmentQuotaExceededException if the software module type quota is exceeded
|
||||
*/
|
||||
private void assertSoftwareModuleTypeQuota(final long id, final int requested) {
|
||||
QuotaHelper.assertAssignmentQuota(id, requested,
|
||||
@@ -204,79 +311,6 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
|
||||
DistributionSetType.class, distributionSetTypeRepository::countSmTypesById);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetType unassignSoftwareModuleType(final long id, final long softwareModuleTypeId) {
|
||||
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(id);
|
||||
|
||||
checkDistributionSetTypeNotAssigned(id);
|
||||
|
||||
type.removeModuleType(softwareModuleTypeId);
|
||||
|
||||
return distributionSetTypeRepository.save(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSetType> findByRsql(final Pageable pageable, final String rsqlParam) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(distributionSetTypeRepository, pageable, List.of(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetTypeFields.class, virtualPropertyReplacer,
|
||||
database),
|
||||
DistributionSetTypeSpecification.isNotDeleted()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<DistributionSetType> findAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetTypeRepository, pageable, List.of(
|
||||
DistributionSetTypeSpecification.isNotDeleted()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return distributionSetTypeRepository.count(DistributionSetTypeSpecification.isNotDeleted());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSetType> getByName(final String name) {
|
||||
return distributionSetTypeRepository
|
||||
.findOne(DistributionSetTypeSpecification.byName(name)).map(DistributionSetType.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSetType> getByKey(final String key) {
|
||||
return distributionSetTypeRepository
|
||||
.findOne(DistributionSetTypeSpecification.byKey(key)).map(DistributionSetType.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetType create(final DistributionSetTypeCreate c) {
|
||||
final JpaDistributionSetType distributionSetType = ((JpaDistributionSetTypeCreate) c).build();
|
||||
|
||||
return distributionSetTypeRepository.save(AccessController.Operation.CREATE, distributionSetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void delete(final long id) {
|
||||
final JpaDistributionSetType toDelete = distributionSetTypeRepository.findById(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, id));
|
||||
|
||||
unassignDsTypeFromTargetTypes(id);
|
||||
|
||||
if (distributionSetRepository.countByTypeId(id) > 0) {
|
||||
toDelete.setDeleted(true);
|
||||
distributionSetTypeRepository.save(AccessController.Operation.DELETE, toDelete);
|
||||
} else {
|
||||
distributionSetTypeRepository.deleteById(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void unassignDsTypeFromTargetTypes(final long typeId) {
|
||||
final List<JpaTargetType> targetTypesByDsType = targetTypeRepository.findByDsType(typeId);
|
||||
targetTypesByDsType.forEach(targetType -> {
|
||||
@@ -285,54 +319,15 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public List<DistributionSetType> create(final Collection<DistributionSetTypeCreate> types) {
|
||||
final List<JpaDistributionSetType> typesToCreate = types.stream().map(JpaDistributionSetTypeCreate.class::cast)
|
||||
.map(JpaDistributionSetTypeCreate::build).toList();
|
||||
|
||||
return Collections.unmodifiableList(
|
||||
distributionSetTypeRepository.saveAll(AccessController.Operation.CREATE, typesToCreate));
|
||||
}
|
||||
|
||||
private JpaDistributionSetType findDistributionSetTypeAndThrowExceptionIfNotFound(final Long setId) {
|
||||
return (JpaDistributionSetType) get(setId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, setId));
|
||||
}
|
||||
|
||||
private static boolean hasModuleChanges(final GenericDistributionSetTypeUpdate update) {
|
||||
return update.getOptional().isPresent() || update.getMandatory().isPresent();
|
||||
}
|
||||
|
||||
private void checkDistributionSetTypeNotAssigned(final Long id) {
|
||||
if (distributionSetRepository.countByTypeId(id) > 0) {
|
||||
throw new EntityReadOnlyException(String.format(
|
||||
"Distribution set type %s is already assigned to distribution sets and cannot be changed!", id));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void delete(final Collection<Long> ids) {
|
||||
distributionSetTypeRepository.deleteAllById(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DistributionSetType> get(final Collection<Long> ids) {
|
||||
return Collections.unmodifiableList(distributionSetTypeRepository.findAllById(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DistributionSetType> get(final long id) {
|
||||
return distributionSetTypeRepository.findById(id).map(DistributionSetType.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final long id) {
|
||||
return distributionSetTypeRepository.existsById(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,10 +91,10 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
private final Database database;
|
||||
|
||||
public JpaRolloutGroupManagement(final RolloutGroupRepository rolloutGroupRepository,
|
||||
final RolloutRepository rolloutRepository, final ActionRepository actionRepository,
|
||||
final TargetRepository targetRepository, final EntityManager entityManager,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final RolloutStatusCache rolloutStatusCache,
|
||||
final Database database) {
|
||||
final RolloutRepository rolloutRepository, final ActionRepository actionRepository,
|
||||
final TargetRepository targetRepository, final EntityManager entityManager,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final RolloutStatusCache rolloutStatusCache,
|
||||
final Database database) {
|
||||
this.rolloutGroupRepository = rolloutGroupRepository;
|
||||
this.rolloutRepository = rolloutRepository;
|
||||
this.actionRepository = actionRepository;
|
||||
@@ -105,37 +105,6 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RolloutGroup> get(final long rolloutGroupId) {
|
||||
return rolloutGroupRepository.findById(rolloutGroupId).map(RolloutGroup.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<RolloutGroup> findByRollout(final Pageable pageable, final long rolloutId) {
|
||||
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
|
||||
|
||||
return JpaManagementHelper.convertPage(rolloutGroupRepository.findByRolloutId(rolloutId, pageable), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<RolloutGroup> findByRolloutAndRsql(final Pageable pageable, final long rolloutId,
|
||||
final String rsqlParam) {
|
||||
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
|
||||
|
||||
final List<Specification<JpaRolloutGroup>> specList = Arrays.asList(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, RolloutGroupFields.class, virtualPropertyReplacer,
|
||||
database),
|
||||
(root, query, cb) -> cb.equal(root.get(JpaRolloutGroup_.rollout).get(JpaRollout_.id), rolloutId));
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(rolloutGroupRepository, pageable, specList);
|
||||
}
|
||||
|
||||
private void throwEntityNotFoundExceptionIfRolloutDoesNotExist(final Long rolloutId) {
|
||||
if (!rolloutRepository.existsById(rolloutId)) {
|
||||
throw new EntityNotFoundException(Rollout.class, rolloutId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<RolloutGroup> findByRolloutWithDetailedStatus(final Pageable pageable, final long rolloutId) {
|
||||
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
|
||||
@@ -154,7 +123,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
|
||||
for (final JpaRolloutGroup rolloutGroup : rolloutGroups) {
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||
allStatesForRollout.get(rolloutGroup.getId()), (long)rolloutGroup.getTotalTargets(),
|
||||
allStatesForRollout.get(rolloutGroup.getId()), (long) rolloutGroup.getTotalTargets(),
|
||||
rolloutGroup.getRollout().getActionType());
|
||||
rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
}
|
||||
@@ -162,6 +131,49 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
return JpaManagementHelper.convertPage(rolloutGroups, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetWithActionStatus> findAllTargetsOfRolloutGroupWithActionStatus(final Pageable pageRequest,
|
||||
final long rolloutGroupId) {
|
||||
|
||||
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
|
||||
final Root<RolloutTargetGroup> targetRoot = query.distinct(true).from(RolloutTargetGroup.class);
|
||||
final Join<RolloutTargetGroup, JpaTarget> targetJoin = targetRoot.join(RolloutTargetGroup_.target);
|
||||
final ListJoin<RolloutTargetGroup, JpaAction> actionJoin = targetRoot.join(RolloutTargetGroup_.actions,
|
||||
JoinType.LEFT);
|
||||
|
||||
final CriteriaQuery<Object[]> multiselect = query
|
||||
.multiselect(targetJoin, actionJoin.get(JpaAction_.status),
|
||||
actionJoin.get(JpaAction_.lastActionStatusCode))
|
||||
.where(getRolloutGroupTargetWithRolloutGroupJoinCondition(rolloutGroupId, cb, targetRoot))
|
||||
.orderBy(getOrderBy(pageRequest, cb, targetJoin, actionJoin));
|
||||
final List<TargetWithActionStatus> targetWithActionStatus = entityManager.createQuery(multiselect)
|
||||
.setFirstResult((int) pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList()
|
||||
.stream().map(this::getTargetWithActionStatusFromQuery).collect(Collectors.toList());
|
||||
|
||||
return new PageImpl<>(targetWithActionStatus, pageRequest, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RolloutGroup> get(final long rolloutGroupId) {
|
||||
return rolloutGroupRepository.findById(rolloutGroupId).map(RolloutGroup.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<RolloutGroup> findByRolloutAndRsql(final Pageable pageable, final long rolloutId,
|
||||
final String rsqlParam) {
|
||||
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
|
||||
|
||||
final List<Specification<JpaRolloutGroup>> specList = Arrays.asList(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, RolloutGroupFields.class, virtualPropertyReplacer,
|
||||
database),
|
||||
(root, query, cb) -> cb.equal(root.get(JpaRolloutGroup_.rollout).get(JpaRollout_.id), rolloutId));
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(rolloutGroupRepository, pageable, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<RolloutGroup> findByRolloutAndRsqlWithDetailedStatus(final Pageable pageable, final long rolloutId,
|
||||
final String rsqlParam) {
|
||||
@@ -189,6 +201,53 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
return JpaManagementHelper.convertPage(rolloutGroups, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<RolloutGroup> findByRollout(final Pageable pageable, final long rolloutId) {
|
||||
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
|
||||
|
||||
return JpaManagementHelper.convertPage(rolloutGroupRepository.findByRolloutId(rolloutId, pageable), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByRollout(final long rolloutId) {
|
||||
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
|
||||
|
||||
return rolloutGroupRepository.countByRolloutId(rolloutId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findTargetsOfRolloutGroup(final Pageable page, final long rolloutGroupId) {
|
||||
final JpaRolloutGroup rolloutGroup = rolloutGroupRepository.findById(rolloutGroupId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(RolloutGroup.class, rolloutGroupId));
|
||||
|
||||
if (isRolloutStatusReady(rolloutGroup)) {
|
||||
// in case of status ready the action has not been created yet and
|
||||
// the relation information between target and rollout-group is
|
||||
// stored in the #TargetRolloutGroup.
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, page,
|
||||
Collections.singletonList(TargetSpecifications.isInRolloutGroup(rolloutGroupId)));
|
||||
}
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, page,
|
||||
Collections.singletonList(TargetSpecifications.isInActionRolloutGroup(rolloutGroupId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findTargetsOfRolloutGroupByRsql(final Pageable pageable, final long rolloutGroupId,
|
||||
final String rsqlParam) {
|
||||
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
|
||||
|
||||
final List<Specification<JpaTarget>> specList = Arrays.asList(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer, database),
|
||||
(root, query, cb) -> {
|
||||
final ListJoin<JpaTarget, RolloutTargetGroup> rolloutTargetJoin = root
|
||||
.join(JpaTarget_.rolloutTargetGroup);
|
||||
return cb.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup).get(JpaRolloutGroup_.id),
|
||||
rolloutGroupId);
|
||||
});
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageable, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RolloutGroup> getWithDetailedStatus(final long rolloutGroupId) {
|
||||
final Optional<RolloutGroup> rolloutGroup = get(rolloutGroupId);
|
||||
@@ -214,6 +273,28 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countTargetsOfRolloutsGroup(final long rolloutGroupId) {
|
||||
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
|
||||
final Root<RolloutTargetGroup> countQueryFrom = countQuery.from(RolloutTargetGroup.class);
|
||||
countQuery.select(cb.count(countQueryFrom))
|
||||
.where(getRolloutGroupTargetWithRolloutGroupJoinCondition(rolloutGroupId, cb, countQueryFrom));
|
||||
return entityManager.createQuery(countQuery).getSingleResult();
|
||||
}
|
||||
|
||||
private static boolean isRolloutStatusReady(final RolloutGroup rolloutGroup) {
|
||||
return rolloutGroup != null && (RolloutStatus.READY == rolloutGroup.getRollout().getStatus());
|
||||
}
|
||||
|
||||
private void throwEntityNotFoundExceptionIfRolloutDoesNotExist(final Long rolloutId) {
|
||||
if (!rolloutRepository.existsById(rolloutId)) {
|
||||
throw new EntityNotFoundException(Rollout.class, rolloutId);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRolloutGroup(final List<Long> groupIds) {
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> fromCache = rolloutStatusCache
|
||||
.getRolloutGroupStatus(groupIds);
|
||||
@@ -235,68 +316,6 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
return fromCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findTargetsOfRolloutGroupByRsql(final Pageable pageable, final long rolloutGroupId,
|
||||
final String rsqlParam) {
|
||||
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
|
||||
|
||||
final List<Specification<JpaTarget>> specList = Arrays.asList(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer, database),
|
||||
(root, query, cb) -> {
|
||||
final ListJoin<JpaTarget, RolloutTargetGroup> rolloutTargetJoin = root
|
||||
.join(JpaTarget_.rolloutTargetGroup);
|
||||
return cb.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup).get(JpaRolloutGroup_.id),
|
||||
rolloutGroupId);
|
||||
});
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageable, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findTargetsOfRolloutGroup(final Pageable page, final long rolloutGroupId) {
|
||||
final JpaRolloutGroup rolloutGroup = rolloutGroupRepository.findById(rolloutGroupId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(RolloutGroup.class, rolloutGroupId));
|
||||
|
||||
if (isRolloutStatusReady(rolloutGroup)) {
|
||||
// in case of status ready the action has not been created yet and
|
||||
// the relation information between target and rollout-group is
|
||||
// stored in the #TargetRolloutGroup.
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, page,
|
||||
Collections.singletonList(TargetSpecifications.isInRolloutGroup(rolloutGroupId)));
|
||||
}
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, page,
|
||||
Collections.singletonList(TargetSpecifications.isInActionRolloutGroup(rolloutGroupId)));
|
||||
}
|
||||
|
||||
private static boolean isRolloutStatusReady(final RolloutGroup rolloutGroup) {
|
||||
return rolloutGroup != null && (RolloutStatus.READY == rolloutGroup.getRollout().getStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetWithActionStatus> findAllTargetsOfRolloutGroupWithActionStatus(final Pageable pageRequest,
|
||||
final long rolloutGroupId) {
|
||||
|
||||
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
|
||||
final Root<RolloutTargetGroup> targetRoot = query.distinct(true).from(RolloutTargetGroup.class);
|
||||
final Join<RolloutTargetGroup, JpaTarget> targetJoin = targetRoot.join(RolloutTargetGroup_.target);
|
||||
final ListJoin<RolloutTargetGroup, JpaAction> actionJoin = targetRoot.join(RolloutTargetGroup_.actions,
|
||||
JoinType.LEFT);
|
||||
|
||||
final CriteriaQuery<Object[]> multiselect = query
|
||||
.multiselect(targetJoin, actionJoin.get(JpaAction_.status),
|
||||
actionJoin.get(JpaAction_.lastActionStatusCode))
|
||||
.where(getRolloutGroupTargetWithRolloutGroupJoinCondition(rolloutGroupId, cb, targetRoot))
|
||||
.orderBy(getOrderBy(pageRequest, cb, targetJoin, actionJoin));
|
||||
final List<TargetWithActionStatus> targetWithActionStatus = entityManager.createQuery(multiselect)
|
||||
.setFirstResult((int) pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList()
|
||||
.stream().map(this::getTargetWithActionStatusFromQuery).collect(Collectors.toList());
|
||||
|
||||
return new PageImpl<>(targetWithActionStatus, pageRequest, 0);
|
||||
}
|
||||
|
||||
private Predicate getRolloutGroupTargetWithRolloutGroupJoinCondition(final long rolloutGroupId,
|
||||
final CriteriaBuilder cb, final Root<RolloutTargetGroup> targetRoot) {
|
||||
return cb.equal(targetRoot.get(RolloutTargetGroup_.rolloutGroup).get(JpaRolloutGroup_.id), //
|
||||
@@ -327,29 +346,10 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countTargetsOfRolloutsGroup(final long rolloutGroupId) {
|
||||
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
|
||||
final Root<RolloutTargetGroup> countQueryFrom = countQuery.from(RolloutTargetGroup.class);
|
||||
countQuery.select(cb.count(countQueryFrom))
|
||||
.where(getRolloutGroupTargetWithRolloutGroupJoinCondition(rolloutGroupId, cb, countQueryFrom));
|
||||
return entityManager.createQuery(countQuery).getSingleResult();
|
||||
}
|
||||
|
||||
private void throwExceptionIfRolloutGroupDoesNotExist(final Long rolloutGroupId) {
|
||||
if (!rolloutGroupRepository.existsById(rolloutGroupId)) {
|
||||
throw new EntityNotFoundException(RolloutGroup.class, rolloutGroupId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByRollout(final long rolloutId) {
|
||||
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
|
||||
|
||||
return rolloutGroupRepository.countByRolloutId(rolloutId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,9 +24,10 @@ import java.util.stream.Collectors;
|
||||
import jakarta.validation.ConstraintDeclarationException;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.ValidationException;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.ContextAware;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
@@ -37,7 +38,6 @@ import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutStatusCache;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.ContextAware;
|
||||
import org.eclipse.hawkbit.repository.builder.DynamicRolloutGroupTemplate;
|
||||
import org.eclipse.hawkbit.repository.builder.GenericRolloutUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
|
||||
@@ -110,31 +110,6 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
private static final List<RolloutStatus> ROLLOUT_STATUS_STOPPABLE = Arrays.asList(RolloutStatus.RUNNING,
|
||||
RolloutStatus.CREATING, RolloutStatus.PAUSED, RolloutStatus.READY, RolloutStatus.STARTING,
|
||||
RolloutStatus.WAITING_FOR_APPROVAL, RolloutStatus.APPROVAL_DENIED);
|
||||
|
||||
@Autowired
|
||||
private RepositoryProperties repositoryProperties;
|
||||
|
||||
@Autowired
|
||||
private RolloutRepository rolloutRepository;
|
||||
|
||||
@Autowired
|
||||
private RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
|
||||
@Autowired
|
||||
private AfterTransactionCommitExecutor afterCommit;
|
||||
|
||||
@Autowired
|
||||
private QuotaManagement quotaManagement;
|
||||
|
||||
@Autowired
|
||||
private RolloutStatusCache rolloutStatusCache;
|
||||
|
||||
@Autowired
|
||||
private StartNextGroupRolloutGroupSuccessAction startNextRolloutGroupAction;
|
||||
|
||||
private final TargetManagement targetManagement;
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
private final VirtualPropertyReplacer virtualPropertyReplacer;
|
||||
@@ -144,6 +119,22 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
private final EventPublisherHolder eventPublisherHolder;
|
||||
private final ContextAware contextAware;
|
||||
private final Database database;
|
||||
@Autowired
|
||||
private RepositoryProperties repositoryProperties;
|
||||
@Autowired
|
||||
private RolloutRepository rolloutRepository;
|
||||
@Autowired
|
||||
private RolloutGroupRepository rolloutGroupRepository;
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
@Autowired
|
||||
private AfterTransactionCommitExecutor afterCommit;
|
||||
@Autowired
|
||||
private QuotaManagement quotaManagement;
|
||||
@Autowired
|
||||
private RolloutStatusCache rolloutStatusCache;
|
||||
@Autowired
|
||||
private StartNextGroupRolloutGroupSuccessAction startNextRolloutGroupAction;
|
||||
|
||||
public JpaRolloutManagement(final TargetManagement targetManagement,
|
||||
final DistributionSetManagement distributionSetManagement, final EventPublisherHolder eventPublisherHolder,
|
||||
@@ -163,34 +154,29 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
this.contextAware = contextAware;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Rollout> findAll(final Pageable pageable, final boolean deleted) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(rolloutRepository, pageable, Collections
|
||||
.singletonList(RolloutSpecification.isDeletedWithDistributionSet(deleted, pageable.getSort())));
|
||||
public static String createRolloutLockKey(final String tenant) {
|
||||
return tenant + "-rollout";
|
||||
}
|
||||
|
||||
public void publishRolloutGroupCreatedEventAfterCommit(final RolloutGroup group, final Rollout rollout) {
|
||||
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher().publishEvent(
|
||||
new RolloutGroupCreatedEvent(group, rollout.getId(), eventPublisherHolder.getApplicationId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Rollout> findByRsql(final Pageable pageable, final String rsqlParam, final boolean deleted) {
|
||||
final List<Specification<JpaRollout>> specList = new ArrayList<>(2);
|
||||
specList.add(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, RolloutFields.class, virtualPropertyReplacer, database));
|
||||
specList.add(RolloutSpecification.isDeletedWithDistributionSet(deleted, pageable.getSort()));
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(rolloutRepository, pageable, specList);
|
||||
public long count() {
|
||||
return rolloutRepository.count(
|
||||
RolloutSpecification.isDeletedWithDistributionSet(false, Sort.by(Direction.DESC, JpaRollout_.ID)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Rollout> get(final long rolloutId) {
|
||||
return rolloutRepository.findById(rolloutId).map(Rollout.class::cast);
|
||||
public long countByFilters(final String searchText) {
|
||||
return rolloutRepository.count(RolloutSpecification.likeName(searchText, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Rollout create(@NotNull @Valid RolloutCreate create, int amountGroup, boolean confirmationRequired,
|
||||
@NotNull RolloutGroupConditions conditions) {
|
||||
return create(create, amountGroup, confirmationRequired, conditions, null);
|
||||
public long countByDistributionSetIdAndRolloutIsStoppable(final long setId) {
|
||||
return rolloutRepository.countByDistributionSetIdAndStatusIn(setId, ROLLOUT_STATUS_STOPPABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -204,7 +190,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
} else if (amountGroup == 0) {
|
||||
if (dynamicRolloutGroupTemplate == null) {
|
||||
throw new ValidationException(
|
||||
"When amount of groups is 0, the rollouts shall be dynamic and a dynamic group template must be provided");
|
||||
"When amount of groups is 0, the rollouts shall be dynamic and a dynamic group template must be provided");
|
||||
}
|
||||
} else {
|
||||
RolloutHelper.verifyRolloutGroupAmount(amountGroup, quotaManagement);
|
||||
@@ -219,6 +205,15 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
return createRolloutGroups(amountGroup, conditions, savedRollout, confirmationRequired, dynamicRolloutGroupTemplate);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Rollout create(@NotNull @Valid RolloutCreate create, int amountGroup, boolean confirmationRequired,
|
||||
@NotNull RolloutGroupConditions conditions) {
|
||||
return create(create, amountGroup, confirmationRequired, conditions, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
@@ -234,6 +229,314 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
return createRolloutGroups(groups, conditions, savedRollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(final List<RolloutGroupCreate> groups,
|
||||
final String targetFilter, final Long createdAt, final Long dsTypeId) {
|
||||
|
||||
final TargetCount targets = calculateTargets(targetFilter, createdAt, dsTypeId);
|
||||
long totalTargets = targets.total();
|
||||
final String baseFilter = targets.filter();
|
||||
|
||||
if (totalTargets == 0) {
|
||||
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
|
||||
}
|
||||
|
||||
return new AsyncResult<>(
|
||||
validateTargetsInGroups(groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()),
|
||||
baseFilter, totalTargets, dsTypeId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Rollout> findAll(final Pageable pageable, final boolean deleted) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(rolloutRepository, pageable, Collections
|
||||
.singletonList(RolloutSpecification.isDeletedWithDistributionSet(deleted, pageable.getSort())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Rollout> findAllWithDetailedStatus(final Pageable pageable, final boolean deleted) {
|
||||
final Slice<Rollout> rollouts = JpaManagementHelper.findAllWithoutCountBySpec(rolloutRepository, pageable,
|
||||
Collections
|
||||
.singletonList(RolloutSpecification.isDeletedWithDistributionSet(deleted, pageable.getSort())));
|
||||
setRolloutStatusDetails(rollouts);
|
||||
return rollouts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Rollout> findByRsql(final Pageable pageable, final String rsqlParam, final boolean deleted) {
|
||||
final List<Specification<JpaRollout>> specList = new ArrayList<>(2);
|
||||
specList.add(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, RolloutFields.class, virtualPropertyReplacer, database));
|
||||
specList.add(RolloutSpecification.isDeletedWithDistributionSet(deleted, pageable.getSort()));
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(rolloutRepository, pageable, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Rollout> findByFiltersWithDetailedStatus(final Pageable pageable, final String searchText,
|
||||
final boolean deleted) {
|
||||
final Slice<Rollout> findAll = JpaManagementHelper.findAllWithoutCountBySpec(rolloutRepository, pageable,
|
||||
Collections.singletonList(RolloutSpecification.likeName(searchText, deleted)));
|
||||
setRolloutStatusDetails(findAll);
|
||||
return findAll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> findActiveRollouts() {
|
||||
return rolloutRepository.findByStatusIn(ACTIVE_ROLLOUTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Rollout> get(final long rolloutId) {
|
||||
return rolloutRepository.findById(rolloutId).map(Rollout.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Rollout> getByName(final String rolloutName) {
|
||||
return rolloutRepository.findByName(rolloutName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Rollout> getWithDetailedStatus(final long rolloutId) {
|
||||
final Optional<Rollout> rollout = get(rolloutId);
|
||||
|
||||
if (!rollout.isPresent()) {
|
||||
return rollout;
|
||||
}
|
||||
|
||||
List<TotalTargetCountActionStatus> rolloutStatusCountItems = rolloutStatusCache.getRolloutStatus(rolloutId);
|
||||
|
||||
if (CollectionUtils.isEmpty(rolloutStatusCountItems)) {
|
||||
rolloutStatusCountItems = actionRepository.getStatusCountByRolloutId(rolloutId);
|
||||
rolloutStatusCache.putRolloutStatus(rolloutId, rolloutStatusCountItems);
|
||||
}
|
||||
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
|
||||
rollout.get().getTotalTargets(), rollout.get().getActionType());
|
||||
((JpaRollout) rollout.get()).setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
return rollout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final long rolloutId) {
|
||||
return rolloutRepository.existsById(rolloutId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void pauseRollout(final long rolloutId) {
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
|
||||
if (RolloutStatus.RUNNING != rollout.getStatus()) {
|
||||
throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is "
|
||||
+ rollout.getStatus().name().toLowerCase());
|
||||
}
|
||||
// setting the complete rollout only in paused state. This is sufficient
|
||||
// due the currently running groups will be completed and new groups are
|
||||
// not started until rollout goes back to running state again. The
|
||||
// periodically check for running rollouts will skip rollouts in pause
|
||||
// state.
|
||||
rollout.setStatus(RolloutStatus.PAUSED);
|
||||
rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void resumeRollout(final long rolloutId) {
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
|
||||
if (RolloutStatus.PAUSED != rollout.getStatus()) {
|
||||
throw new RolloutIllegalStateException("Rollout can only be resumed in state paused but current state is "
|
||||
+ rollout.getStatus().name().toLowerCase());
|
||||
}
|
||||
rollout.setStatus(RolloutStatus.RUNNING);
|
||||
rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Rollout approveOrDeny(final long rolloutId, final Rollout.ApprovalDecision decision) {
|
||||
return this.approveOrDeny(rolloutId, decision, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Rollout approveOrDeny(final long rolloutId, final Rollout.ApprovalDecision decision, final String remark) {
|
||||
log.debug("approveOrDeny rollout called for rollout {} with decision {}", rolloutId, decision);
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
|
||||
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.WAITING_FOR_APPROVAL);
|
||||
switch (decision) {
|
||||
case APPROVED:
|
||||
rollout.setStatus(RolloutStatus.READY);
|
||||
break;
|
||||
case DENIED:
|
||||
rollout.setStatus(RolloutStatus.APPROVAL_DENIED);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown approval decision: " + decision);
|
||||
}
|
||||
rollout.setApprovalDecidedBy(rolloutApprovalStrategy.getApprovalUser(rollout));
|
||||
if (remark != null) {
|
||||
rollout.setApprovalRemark(remark);
|
||||
}
|
||||
return rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Rollout start(final long rolloutId) {
|
||||
log.debug("startRollout called for rollout {}", rolloutId);
|
||||
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
|
||||
RolloutHelper.checkIfRolloutCanStarted(rollout, rollout);
|
||||
rollout.setStatus(RolloutStatus.STARTING);
|
||||
rollout.setLastCheck(0);
|
||||
return rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Rollout update(final RolloutUpdate u) {
|
||||
final GenericRolloutUpdate update = (GenericRolloutUpdate) u;
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(update.getId());
|
||||
|
||||
checkIfDeleted(update.getId(), rollout.getStatus());
|
||||
|
||||
update.getName().ifPresent(rollout::setName);
|
||||
update.getDescription().ifPresent(rollout::setDescription);
|
||||
return rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void delete(final long rolloutId) {
|
||||
final JpaRollout jpaRollout = rolloutRepository.findById(rolloutId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Rollout.class, rolloutId));
|
||||
|
||||
if (jpaRollout == null) {
|
||||
throw new EntityNotFoundException(Rollout.class, rolloutId);
|
||||
}
|
||||
|
||||
if (RolloutStatus.DELETING == jpaRollout.getStatus()) {
|
||||
return;
|
||||
}
|
||||
|
||||
jpaRollout.setStatus(RolloutStatus.DELETING);
|
||||
rolloutRepository.save(jpaRollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelRolloutsForDistributionSet(final DistributionSet set) {
|
||||
// stop all rollouts for this distribution set
|
||||
rolloutRepository.findByDistributionSetAndStatusIn(set, ROLLOUT_STATUS_STOPPABLE).forEach(rollout -> {
|
||||
final JpaRollout jpaRollout = (JpaRollout) rollout;
|
||||
jpaRollout.setStatus(RolloutStatus.STOPPING);
|
||||
rolloutRepository.save(jpaRollout);
|
||||
log.debug("Rollout {} stopped", jpaRollout.getId());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void triggerNextGroup(final long rolloutId) {
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
|
||||
if (RolloutStatus.RUNNING != rollout.getStatus()) {
|
||||
throw new RolloutIllegalStateException("Rollout is not in running state");
|
||||
}
|
||||
final List<RolloutGroup> groups = rollout.getRolloutGroups();
|
||||
|
||||
final boolean isNextGroupTriggerable = groups.stream()
|
||||
.anyMatch(g -> RolloutGroupStatus.SCHEDULED.equals(g.getStatus()));
|
||||
|
||||
if (!isNextGroupTriggerable) {
|
||||
throw new RolloutIllegalStateException("Rollout does not have any groups left to be triggered");
|
||||
}
|
||||
|
||||
final RolloutGroup latestRunning = groups.stream()
|
||||
.sorted(Comparator.comparingLong(RolloutGroup::getId).reversed())
|
||||
.filter(g -> RolloutGroupStatus.RUNNING.equals(g.getStatus()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RolloutIllegalStateException("No group is running"));
|
||||
|
||||
startNextRolloutGroupAction.exec(rollout, latestRunning);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRolloutStatusDetails(final Slice<Rollout> rollouts) {
|
||||
final List<Long> rolloutIds = rollouts.getContent().stream().map(Rollout::getId).collect(Collectors.toList());
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRollout(
|
||||
rolloutIds);
|
||||
|
||||
if (allStatesForRollout != null) {
|
||||
rollouts.forEach(rollout -> {
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets(), rollout.getActionType());
|
||||
((JpaRollout) rollout).setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In case the given group is missing conditions or actions, they will be set
|
||||
* from the supplied default conditions.
|
||||
*
|
||||
* @param create group to check
|
||||
* @param conditions default conditions and actions
|
||||
*/
|
||||
private static JpaRolloutGroup prepareRolloutGroupWithDefaultConditions(final RolloutGroupCreate create,
|
||||
final RolloutGroupConditions conditions) {
|
||||
final JpaRolloutGroup group = ((JpaRolloutGroupCreate) create).build();
|
||||
|
||||
if (group.getSuccessCondition() == null) {
|
||||
group.setSuccessCondition(conditions.getSuccessCondition());
|
||||
}
|
||||
if (group.getSuccessConditionExp() == null) {
|
||||
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
|
||||
}
|
||||
if (group.getSuccessAction() == null) {
|
||||
group.setSuccessAction(conditions.getSuccessAction());
|
||||
}
|
||||
if (group.getSuccessActionExp() == null) {
|
||||
group.setSuccessActionExp(conditions.getSuccessActionExp());
|
||||
}
|
||||
|
||||
if (group.getErrorCondition() == null) {
|
||||
group.setErrorCondition(conditions.getErrorCondition());
|
||||
}
|
||||
if (group.getErrorConditionExp() == null) {
|
||||
group.setErrorConditionExp(conditions.getErrorConditionExp());
|
||||
}
|
||||
if (group.getErrorAction() == null) {
|
||||
group.setErrorAction(conditions.getErrorAction());
|
||||
}
|
||||
if (group.getErrorActionExp() == null) {
|
||||
group.setErrorActionExp(conditions.getErrorActionExp());
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
private static void checkIfDeleted(final Long rolloutId, final RolloutStatus status) {
|
||||
if (RolloutStatus.DELETING == status || RolloutStatus.DELETED == status) {
|
||||
throw new EntityReadOnlyException("Rollout " + rolloutId + " is soft deleted and cannot be changed");
|
||||
}
|
||||
}
|
||||
|
||||
private JpaRollout createRollout(final JpaRollout rollout, final boolean pureDynamic) {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(rollout);
|
||||
final JpaDistributionSet distributionSet = (JpaDistributionSet) rollout.getDistributionSet();
|
||||
@@ -259,7 +562,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
rollout.setTotalTargets(totalTargets);
|
||||
}
|
||||
|
||||
if (((JpaDistributionSetManagement)distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
|
||||
if (((JpaDistributionSetManagement) distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
|
||||
distributionSetManagement.lock(distributionSet.getId());
|
||||
}
|
||||
|
||||
@@ -391,253 +694,11 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
return rolloutRepository.save(savedRollout);
|
||||
}
|
||||
|
||||
/**
|
||||
* In case the given group is missing conditions or actions, they will be set
|
||||
* from the supplied default conditions.
|
||||
*
|
||||
* @param create
|
||||
* group to check
|
||||
* @param conditions
|
||||
* default conditions and actions
|
||||
*/
|
||||
private static JpaRolloutGroup prepareRolloutGroupWithDefaultConditions(final RolloutGroupCreate create,
|
||||
final RolloutGroupConditions conditions) {
|
||||
final JpaRolloutGroup group = ((JpaRolloutGroupCreate) create).build();
|
||||
|
||||
if (group.getSuccessCondition() == null) {
|
||||
group.setSuccessCondition(conditions.getSuccessCondition());
|
||||
}
|
||||
if (group.getSuccessConditionExp() == null) {
|
||||
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
|
||||
}
|
||||
if (group.getSuccessAction() == null) {
|
||||
group.setSuccessAction(conditions.getSuccessAction());
|
||||
}
|
||||
if (group.getSuccessActionExp() == null) {
|
||||
group.setSuccessActionExp(conditions.getSuccessActionExp());
|
||||
}
|
||||
|
||||
if (group.getErrorCondition() == null) {
|
||||
group.setErrorCondition(conditions.getErrorCondition());
|
||||
}
|
||||
if (group.getErrorConditionExp() == null) {
|
||||
group.setErrorConditionExp(conditions.getErrorConditionExp());
|
||||
}
|
||||
if (group.getErrorAction() == null) {
|
||||
group.setErrorAction(conditions.getErrorAction());
|
||||
}
|
||||
if (group.getErrorActionExp() == null) {
|
||||
group.setErrorActionExp(conditions.getErrorActionExp());
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
public void publishRolloutGroupCreatedEventAfterCommit(final RolloutGroup group, final Rollout rollout) {
|
||||
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher().publishEvent(
|
||||
new RolloutGroupCreatedEvent(group, rollout.getId(), eventPublisherHolder.getApplicationId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Rollout approveOrDeny(final long rolloutId, final Rollout.ApprovalDecision decision) {
|
||||
return this.approveOrDeny(rolloutId, decision, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Rollout approveOrDeny(final long rolloutId, final Rollout.ApprovalDecision decision, final String remark) {
|
||||
log.debug("approveOrDeny rollout called for rollout {} with decision {}", rolloutId, decision);
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
|
||||
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.WAITING_FOR_APPROVAL);
|
||||
switch (decision) {
|
||||
case APPROVED:
|
||||
rollout.setStatus(RolloutStatus.READY);
|
||||
break;
|
||||
case DENIED:
|
||||
rollout.setStatus(RolloutStatus.APPROVAL_DENIED);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown approval decision: " + decision);
|
||||
}
|
||||
rollout.setApprovalDecidedBy(rolloutApprovalStrategy.getApprovalUser(rollout));
|
||||
if (remark != null) {
|
||||
rollout.setApprovalRemark(remark);
|
||||
}
|
||||
return rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Rollout start(final long rolloutId) {
|
||||
log.debug("startRollout called for rollout {}", rolloutId);
|
||||
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
|
||||
RolloutHelper.checkIfRolloutCanStarted(rollout, rollout);
|
||||
rollout.setStatus(RolloutStatus.STARTING);
|
||||
rollout.setLastCheck(0);
|
||||
return rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void pauseRollout(final long rolloutId) {
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
|
||||
if (RolloutStatus.RUNNING != rollout.getStatus()) {
|
||||
throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is "
|
||||
+ rollout.getStatus().name().toLowerCase());
|
||||
}
|
||||
// setting the complete rollout only in paused state. This is sufficient
|
||||
// due the currently running groups will be completed and new groups are
|
||||
// not started until rollout goes back to running state again. The
|
||||
// periodically check for running rollouts will skip rollouts in pause
|
||||
// state.
|
||||
rollout.setStatus(RolloutStatus.PAUSED);
|
||||
rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void resumeRollout(final long rolloutId) {
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
|
||||
if (RolloutStatus.PAUSED != rollout.getStatus()) {
|
||||
throw new RolloutIllegalStateException("Rollout can only be resumed in state paused but current state is "
|
||||
+ rollout.getStatus().name().toLowerCase());
|
||||
}
|
||||
rollout.setStatus(RolloutStatus.RUNNING);
|
||||
rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
public static String createRolloutLockKey(final String tenant) {
|
||||
return tenant + "-rollout";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void delete(final long rolloutId) {
|
||||
final JpaRollout jpaRollout = rolloutRepository.findById(rolloutId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Rollout.class, rolloutId));
|
||||
|
||||
if (jpaRollout == null) {
|
||||
throw new EntityNotFoundException(Rollout.class, rolloutId);
|
||||
}
|
||||
|
||||
if (RolloutStatus.DELETING == jpaRollout.getStatus()) {
|
||||
return;
|
||||
}
|
||||
|
||||
jpaRollout.setStatus(RolloutStatus.DELETING);
|
||||
rolloutRepository.save(jpaRollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return rolloutRepository.count(
|
||||
RolloutSpecification.isDeletedWithDistributionSet(false, Sort.by(Direction.DESC, JpaRollout_.ID)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByFilters(final String searchText) {
|
||||
return rolloutRepository.count(RolloutSpecification.likeName(searchText, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByDistributionSetIdAndRolloutIsStoppable(final long setId) {
|
||||
return rolloutRepository.countByDistributionSetIdAndStatusIn(setId, ROLLOUT_STATUS_STOPPABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Rollout> findByFiltersWithDetailedStatus(final Pageable pageable, final String searchText,
|
||||
final boolean deleted) {
|
||||
final Slice<Rollout> findAll = JpaManagementHelper.findAllWithoutCountBySpec(rolloutRepository, pageable,
|
||||
Collections.singletonList(RolloutSpecification.likeName(searchText, deleted)));
|
||||
setRolloutStatusDetails(findAll);
|
||||
return findAll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> findActiveRollouts() {
|
||||
return rolloutRepository.findByStatusIn(ACTIVE_ROLLOUTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Rollout> getByName(final String rolloutName) {
|
||||
return rolloutRepository.findByName(rolloutName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Rollout update(final RolloutUpdate u) {
|
||||
final GenericRolloutUpdate update = (GenericRolloutUpdate) u;
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(update.getId());
|
||||
|
||||
checkIfDeleted(update.getId(), rollout.getStatus());
|
||||
|
||||
update.getName().ifPresent(rollout::setName);
|
||||
update.getDescription().ifPresent(rollout::setDescription);
|
||||
return rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
private static void checkIfDeleted(final Long rolloutId, final RolloutStatus status) {
|
||||
if (RolloutStatus.DELETING == status || RolloutStatus.DELETED == status) {
|
||||
throw new EntityReadOnlyException("Rollout " + rolloutId + " is soft deleted and cannot be changed");
|
||||
}
|
||||
}
|
||||
|
||||
private JpaRollout getRolloutOrThrowExceptionIfNotFound(final Long rolloutId) {
|
||||
return rolloutRepository.findById(rolloutId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Rollout.class, rolloutId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Rollout> findAllWithDetailedStatus(final Pageable pageable, final boolean deleted) {
|
||||
final Slice<Rollout> rollouts = JpaManagementHelper.findAllWithoutCountBySpec(rolloutRepository, pageable,
|
||||
Collections
|
||||
.singletonList(RolloutSpecification.isDeletedWithDistributionSet(deleted, pageable.getSort())));
|
||||
setRolloutStatusDetails(rollouts);
|
||||
return rollouts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Rollout> getWithDetailedStatus(final long rolloutId) {
|
||||
final Optional<Rollout> rollout = get(rolloutId);
|
||||
|
||||
if (!rollout.isPresent()) {
|
||||
return rollout;
|
||||
}
|
||||
|
||||
List<TotalTargetCountActionStatus> rolloutStatusCountItems = rolloutStatusCache.getRolloutStatus(rolloutId);
|
||||
|
||||
if (CollectionUtils.isEmpty(rolloutStatusCountItems)) {
|
||||
rolloutStatusCountItems = actionRepository.getStatusCountByRolloutId(rolloutId);
|
||||
rolloutStatusCache.putRolloutStatus(rolloutId, rolloutStatusCountItems);
|
||||
}
|
||||
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
|
||||
rollout.get().getTotalTargets(), rollout.get().getActionType());
|
||||
((JpaRollout) rollout.get()).setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
return rollout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final long rolloutId) {
|
||||
return rolloutRepository.existsById(rolloutId);
|
||||
}
|
||||
|
||||
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRollout(final List<Long> rollouts) {
|
||||
if (rollouts.isEmpty()) {
|
||||
return null;
|
||||
@@ -662,60 +723,32 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
return fromCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRolloutStatusDetails(final Slice<Rollout> rollouts) {
|
||||
final List<Long> rolloutIds = rollouts.getContent().stream().map(Rollout::getId).collect(Collectors.toList());
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRollout(
|
||||
rolloutIds);
|
||||
|
||||
if (allStatesForRollout != null) {
|
||||
rollouts.forEach(rollout -> {
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets(), rollout.getActionType());
|
||||
((JpaRollout) rollout).setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces the quota defining the maximum number of {@link Target}s per
|
||||
* {@link RolloutGroup}.
|
||||
*
|
||||
* @param requested
|
||||
* number of targets to check
|
||||
* @param requested number of targets to check
|
||||
*/
|
||||
private void assertTargetsPerRolloutGroupQuota(final long requested) {
|
||||
final int quota = quotaManagement.getMaxTargetsPerRolloutGroup();
|
||||
QuotaHelper.assertAssignmentQuota(requested, quota, Target.class, RolloutGroup.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelRolloutsForDistributionSet(final DistributionSet set) {
|
||||
// stop all rollouts for this distribution set
|
||||
rolloutRepository.findByDistributionSetAndStatusIn(set, ROLLOUT_STATUS_STOPPABLE).forEach(rollout -> {
|
||||
final JpaRollout jpaRollout = (JpaRollout) rollout;
|
||||
jpaRollout.setStatus(RolloutStatus.STOPPING);
|
||||
rolloutRepository.save(jpaRollout);
|
||||
log.debug("Rollout {} stopped", jpaRollout.getId());
|
||||
});
|
||||
}
|
||||
|
||||
private RolloutGroupsValidation validateTargetsInGroups(final List<RolloutGroup> groups, final String baseFilter,
|
||||
final long totalTargets, final Long dsTypeId) {
|
||||
final List<Long> groupTargetCounts = new ArrayList<>(groups.size());
|
||||
Map<String, Long> targetFilterCounts;
|
||||
if (!RolloutHelper.isRolloutRetried(baseFilter)) {
|
||||
targetFilterCounts = groups.stream()
|
||||
.map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct()
|
||||
.collect(Collectors.toMap(Function.identity(),
|
||||
groupTargetFilter -> targetManagement.countByRsqlAndCompatible(groupTargetFilter, dsTypeId)));
|
||||
.map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct()
|
||||
.collect(Collectors.toMap(Function.identity(),
|
||||
groupTargetFilter -> targetManagement.countByRsqlAndCompatible(groupTargetFilter, dsTypeId)));
|
||||
} else {
|
||||
targetFilterCounts = groups.stream()
|
||||
.map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct()
|
||||
.collect(Collectors.toMap(Function.identity(),
|
||||
groupTargetFilter -> targetManagement.countByFailedInRollout(
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(baseFilter), dsTypeId)));
|
||||
.map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct()
|
||||
.collect(Collectors.toMap(Function.identity(),
|
||||
groupTargetFilter -> targetManagement.countByFailedInRollout(
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(baseFilter), dsTypeId)));
|
||||
}
|
||||
|
||||
long unusedTargetsCount = 0;
|
||||
@@ -786,51 +819,6 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
return totalTargets - validation.getTargetsInGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(final List<RolloutGroupCreate> groups,
|
||||
final String targetFilter, final Long createdAt, final Long dsTypeId) {
|
||||
|
||||
final TargetCount targets = calculateTargets(targetFilter, createdAt, dsTypeId);
|
||||
long totalTargets = targets.total();
|
||||
final String baseFilter = targets.filter();
|
||||
|
||||
if (totalTargets == 0) {
|
||||
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
|
||||
}
|
||||
|
||||
return new AsyncResult<>(
|
||||
validateTargetsInGroups(groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()),
|
||||
baseFilter, totalTargets, dsTypeId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void triggerNextGroup(final long rolloutId) {
|
||||
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
|
||||
if (RolloutStatus.RUNNING != rollout.getStatus()) {
|
||||
throw new RolloutIllegalStateException("Rollout is not in running state");
|
||||
}
|
||||
final List<RolloutGroup> groups = rollout.getRolloutGroups();
|
||||
|
||||
final boolean isNextGroupTriggerable = groups.stream()
|
||||
.anyMatch(g -> RolloutGroupStatus.SCHEDULED.equals(g.getStatus()));
|
||||
|
||||
if (!isNextGroupTriggerable) {
|
||||
throw new RolloutIllegalStateException("Rollout does not have any groups left to be triggered");
|
||||
}
|
||||
|
||||
final RolloutGroup latestRunning = groups.stream()
|
||||
.sorted(Comparator.comparingLong(RolloutGroup::getId).reversed())
|
||||
.filter(g -> RolloutGroupStatus.RUNNING.equals(g.getStatus()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RolloutIllegalStateException("No group is running"));
|
||||
|
||||
startNextRolloutGroupAction.exec(rollout, latestRunning);
|
||||
}
|
||||
|
||||
private TargetCount calculateTargets(final String targetFilter, final Long createdAt, final Long dsTypeId) {
|
||||
String baseFilter;
|
||||
long totalTargets;
|
||||
@@ -839,7 +827,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
totalTargets = targetManagement.countByRsqlAndCompatible(baseFilter, dsTypeId);
|
||||
} else {
|
||||
totalTargets = targetManagement.countByFailedInRollout(
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(targetFilter), dsTypeId);
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(targetFilter), dsTypeId);
|
||||
baseFilter = targetFilter;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,15 +22,6 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.Tuple;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Expression;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
import jakarta.persistence.criteria.ListJoin;
|
||||
import jakarta.persistence.criteria.Order;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
|
||||
import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
@@ -47,14 +38,11 @@ import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleCreate;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleMetadataCreate;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata_;
|
||||
@@ -68,7 +56,6 @@ import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.AssignedSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
@@ -80,10 +67,7 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.SliceImpl;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.query.QueryUtils;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
@@ -133,23 +117,20 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public SoftwareModule update(final SoftwareModuleUpdate u) {
|
||||
final GenericSoftwareModuleUpdate update = (GenericSoftwareModuleUpdate) u;
|
||||
public List<SoftwareModule> create(final Collection<SoftwareModuleCreate> swModules) {
|
||||
final List<JpaSoftwareModule> modulesToCreate = swModules.stream().map(JpaSoftwareModuleCreate.class::cast)
|
||||
.map(JpaSoftwareModuleCreate::build).toList();
|
||||
|
||||
final JpaSoftwareModule module = softwareModuleRepository.findById(update.getId())
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, update.getId()));
|
||||
final List<SoftwareModule> createdModules = Collections
|
||||
.unmodifiableList(softwareModuleRepository.saveAll(AccessController.Operation.CREATE, modulesToCreate));
|
||||
|
||||
update.getDescription().ifPresent(module::setDescription);
|
||||
update.getVendor().ifPresent(module::setVendor);
|
||||
|
||||
// lock/unlock ONLY if locked flag is present!
|
||||
if (Boolean.TRUE.equals(update.locked())) {
|
||||
module.lock();
|
||||
} else if (Boolean.FALSE.equals(update.locked())) {
|
||||
module.unlock();
|
||||
if (createdModules.stream().anyMatch(SoftwareModule::isEncrypted)) {
|
||||
entityManager.flush();
|
||||
createdModules.stream().filter(SoftwareModule::isEncrypted).map(SoftwareModule::getId)
|
||||
.forEach(encryptedModuleId -> ArtifactEncryptionService.getInstance()
|
||||
.addSoftwareModuleEncryptionSecrets(encryptedModuleId));
|
||||
}
|
||||
|
||||
return softwareModuleRepository.save(module);
|
||||
return createdModules;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,61 +154,36 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public List<SoftwareModule> create(final Collection<SoftwareModuleCreate> swModules) {
|
||||
final List<JpaSoftwareModule> modulesToCreate = swModules.stream().map(JpaSoftwareModuleCreate.class::cast)
|
||||
.map(JpaSoftwareModuleCreate::build).toList();
|
||||
public SoftwareModule update(final SoftwareModuleUpdate u) {
|
||||
final GenericSoftwareModuleUpdate update = (GenericSoftwareModuleUpdate) u;
|
||||
|
||||
final List<SoftwareModule> createdModules = Collections
|
||||
.unmodifiableList(softwareModuleRepository.saveAll(AccessController.Operation.CREATE, modulesToCreate));
|
||||
final JpaSoftwareModule module = softwareModuleRepository.findById(update.getId())
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, update.getId()));
|
||||
|
||||
if (createdModules.stream().anyMatch(SoftwareModule::isEncrypted)) {
|
||||
entityManager.flush();
|
||||
createdModules.stream().filter(SoftwareModule::isEncrypted).map(SoftwareModule::getId)
|
||||
.forEach(encryptedModuleId -> ArtifactEncryptionService.getInstance()
|
||||
.addSoftwareModuleEncryptionSecrets(encryptedModuleId));
|
||||
update.getDescription().ifPresent(module::setDescription);
|
||||
update.getVendor().ifPresent(module::setVendor);
|
||||
|
||||
// lock/unlock ONLY if locked flag is present!
|
||||
if (Boolean.TRUE.equals(update.locked())) {
|
||||
module.lock();
|
||||
} else if (Boolean.FALSE.equals(update.locked())) {
|
||||
module.unlock();
|
||||
}
|
||||
return createdModules;
|
||||
|
||||
return softwareModuleRepository.save(module);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<SoftwareModule> findByType(final Pageable pageable, final long typeId) {
|
||||
assertSoftwareModuleTypeExists(typeId);
|
||||
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(
|
||||
softwareModuleRepository,
|
||||
pageable,
|
||||
List.of(
|
||||
SoftwareModuleSpecification.equalType(typeId),
|
||||
SoftwareModuleSpecification.isNotDeleted()));
|
||||
public long count() {
|
||||
return softwareModuleRepository.count(SoftwareModuleSpecification.isNotDeleted());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SoftwareModule> get(final long id) {
|
||||
return softwareModuleRepository.findById(id).map(SoftwareModule.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SoftwareModule> getByNameAndVersionAndType(final String name, final String version,
|
||||
final long typeId) {
|
||||
assertSoftwareModuleTypeExists(typeId);
|
||||
|
||||
// TODO AC - Access is restricted. This could have problem with UI when access control is enabled.
|
||||
// Vaadin UI use this for validation. May need to be called via elevated access
|
||||
return JpaManagementHelper
|
||||
.findOneBySpec(softwareModuleRepository, List.of(
|
||||
SoftwareModuleSpecification.likeNameAndVersion(name, version),
|
||||
SoftwareModuleSpecification.equalType(typeId),
|
||||
SoftwareModuleSpecification.fetchType()))
|
||||
.map(SoftwareModule.class::cast);
|
||||
}
|
||||
|
||||
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
|
||||
softwareModuleRepository.getAccessController().ifPresent(accessController ->
|
||||
accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule));
|
||||
for (final Artifact localArtifact : swModule.getArtifacts()) {
|
||||
((JpaArtifactManagement)artifactManagement)
|
||||
.clearArtifactBinary(localArtifact.getSha1Hash());
|
||||
}
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void delete(final long id) {
|
||||
delete(List.of(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -279,6 +235,21 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SoftwareModule> get(final Collection<Long> ids) {
|
||||
return Collections.unmodifiableList(softwareModuleRepository.findAllById(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final long id) {
|
||||
return softwareModuleRepository.existsById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SoftwareModule> get(final long id) {
|
||||
return softwareModuleRepository.findById(id).map(SoftwareModule.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<SoftwareModule> findAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleRepository, pageable, List.of(
|
||||
@@ -286,11 +257,6 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
SoftwareModuleSpecification.fetchType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return softwareModuleRepository.count(SoftwareModuleSpecification.isNotDeleted());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModule> findByRsql(final Pageable pageable, final String rsqlParam) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleRepository, pageable, List.of(
|
||||
@@ -299,70 +265,6 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
SoftwareModuleSpecification.isNotDeleted()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SoftwareModule> get(final Collection<Long> ids) {
|
||||
return Collections.unmodifiableList(softwareModuleRepository.findAllById(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<SoftwareModule> findByTextAndType(final Pageable pageable, final String searchText,
|
||||
final Long typeId) {
|
||||
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>(3);
|
||||
specList.add(SoftwareModuleSpecification.isNotDeleted());
|
||||
|
||||
if (!ObjectUtils.isEmpty(searchText)) {
|
||||
specList.add(buildSmSearchQuerySpec(searchText));
|
||||
}
|
||||
|
||||
if (null != typeId) {
|
||||
assertSoftwareModuleTypeExists(typeId);
|
||||
specList.add(SoftwareModuleSpecification.equalType(typeId));
|
||||
}
|
||||
|
||||
specList.add(SoftwareModuleSpecification.fetchType());
|
||||
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleRepository, pageable, specList);
|
||||
}
|
||||
|
||||
private Specification<JpaSoftwareModule> buildSmSearchQuerySpec(final String searchText) {
|
||||
final String[] smFilterNameAndVersionEntries = JpaManagementHelper
|
||||
.getFilterNameAndVersionEntries(searchText.trim());
|
||||
return SoftwareModuleSpecification.likeNameAndVersion(smFilterNameAndVersionEntries[0],
|
||||
smFilterNameAndVersionEntries[1]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModule> findByAssignedTo(final Pageable pageable, final long distributionSetId) {
|
||||
assertDistributionSetExists(distributionSetId);
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleRepository, pageable,
|
||||
Collections.singletonList(SoftwareModuleSpecification.byAssignedToDs(distributionSetId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByAssignedTo(final long distributionSetId) {
|
||||
assertDistributionSetExists(distributionSetId);
|
||||
|
||||
return softwareModuleRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public SoftwareModuleMetadata createMetaData(final SoftwareModuleMetadataCreate c) {
|
||||
final JpaSoftwareModuleMetadataCreate create = (JpaSoftwareModuleMetadataCreate) c;
|
||||
final Long id = create.getSoftwareModuleId();
|
||||
|
||||
assertSoftwareModuleExists(id);
|
||||
assertMetaDataQuota(id, 1);
|
||||
|
||||
// touch to update revision and last modified timestamp
|
||||
JpaManagementHelper.touch(entityManager, softwareModuleRepository, (JpaSoftwareModule) get(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
|
||||
return saveMetadata(create);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
@@ -400,56 +302,21 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static Stream<JpaSoftwareModuleMetadataCreate> createJpaMetadataCreateStream(
|
||||
final Collection<SoftwareModuleMetadataCreate> create) {
|
||||
return create.stream().map(JpaSoftwareModuleMetadataCreate.class::cast);
|
||||
}
|
||||
|
||||
private SoftwareModuleMetadata saveMetadata(final JpaSoftwareModuleMetadataCreate create) {
|
||||
assertSoftwareModuleMetadataDoesNotExist(create.getSoftwareModuleId(), create);
|
||||
return softwareModuleMetadataRepository.save(create.build());
|
||||
}
|
||||
|
||||
private void assertSoftwareModuleMetadataDoesNotExist(final Long id,
|
||||
final JpaSoftwareModuleMetadataCreate md) {
|
||||
if (softwareModuleMetadataRepository.existsById(new SwMetadataCompositeKey(id, md.getKey()))) {
|
||||
throw new EntityAlreadyExistsException("Metadata entry with key '" + md.getKey() + "' already exists!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the meta data quota for the software module with the given ID.
|
||||
*
|
||||
* @param id
|
||||
* The software module ID.
|
||||
* @param requested
|
||||
* Number of meta data entries to be created.
|
||||
*/
|
||||
private void assertMetaDataQuota(final Long id, final int requested) {
|
||||
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
|
||||
QuotaHelper.assertAssignmentQuota(id, requested, maxMetaData, SoftwareModuleMetadata.class,
|
||||
SoftwareModule.class, softwareModuleMetadataRepository::countBySoftwareModuleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public SoftwareModuleMetadata updateMetaData(final SoftwareModuleMetadataUpdate u) {
|
||||
final GenericSoftwareModuleMetadataUpdate update = (GenericSoftwareModuleMetadataUpdate) u;
|
||||
public SoftwareModuleMetadata createMetaData(final SoftwareModuleMetadataCreate c) {
|
||||
final JpaSoftwareModuleMetadataCreate create = (JpaSoftwareModuleMetadataCreate) c;
|
||||
final Long id = create.getSoftwareModuleId();
|
||||
|
||||
// check if exists otherwise throw entity not found exception
|
||||
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) getMetaDataBySoftwareModuleId(
|
||||
update.getSoftwareModuleId(), update.getKey())
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleMetadata.class,
|
||||
update.getSoftwareModuleId(), update.getKey()));
|
||||
assertSoftwareModuleExists(id);
|
||||
assertMetaDataQuota(id, 1);
|
||||
|
||||
update.getValue().ifPresent(metadata::setValue);
|
||||
update.isTargetVisible().ifPresent(metadata::setTargetVisible);
|
||||
|
||||
JpaManagementHelper.touch(entityManager, softwareModuleRepository,
|
||||
(JpaSoftwareModule) metadata.getSoftwareModule());
|
||||
return softwareModuleMetadataRepository.save(metadata);
|
||||
// touch to update revision and last modified timestamp
|
||||
JpaManagementHelper.touch(entityManager, softwareModuleRepository, (JpaSoftwareModule) get(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
|
||||
return saveMetadata(create);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -466,14 +333,61 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModuleMetadata> findMetaDataByRsql(final Pageable pageable, final long id,
|
||||
final String rsqlParam) {
|
||||
public Page<SoftwareModule> findByAssignedTo(final Pageable pageable, final long distributionSetId) {
|
||||
assertDistributionSetExists(distributionSetId);
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleRepository, pageable,
|
||||
Collections.singletonList(SoftwareModuleSpecification.byAssignedToDs(distributionSetId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByAssignedTo(final long distributionSetId) {
|
||||
assertDistributionSetExists(distributionSetId);
|
||||
|
||||
return softwareModuleRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<SoftwareModule> findByTextAndType(final Pageable pageable, final String searchText,
|
||||
final Long typeId) {
|
||||
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>(3);
|
||||
specList.add(SoftwareModuleSpecification.isNotDeleted());
|
||||
|
||||
if (!ObjectUtils.isEmpty(searchText)) {
|
||||
specList.add(buildSmSearchQuerySpec(searchText));
|
||||
}
|
||||
|
||||
if (null != typeId) {
|
||||
assertSoftwareModuleTypeExists(typeId);
|
||||
specList.add(SoftwareModuleSpecification.equalType(typeId));
|
||||
}
|
||||
|
||||
specList.add(SoftwareModuleSpecification.fetchType());
|
||||
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleRepository, pageable, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SoftwareModule> getByNameAndVersionAndType(final String name, final String version,
|
||||
final long typeId) {
|
||||
assertSoftwareModuleTypeExists(typeId);
|
||||
|
||||
// TODO AC - Access is restricted. This could have problem with UI when access control is enabled.
|
||||
// Vaadin UI use this for validation. May need to be called via elevated access
|
||||
return JpaManagementHelper
|
||||
.findOneBySpec(softwareModuleRepository, List.of(
|
||||
SoftwareModuleSpecification.likeNameAndVersion(name, version),
|
||||
SoftwareModuleSpecification.equalType(typeId),
|
||||
SoftwareModuleSpecification.fetchType()))
|
||||
.map(SoftwareModule.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SoftwareModuleMetadata> getMetaDataBySoftwareModuleId(final long id, final String key) {
|
||||
assertSoftwareModuleExists(id);
|
||||
|
||||
final List<Specification<JpaSoftwareModuleMetadata>> specList = Arrays
|
||||
.asList(RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleMetadataFields.class,
|
||||
virtualPropertyReplacer, database), metadataBySoftwareModuleIdSpec(id));
|
||||
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleMetadataRepository, pageable, specList);
|
||||
return softwareModuleMetadataRepository.findById(new SwMetadataCompositeKey(id, key))
|
||||
.map(SoftwareModuleMetadata.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -492,11 +406,35 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SoftwareModuleMetadata> getMetaDataBySoftwareModuleId(final long id, final String key) {
|
||||
public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(final Pageable pageable,
|
||||
final long id) {
|
||||
assertSoftwareModuleExists(id);
|
||||
|
||||
return softwareModuleMetadataRepository.findById(new SwMetadataCompositeKey(id, key))
|
||||
.map(SoftwareModuleMetadata.class::cast);
|
||||
return JpaManagementHelper.convertPage(softwareModuleMetadataRepository.findBySoftwareModuleIdAndTargetVisible(
|
||||
PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), id, true), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModuleMetadata> findMetaDataByRsql(final Pageable pageable, final long id,
|
||||
final String rsqlParam) {
|
||||
assertSoftwareModuleExists(id);
|
||||
|
||||
final List<Specification<JpaSoftwareModuleMetadata>> specList = Arrays
|
||||
.asList(RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleMetadataFields.class,
|
||||
virtualPropertyReplacer, database), metadataBySoftwareModuleIdSpec(id));
|
||||
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleMetadataRepository, pageable, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<SoftwareModule> findByType(final Pageable pageable, final long typeId) {
|
||||
assertSoftwareModuleTypeExists(typeId);
|
||||
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(
|
||||
softwareModuleRepository,
|
||||
pageable,
|
||||
List.of(
|
||||
SoftwareModuleSpecification.equalType(typeId),
|
||||
SoftwareModuleSpecification.isNotDeleted()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -531,22 +469,71 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void delete(final long id) {
|
||||
delete(List.of(id));
|
||||
public SoftwareModuleMetadata updateMetaData(final SoftwareModuleMetadataUpdate u) {
|
||||
final GenericSoftwareModuleMetadataUpdate update = (GenericSoftwareModuleMetadataUpdate) u;
|
||||
|
||||
// check if exists otherwise throw entity not found exception
|
||||
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) getMetaDataBySoftwareModuleId(
|
||||
update.getSoftwareModuleId(), update.getKey())
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleMetadata.class,
|
||||
update.getSoftwareModuleId(), update.getKey()));
|
||||
|
||||
update.getValue().ifPresent(metadata::setValue);
|
||||
update.isTargetVisible().ifPresent(metadata::setTargetVisible);
|
||||
|
||||
JpaManagementHelper.touch(entityManager, softwareModuleRepository,
|
||||
(JpaSoftwareModule) metadata.getSoftwareModule());
|
||||
return softwareModuleMetadataRepository.save(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final long id) {
|
||||
return softwareModuleRepository.existsById(id);
|
||||
private static Stream<JpaSoftwareModuleMetadataCreate> createJpaMetadataCreateStream(
|
||||
final Collection<SoftwareModuleMetadataCreate> create) {
|
||||
return create.stream().map(JpaSoftwareModuleMetadataCreate.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(final Pageable pageable,
|
||||
final long id) {
|
||||
assertSoftwareModuleExists(id);
|
||||
private static Specification<JpaSoftwareModuleMetadata> metadataBySoftwareModuleIdSpec(final long id) {
|
||||
return (root, query, cb) -> cb
|
||||
.equal(root.get(JpaSoftwareModuleMetadata_.softwareModule).get(JpaSoftwareModule_.id), id);
|
||||
}
|
||||
|
||||
return JpaManagementHelper.convertPage(softwareModuleMetadataRepository.findBySoftwareModuleIdAndTargetVisible(
|
||||
PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), id, true), pageable);
|
||||
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
|
||||
softwareModuleRepository.getAccessController().ifPresent(accessController ->
|
||||
accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule));
|
||||
for (final Artifact localArtifact : swModule.getArtifacts()) {
|
||||
((JpaArtifactManagement) artifactManagement)
|
||||
.clearArtifactBinary(localArtifact.getSha1Hash());
|
||||
}
|
||||
}
|
||||
|
||||
private Specification<JpaSoftwareModule> buildSmSearchQuerySpec(final String searchText) {
|
||||
final String[] smFilterNameAndVersionEntries = JpaManagementHelper
|
||||
.getFilterNameAndVersionEntries(searchText.trim());
|
||||
return SoftwareModuleSpecification.likeNameAndVersion(smFilterNameAndVersionEntries[0],
|
||||
smFilterNameAndVersionEntries[1]);
|
||||
}
|
||||
|
||||
private SoftwareModuleMetadata saveMetadata(final JpaSoftwareModuleMetadataCreate create) {
|
||||
assertSoftwareModuleMetadataDoesNotExist(create.getSoftwareModuleId(), create);
|
||||
return softwareModuleMetadataRepository.save(create.build());
|
||||
}
|
||||
|
||||
private void assertSoftwareModuleMetadataDoesNotExist(final Long id,
|
||||
final JpaSoftwareModuleMetadataCreate md) {
|
||||
if (softwareModuleMetadataRepository.existsById(new SwMetadataCompositeKey(id, md.getKey()))) {
|
||||
throw new EntityAlreadyExistsException("Metadata entry with key '" + md.getKey() + "' already exists!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the meta data quota for the software module with the given ID.
|
||||
*
|
||||
* @param id The software module ID.
|
||||
* @param requested Number of meta data entries to be created.
|
||||
*/
|
||||
private void assertMetaDataQuota(final Long id, final int requested) {
|
||||
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
|
||||
QuotaHelper.assertAssignmentQuota(id, requested, maxMetaData, SoftwareModuleMetadata.class,
|
||||
SoftwareModule.class, softwareModuleMetadataRepository::countBySoftwareModuleId);
|
||||
}
|
||||
|
||||
private void assertSoftwareModuleExists(final Long id) {
|
||||
@@ -566,9 +553,4 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
throw new EntityNotFoundException(DistributionSet.class, distributionSetId);
|
||||
}
|
||||
}
|
||||
|
||||
private static Specification<JpaSoftwareModuleMetadata> metadataBySoftwareModuleIdSpec(final long id) {
|
||||
return (root, query, cb) -> cb
|
||||
.equal(root.get(JpaSoftwareModuleMetadata_.softwareModule).get(JpaSoftwareModule_.id), id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link SoftwareModuleTypeManagement}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Validated
|
||||
@@ -68,6 +67,37 @@ public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManage
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SoftwareModuleType> getByKey(final String key) {
|
||||
return softwareModuleTypeRepository.findByKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SoftwareModuleType> getByName(final String name) {
|
||||
return softwareModuleTypeRepository.findByName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public List<SoftwareModuleType> create(final Collection<SoftwareModuleTypeCreate> c) {
|
||||
final List<JpaSoftwareModuleType> creates = c.stream().map(JpaSoftwareModuleTypeCreate.class::cast)
|
||||
.map(JpaSoftwareModuleTypeCreate::build).toList();
|
||||
return Collections.unmodifiableList(
|
||||
softwareModuleTypeRepository.saveAll(AccessController.Operation.CREATE, creates));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public SoftwareModuleType create(final SoftwareModuleTypeCreate c) {
|
||||
final JpaSoftwareModuleTypeCreate create = (JpaSoftwareModuleTypeCreate) c;
|
||||
|
||||
return softwareModuleTypeRepository.save(AccessController.Operation.CREATE, create.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
@@ -84,46 +114,11 @@ public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManage
|
||||
return softwareModuleTypeRepository.save(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModuleType> findByRsql(final Pageable pageable, final String rsqlParam) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleTypeRepository, pageable,
|
||||
List.of(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleTypeFields.class,
|
||||
virtualPropertyReplacer, database),
|
||||
SoftwareModuleTypeSpecification.isNotDeleted()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<SoftwareModuleType> findAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleTypeRepository, pageable,
|
||||
List.of(SoftwareModuleTypeSpecification.isNotDeleted()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return softwareModuleTypeRepository.count(SoftwareModuleTypeSpecification.isNotDeleted());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SoftwareModuleType> getByKey(final String key) {
|
||||
return softwareModuleTypeRepository.findByKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SoftwareModuleType> getByName(final String name) {
|
||||
return softwareModuleTypeRepository.findByName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public SoftwareModuleType create(final SoftwareModuleTypeCreate c) {
|
||||
final JpaSoftwareModuleTypeCreate create = (JpaSoftwareModuleTypeCreate) c;
|
||||
|
||||
return softwareModuleTypeRepository.save(AccessController.Operation.CREATE, create.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
@@ -135,17 +130,6 @@ public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManage
|
||||
delete(toDelete);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public List<SoftwareModuleType> create(final Collection<SoftwareModuleTypeCreate> c) {
|
||||
final List<JpaSoftwareModuleType> creates = c.stream().map(JpaSoftwareModuleTypeCreate.class::cast)
|
||||
.map(JpaSoftwareModuleTypeCreate::build).toList();
|
||||
return Collections.unmodifiableList(
|
||||
softwareModuleTypeRepository.saveAll(AccessController.Operation.CREATE, creates));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
@@ -161,14 +145,29 @@ public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManage
|
||||
return Collections.unmodifiableList(softwareModuleTypeRepository.findAllById(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final long id) {
|
||||
return softwareModuleTypeRepository.existsById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SoftwareModuleType> get(final long id) {
|
||||
return softwareModuleTypeRepository.findById(id).map(SoftwareModuleType.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final long id) {
|
||||
return softwareModuleTypeRepository.existsById(id);
|
||||
public Slice<SoftwareModuleType> findAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleTypeRepository, pageable,
|
||||
List.of(SoftwareModuleTypeSpecification.isNotDeleted()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModuleType> findByRsql(final Pageable pageable, final String rsqlParam) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleTypeRepository, pageable,
|
||||
List.of(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleTypeFields.class,
|
||||
virtualPropertyReplacer, database),
|
||||
SoftwareModuleTypeSpecification.isNotDeleted()));
|
||||
}
|
||||
|
||||
private void delete(JpaSoftwareModuleType toDelete) {
|
||||
|
||||
@@ -74,81 +74,57 @@ import org.springframework.validation.annotation.Validated;
|
||||
public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, SystemManagement {
|
||||
|
||||
private static final int MAX_TENANTS_QUERY = 1000;
|
||||
|
||||
private final String countArtifactQuery;
|
||||
private final String countSoftwareModulesQuery;
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Autowired
|
||||
private TargetRepository targetRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetFilterQueryRepository targetFilterQueryRepository;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetRepository distributionSetRepository;
|
||||
|
||||
@Autowired
|
||||
private SoftwareModuleRepository softwareModuleRepository;
|
||||
|
||||
@Autowired
|
||||
private TenantMetaDataRepository tenantMetaDataRepository;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetTypeRepository distributionSetTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private SoftwareModuleTypeRepository softwareModuleTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetTagRepository targetTagRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetTypeRepository targetTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetTagRepository distributionSetTagRepository;
|
||||
|
||||
@Autowired
|
||||
private TenantConfigurationRepository tenantConfigurationRepository;
|
||||
|
||||
@Autowired
|
||||
private RolloutRepository rolloutRepository;
|
||||
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
|
||||
@Autowired
|
||||
private TenantStatsManagement systemStatsManagement;
|
||||
|
||||
@Autowired
|
||||
private TenancyCacheManager cacheManager;
|
||||
|
||||
@Autowired
|
||||
private SystemManagementCacheKeyGenerator currentTenantCacheKeyGenerator;
|
||||
|
||||
@Autowired
|
||||
private SystemSecurityContext systemSecurityContext;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager txManager;
|
||||
|
||||
@Autowired
|
||||
private RolloutStatusCache rolloutStatusCache;
|
||||
|
||||
@Autowired
|
||||
private ArtifactRepository artifactRepository;
|
||||
|
||||
@Autowired
|
||||
private RepositoryProperties repositoryProperties;
|
||||
|
||||
private final String countArtifactQuery;
|
||||
private final String countSoftwareModulesQuery;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param properties
|
||||
* properties to get the underlying database
|
||||
*
|
||||
* @param properties properties to get the underlying database
|
||||
*/
|
||||
public JpaSystemManagement(final JpaProperties properties) {
|
||||
|
||||
@@ -159,48 +135,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
+ isDeleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SystemUsageReport getSystemUsageStatistics() {
|
||||
|
||||
final Number count = (Number) entityManager.createNativeQuery(countSoftwareModulesQuery).getSingleResult();
|
||||
|
||||
long sumOfArtifacts = 0;
|
||||
if (count != null) {
|
||||
sumOfArtifacts = count.longValue();
|
||||
}
|
||||
|
||||
// we use native queries to punch through the tenant boundaries. This
|
||||
// has to be used with care!
|
||||
final long targets = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_target")
|
||||
.getSingleResult()).longValue();
|
||||
|
||||
final long artifacts = ((Number) entityManager.createNativeQuery(countArtifactQuery).getSingleResult())
|
||||
.longValue();
|
||||
|
||||
final long actions = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_action")
|
||||
.getSingleResult()).longValue();
|
||||
|
||||
return new SystemUsageReportWithTenants(targets, artifacts, actions, sumOfArtifacts,
|
||||
tenantMetaDataRepository.count());
|
||||
}
|
||||
|
||||
private static boolean isPostgreSql(final JpaProperties properties) {
|
||||
return Database.POSTGRESQL == properties.getDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SystemUsageReportWithTenants getSystemUsageStatisticsWithTenants() {
|
||||
final SystemUsageReportWithTenants result = (SystemUsageReportWithTenants) getSystemUsageStatistics();
|
||||
|
||||
usageStatsPerTenant(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void usageStatsPerTenant(final SystemUsageReportWithTenants report) {
|
||||
forEachTenant(tenant -> report.addTenantData(systemStatsManagement.getStatsOfTenant()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public KeyGenerator currentTenantKeyGenerator() {
|
||||
@@ -208,18 +142,13 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
}
|
||||
|
||||
@Override
|
||||
public TenantMetaData createTenantMetadata(final String tenant) {
|
||||
final TenantMetaData result = tenantMetaDataRepository.findByTenantIgnoreCase(tenant);
|
||||
// Create if it does not exist
|
||||
if (result == null) {
|
||||
return createTenantMetadata0(tenant);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<String> findTenants(final Pageable pageable) {
|
||||
return tenantMetaDataRepository.findTenants(pageable);
|
||||
@Cacheable(value = "currentTenant", keyGenerator = "currentTenantKeyGenerator", cacheManager = "directCacheManager", unless = "#result == null")
|
||||
public String currentTenant() {
|
||||
return currentTenantCacheKeyGenerator.getTenantInCreation().orElseGet(() -> {
|
||||
final TenantMetaData findByTenant = tenantMetaDataRepository
|
||||
.findByTenantIgnoreCase(tenantAware.getCurrentTenant());
|
||||
return findByTenant != null ? findByTenant.getTenant() : null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -249,6 +178,70 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<String> findTenants(final Pageable pageable) {
|
||||
return tenantMetaDataRepository.findTenants(pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
// Exception squid:S2229 - calling findTenants without transaction is
|
||||
// intended in this case
|
||||
@SuppressWarnings("squid:S2229")
|
||||
public void forEachTenant(final Consumer<String> consumer) {
|
||||
Page<String> tenants;
|
||||
Pageable query = PageRequest.of(0, MAX_TENANTS_QUERY);
|
||||
do {
|
||||
tenants = findTenants(query);
|
||||
tenants.forEach(tenant -> tenantAware.runAsTenant(tenant, () -> {
|
||||
try {
|
||||
consumer.accept(tenant);
|
||||
} catch (final RuntimeException ex) {
|
||||
log.debug("Exception on forEachTenant execution for tenant {}. Continue with next tenant.",
|
||||
tenant, ex);
|
||||
log.error("Exception on forEachTenant execution for tenant {} with error message [{}]. "
|
||||
+ "Continue with next tenant.", tenant, ex.getMessage());
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
} while ((query = tenants.nextPageable()) != Pageable.unpaged());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SystemUsageReportWithTenants getSystemUsageStatisticsWithTenants() {
|
||||
final SystemUsageReportWithTenants result = (SystemUsageReportWithTenants) getSystemUsageStatistics();
|
||||
|
||||
usageStatsPerTenant(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SystemUsageReport getSystemUsageStatistics() {
|
||||
|
||||
final Number count = (Number) entityManager.createNativeQuery(countSoftwareModulesQuery).getSingleResult();
|
||||
|
||||
long sumOfArtifacts = 0;
|
||||
if (count != null) {
|
||||
sumOfArtifacts = count.longValue();
|
||||
}
|
||||
|
||||
// we use native queries to punch through the tenant boundaries. This
|
||||
// has to be used with care!
|
||||
final long targets = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_target")
|
||||
.getSingleResult()).longValue();
|
||||
|
||||
final long artifacts = ((Number) entityManager.createNativeQuery(countArtifactQuery).getSingleResult())
|
||||
.longValue();
|
||||
|
||||
final long actions = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_action")
|
||||
.getSingleResult()).longValue();
|
||||
|
||||
return new SystemUsageReportWithTenants(targets, artifacts, actions, sumOfArtifacts,
|
||||
tenantMetaDataRepository.count());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TenantMetaData getTenantMetadata() {
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
@@ -270,13 +263,13 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "currentTenant", keyGenerator = "currentTenantKeyGenerator", cacheManager = "directCacheManager", unless = "#result == null")
|
||||
public String currentTenant() {
|
||||
return currentTenantCacheKeyGenerator.getTenantInCreation().orElseGet(() -> {
|
||||
final TenantMetaData findByTenant = tenantMetaDataRepository
|
||||
.findByTenantIgnoreCase(tenantAware.getCurrentTenant());
|
||||
return findByTenant != null ? findByTenant.getTenant() : null;
|
||||
});
|
||||
public TenantMetaData createTenantMetadata(final String tenant) {
|
||||
final TenantMetaData result = tenantMetaDataRepository.findByTenantIgnoreCase(tenant);
|
||||
// Create if it does not exist
|
||||
if (result == null) {
|
||||
return createTenantMetadata0(tenant);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -292,6 +285,20 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
return tenantMetaDataRepository.save(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TenantMetaData getTenantMetadata(final long tenantId) {
|
||||
return tenantMetaDataRepository.findById(tenantId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(TenantMetaData.class, tenantId));
|
||||
}
|
||||
|
||||
private static boolean isPostgreSql(final JpaProperties properties) {
|
||||
return Database.POSTGRESQL == properties.getDatabase();
|
||||
}
|
||||
|
||||
private void usageStatsPerTenant(final SystemUsageReportWithTenants report) {
|
||||
forEachTenant(tenant -> report.addTenantData(systemStatsManagement.getStatsOfTenant()));
|
||||
}
|
||||
|
||||
private DistributionSetType createStandardSoftwareDataSetup() {
|
||||
final SoftwareModuleType app = softwareModuleTypeRepository
|
||||
.save(new JpaSoftwareModuleType(org.eclipse.hawkbit.repository.Constants.SMT_DEFAULT_APP_KEY,
|
||||
@@ -318,38 +325,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
.save(new JpaDistributionSetType(org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_OS_WITH_APPS_KEY,
|
||||
org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_OS_WITH_APPS_NAME,
|
||||
"Default type with Firmware/OS and optional app(s).").addMandatoryModuleType(os)
|
||||
.addOptionalModuleType(app));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TenantMetaData getTenantMetadata(final long tenantId) {
|
||||
return tenantMetaDataRepository.findById(tenantId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(TenantMetaData.class, tenantId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
// Exception squid:S2229 - calling findTenants without transaction is
|
||||
// intended in this case
|
||||
@SuppressWarnings("squid:S2229")
|
||||
public void forEachTenant(final Consumer<String> consumer) {
|
||||
Page<String> tenants;
|
||||
Pageable query = PageRequest.of(0, MAX_TENANTS_QUERY);
|
||||
do {
|
||||
tenants = findTenants(query);
|
||||
tenants.forEach(tenant -> tenantAware.runAsTenant(tenant, () -> {
|
||||
try {
|
||||
consumer.accept(tenant);
|
||||
} catch (final RuntimeException ex) {
|
||||
log.debug("Exception on forEachTenant execution for tenant {}. Continue with next tenant.",
|
||||
tenant, ex);
|
||||
log.error("Exception on forEachTenant execution for tenant {} with error message [{}]. "
|
||||
+ "Continue with next tenant.", tenant, ex.getMessage());
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
} while ((query = tenants.nextPageable()) != Pageable.unpaged());
|
||||
|
||||
.addOptionalModuleType(app));
|
||||
}
|
||||
|
||||
private TenantMetaData createTenantMetadata0(final String tenant) {
|
||||
@@ -371,8 +347,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
* {@link MultiTenantJpaTransactionManager} is called again and set the
|
||||
* tenant for this transaction.
|
||||
*
|
||||
* @param tenant
|
||||
* the tenant to be created
|
||||
* @param tenant the tenant to be created
|
||||
* @return the initial created {@link TenantMetaData}
|
||||
*/
|
||||
private TenantMetaData createInitialTenantMetaData(final String tenant) {
|
||||
|
||||
@@ -16,7 +16,9 @@ import java.util.Optional;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import cz.jirutka.rsql.parser.RSQLParserException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.ContextAware;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
@@ -25,7 +27,6 @@ import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.ContextAware;
|
||||
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.GenericTargetFilterQueryUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
|
||||
@@ -64,8 +65,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import cz.jirutka.rsql.parser.RSQLParserException;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link TargetFilterQueryManagement}.
|
||||
*/
|
||||
@@ -140,6 +139,17 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
targetFilterQueryRepository.deleteById(targetFilterQueryId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyTargetFilterQuerySyntax(final String query) {
|
||||
try {
|
||||
RSQLUtility.validateRsqlFor(query, TargetFields.class);
|
||||
return true;
|
||||
} catch (final RSQLParserException | RSQLParameterUnsupportedFieldException e) {
|
||||
log.debug("The RSQL query '" + query + "' is invalid.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<TargetFilterQuery> findAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(targetFilterQueryRepository, pageable, null);
|
||||
@@ -179,7 +189,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
public Page<TargetFilterQuery> findByRsql(final Pageable pageable, final String rsqlFilter) {
|
||||
final List<Specification<JpaTargetFilterQuery>> specList = !ObjectUtils.isEmpty(rsqlFilter)
|
||||
? Collections.singletonList(RSQLUtility.buildRsqlSpecification(rsqlFilter,
|
||||
TargetFilterQueryFields.class, virtualPropertyReplacer, database))
|
||||
TargetFilterQueryFields.class, virtualPropertyReplacer, database))
|
||||
: Collections.emptyList();
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetFilterQueryRepository, pageable, specList);
|
||||
@@ -225,13 +235,13 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TargetFilterQuery> getByName(final String targetFilterQueryName) {
|
||||
return targetFilterQueryRepository.findByName(targetFilterQueryName);
|
||||
public Optional<TargetFilterQuery> get(final long targetFilterQueryId) {
|
||||
return targetFilterQueryRepository.findById(targetFilterQueryId).map(TargetFilterQuery.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TargetFilterQuery> get(final long targetFilterQueryId) {
|
||||
return targetFilterQueryRepository.findById(targetFilterQueryId).map(TargetFilterQuery.class::cast);
|
||||
public Optional<TargetFilterQuery> getByName(final String targetFilterQueryName) {
|
||||
return targetFilterQueryRepository.findByName(targetFilterQueryName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -279,7 +289,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
final JpaDistributionSet distributionSet = (JpaDistributionSet) distributionSetManagement
|
||||
.getValidAndComplete(update.getDsId());
|
||||
|
||||
if (((JpaDistributionSetManagement)distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
|
||||
if (((JpaDistributionSetManagement) distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
|
||||
distributionSetManagement.lock(distributionSet.getId());
|
||||
}
|
||||
|
||||
@@ -298,9 +308,11 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
return targetFilterQueryRepository.save(targetFilterQuery);
|
||||
}
|
||||
|
||||
private boolean isConfirmationFlowEnabled() {
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
|
||||
.isConfirmationFlowEnabled();
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelAutoAssignmentForDistributionSet(final long distributionSetId) {
|
||||
targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionTypeAndAccessContext(distributionSetId);
|
||||
log.debug("Auto assignments for distribution sets {} deactivated", distributionSetId);
|
||||
}
|
||||
|
||||
private static ActionType sanitizeAutoAssignActionType(final ActionType actionType) {
|
||||
@@ -315,32 +327,19 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
return actionType;
|
||||
}
|
||||
|
||||
private boolean isConfirmationFlowEnabled() {
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
|
||||
.isConfirmationFlowEnabled();
|
||||
}
|
||||
|
||||
private JpaTargetFilterQuery findTargetFilterQueryOrThrowExceptionIfNotFound(final Long queryId) {
|
||||
return targetFilterQueryRepository.findById(queryId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, queryId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyTargetFilterQuerySyntax(final String query) {
|
||||
try {
|
||||
RSQLUtility.validateRsqlFor(query, TargetFields.class);
|
||||
return true;
|
||||
} catch (final RSQLParserException | RSQLParameterUnsupportedFieldException e) {
|
||||
log.debug("The RSQL query '" + query + "' is invalid.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMaxTargetsQuota(final String query, final String filterName, final long dsId) {
|
||||
QuotaHelper.assertAssignmentQuota(filterName,
|
||||
targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(dsId, query),
|
||||
quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelAutoAssignmentForDistributionSet(final long distributionSetId) {
|
||||
targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionTypeAndAccessContext(distributionSetId);
|
||||
log.debug("Auto assignments for distribution sets {} deactivated", distributionSetId);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -41,7 +41,6 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link TargetTagManagement}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Validated
|
||||
@@ -62,8 +61,8 @@ public class JpaTargetTagManagement implements TargetTagManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TargetTag> getByName(final String name) {
|
||||
return targetTagRepository.findByNameEquals(name);
|
||||
public long count() {
|
||||
return targetTagRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,8 +93,13 @@ public class JpaTargetTagManagement implements TargetTagManagement {
|
||||
public void delete(final String targetTagName) {
|
||||
targetTagRepository.delete(
|
||||
targetTagRepository
|
||||
.findOne(((root, query, cb) -> cb.equal(root.get(JpaTargetTag_.name), targetTagName)))
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagName)));
|
||||
.findOne(((root, query, cb) -> cb.equal(root.get(JpaTargetTag_.name), targetTagName)))
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagName)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetTag> findAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetTagRepository, pageable, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,8 +109,18 @@ public class JpaTargetTagManagement implements TargetTagManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return targetTagRepository.count();
|
||||
public Optional<TargetTag> getByName(final String name) {
|
||||
return targetTagRepository.findByNameEquals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TargetTag> get(final long id) {
|
||||
return targetTagRepository.findById(id).map(TargetTag.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TargetTag> get(final Collection<Long> ids) {
|
||||
return Collections.unmodifiableList(targetTagRepository.findAllById(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,19 +139,4 @@ public class JpaTargetTagManagement implements TargetTagManagement {
|
||||
|
||||
return targetTagRepository.save(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TargetTag> get(final long id) {
|
||||
return targetTagRepository.findById(id).map(TargetTag.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TargetTag> get(final Collection<Long> ids) {
|
||||
return Collections.unmodifiableList(targetTagRepository.findAllById(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetTag> findAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetTagRepository, pageable, null);
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,6 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link TargetTypeManagement}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Validated
|
||||
@@ -68,14 +67,10 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param targetTypeRepository
|
||||
* Target type repository
|
||||
* @param targetRepository
|
||||
* Target repository
|
||||
* @param virtualPropertyReplacer
|
||||
* replacer
|
||||
* @param database
|
||||
* database
|
||||
* @param targetTypeRepository Target type repository
|
||||
* @param targetRepository Target repository
|
||||
* @param virtualPropertyReplacer replacer
|
||||
* @param database database
|
||||
*/
|
||||
public JpaTargetTypeManagement(final TargetTypeRepository targetTypeRepository,
|
||||
final TargetRepository targetRepository, final DistributionSetTypeRepository distributionSetTypeRepository,
|
||||
@@ -152,7 +147,7 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetTypeRepository, pageable,
|
||||
List.of(
|
||||
RSQLUtility.buildRsqlSpecification(
|
||||
rsqlParam, TargetTypeFields.class, virtualPropertyReplacer,database)));
|
||||
rsqlParam, TargetTypeFields.class, virtualPropertyReplacer, database)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -236,13 +231,9 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
|
||||
* Enforces the quota specifying the maximum number of
|
||||
* {@link DistributionSetType}s per {@link TargetType}.
|
||||
*
|
||||
* @param id
|
||||
* of the target type
|
||||
* @param requested
|
||||
* number of distribution set types to check
|
||||
*
|
||||
* @throws AssignmentQuotaExceededException
|
||||
* if the software module type quota is exceeded
|
||||
* @param id of the target type
|
||||
* @param requested number of distribution set types to check
|
||||
* @throws AssignmentQuotaExceededException if the software module type quota is exceeded
|
||||
*/
|
||||
private void assertDistributionSetTypeQuota(final long id, final int requested) {
|
||||
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxDistributionSetTypesPerTargetType(),
|
||||
|
||||
@@ -58,76 +58,18 @@ import org.springframework.validation.annotation.Validated;
|
||||
@Validated
|
||||
public class JpaTenantConfigurationManagement implements TenantConfigurationManagement {
|
||||
|
||||
private static final ConfigurableConversionService conversionService = new DefaultConversionService();
|
||||
@Autowired
|
||||
private TenantConfigurationRepository tenantConfigurationRepository;
|
||||
|
||||
@Autowired
|
||||
private TenantConfigurationProperties tenantConfigurationProperties;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private CacheManager cacheManager;
|
||||
|
||||
@Autowired
|
||||
private AfterTransactionCommitExecutor afterCommitExecutor;
|
||||
|
||||
private static final ConfigurableConversionService conversionService = new DefaultConversionService();
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "tenantConfiguration", key = "#configurationKeyName")
|
||||
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(final String configurationKeyName,
|
||||
final Class<T> propertyType) {
|
||||
checkAccess(configurationKeyName);
|
||||
|
||||
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
|
||||
validateTenantConfigurationDataType(configurationKey, propertyType);
|
||||
|
||||
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository
|
||||
.findByKey(configurationKey.getKeyName());
|
||||
|
||||
return buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(
|
||||
final String configurationKeyName) {
|
||||
checkAccess(configurationKeyName);
|
||||
|
||||
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
|
||||
return getConfigurationValue(configurationKeyName, (Class<T>)configurationKey.getDataType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getGlobalConfigurationValue(final String configurationKeyName, final Class<T> propertyType) {
|
||||
checkAccess(configurationKeyName);
|
||||
|
||||
final TenantConfigurationKey key = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
|
||||
if (!key.getDataType().isAssignableFrom(propertyType)) {
|
||||
throw new TenantConfigurationValidatorException(String.format(
|
||||
"Cannot parse the database value of type %s into the type %s.", key.getDataType(), propertyType));
|
||||
}
|
||||
|
||||
return conversionService.convert(key.getDefaultValue(), propertyType);
|
||||
}
|
||||
|
||||
private void checkAccess(final String configurationKeyName) {
|
||||
if (TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY
|
||||
.equalsIgnoreCase(configurationKeyName)) {
|
||||
final SystemSecurityContext systemSecurityContext =
|
||||
SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
||||
if (!systemSecurityContext.isCurrentThreadSystemCode() &&
|
||||
!systemSecurityContext.hasPermission(SpPermission.READ_GATEWAY_SEC_TOKEN)) {
|
||||
throw new InsufficientPermissionException(
|
||||
"Can't read gateway security token! " + SpPermission.READ_GATEWAY_SEC_TOKEN + " is required!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
|
||||
@Transactional
|
||||
@@ -154,6 +96,85 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
return addOrUpdateConfiguration0(configurations);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void deleteConfiguration(final String configurationKeyName) {
|
||||
tenantConfigurationRepository.deleteByKey(configurationKeyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(
|
||||
final String configurationKeyName) {
|
||||
checkAccess(configurationKeyName);
|
||||
|
||||
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
|
||||
return getConfigurationValue(configurationKeyName, (Class<T>) configurationKey.getDataType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "tenantConfiguration", key = "#configurationKeyName")
|
||||
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(final String configurationKeyName,
|
||||
final Class<T> propertyType) {
|
||||
checkAccess(configurationKeyName);
|
||||
|
||||
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
|
||||
validateTenantConfigurationDataType(configurationKey, propertyType);
|
||||
|
||||
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository
|
||||
.findByKey(configurationKey.getKeyName());
|
||||
|
||||
return buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getGlobalConfigurationValue(final String configurationKeyName, final Class<T> propertyType) {
|
||||
checkAccess(configurationKeyName);
|
||||
|
||||
final TenantConfigurationKey key = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
|
||||
if (!key.getDataType().isAssignableFrom(propertyType)) {
|
||||
throw new TenantConfigurationValidatorException(String.format(
|
||||
"Cannot parse the database value of type %s into the type %s.", key.getDataType(), propertyType));
|
||||
}
|
||||
|
||||
return conversionService.convert(key.getDefaultValue(), propertyType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the data type of the tenant configuration. If it is possible to
|
||||
* cast to the given data type.
|
||||
*
|
||||
* @param configurationKey the key
|
||||
* @param propertyType the class
|
||||
*/
|
||||
private static <T> void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey,
|
||||
final Class<T> propertyType) {
|
||||
|
||||
if (!configurationKey.getDataType().isAssignableFrom(propertyType)) {
|
||||
throw new TenantConfigurationValidatorException(
|
||||
String.format("Cannot parse the database value of type %s into the type %s.",
|
||||
configurationKey.getDataType(), propertyType));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAccess(final String configurationKeyName) {
|
||||
if (TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY
|
||||
.equalsIgnoreCase(configurationKeyName)) {
|
||||
final SystemSecurityContext systemSecurityContext =
|
||||
SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
||||
if (!systemSecurityContext.isCurrentThreadSystemCode() &&
|
||||
!systemSecurityContext.hasPermission(SpPermission.READ_GATEWAY_SEC_TOKEN)) {
|
||||
throw new InsufficientPermissionException(
|
||||
"Can't read gateway security token! " + SpPermission.READ_GATEWAY_SEC_TOKEN + " is required!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration0(Map<String, T> configurations) {
|
||||
List<JpaTenantConfiguration> configurationList = new ArrayList<>();
|
||||
configurations.forEach((configurationKeyName, value) -> {
|
||||
@@ -187,9 +208,9 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
JpaTenantConfiguration::getKey,
|
||||
updatedTenantConfiguration -> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<T> clazzT = (Class<T>) configurations.get(updatedTenantConfiguration.getKey()).getClass();
|
||||
return TenantConfigurationValue.<T>builder().global(false)
|
||||
@SuppressWarnings("unchecked") final Class<T> clazzT = (Class<T>) configurations.get(updatedTenantConfiguration.getKey())
|
||||
.getClass();
|
||||
return TenantConfigurationValue.<T> builder().global(false)
|
||||
.createdBy(updatedTenantConfiguration.getCreatedBy())
|
||||
.createdAt(updatedTenantConfiguration.getCreatedAt())
|
||||
.lastModifiedAt(updatedTenantConfiguration.getLastModifiedAt())
|
||||
@@ -199,32 +220,6 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void deleteConfiguration(final String configurationKeyName) {
|
||||
tenantConfigurationRepository.deleteByKey(configurationKeyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the data type of the tenant configuration. If it is possible to
|
||||
* cast to the given data type.
|
||||
*
|
||||
* @param configurationKey the key
|
||||
* @param propertyType the class
|
||||
*/
|
||||
private static <T> void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey,
|
||||
final Class<T> propertyType) {
|
||||
|
||||
if (!configurationKey.getDataType().isAssignableFrom(propertyType)) {
|
||||
throw new TenantConfigurationValidatorException(
|
||||
String.format("Cannot parse the database value of type %s into the type %s.",
|
||||
configurationKey.getDataType(), propertyType));
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Serializable> TenantConfigurationValue<T> buildTenantConfigurationValueByKey(
|
||||
final TenantConfigurationKey configurationKey, final Class<T> propertyType,
|
||||
final TenantConfiguration tenantConfiguration) {
|
||||
@@ -247,14 +242,10 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
/**
|
||||
* Asserts that the requested configuration value change is allowed. Throws
|
||||
* a {@link TenantConfigurationValueChangeNotAllowedException} otherwise.
|
||||
*
|
||||
* @param key
|
||||
* The configuration key.
|
||||
* @param valueChange
|
||||
* The configuration to be validated.
|
||||
*
|
||||
* @throws TenantConfigurationValueChangeNotAllowedException
|
||||
* if the requested configuration change is not allowed.
|
||||
*
|
||||
* @param key The configuration key.
|
||||
* @param valueChange The configuration to be validated.
|
||||
* @throws TenantConfigurationValueChangeNotAllowedException if the requested configuration change is not allowed.
|
||||
*/
|
||||
private void assertValueChangeIsAllowed(final String key, final JpaTenantConfiguration valueChange) {
|
||||
assertMultiAssignmentsValueChange(key, valueChange);
|
||||
@@ -280,7 +271,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
}
|
||||
if (MULTI_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
|
||||
JpaTenantConfiguration batchConfig = tenantConfigurationRepository.findByKey(BATCH_ASSIGNMENTS_ENABLED);
|
||||
if (batchConfig!=null && Boolean.parseBoolean(batchConfig.getValue())) {
|
||||
if (batchConfig != null && Boolean.parseBoolean(batchConfig.getValue())) {
|
||||
log.debug("The Multi-Assignments '{}' feature cannot be enabled as it contradicts with " +
|
||||
"The Batch-Assignments feature, which is already enabled .", key);
|
||||
throw new TenantConfigurationValueChangeNotAllowedException();
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* Management service for statistics of a single tenant.
|
||||
*
|
||||
*/
|
||||
@Validated
|
||||
public class JpaTenantStatsManagement implements TenantStatsManagement {
|
||||
|
||||
@@ -44,27 +44,18 @@ import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||
/**
|
||||
* AbstractDsAssignmentStrategy for offline assignments, i.e. not managed by
|
||||
* hawkBit.
|
||||
*
|
||||
*/
|
||||
public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
|
||||
OfflineDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
||||
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties) {
|
||||
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
||||
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties) {
|
||||
super(targetRepository, afterCommit, eventPublisherHolder, actionRepository, actionStatusRepository,
|
||||
quotaManagement, multiAssignmentsConfig, confirmationFlowConfig, repositoryProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTargetUpdatedEvents(final DistributionSet set, final List<JpaTarget> targets) {
|
||||
targets.forEach(target -> {
|
||||
target.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
|
||||
sendTargetUpdatedEvent(target);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long setId) {
|
||||
final Function<List<String>, List<JpaTarget>> mapper;
|
||||
@@ -80,13 +71,11 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> cancelActiveActions(final List<List<Long>> targetIds) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeActiveActions(final List<List<Long>> targetIds) {
|
||||
// Not supported by offline case
|
||||
public void sendTargetUpdatedEvents(final DistributionSet set, final List<JpaTarget> targets) {
|
||||
targets.forEach(target -> {
|
||||
target.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
|
||||
sendTargetUpdatedEvent(target);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,7 +83,8 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) {
|
||||
final long now = System.currentTimeMillis();
|
||||
targetIds.forEach(targetIdsChunk -> {
|
||||
if (targetRepository.count(AccessController.Operation.UPDATE, targetRepository.byIdsSpec(targetIdsChunk)) != targetIdsChunk.size()) {
|
||||
if (targetRepository.count(AccessController.Operation.UPDATE,
|
||||
targetRepository.byIdsSpec(targetIdsChunk)) != targetIdsChunk.size()) {
|
||||
throw new InsufficientPermissionException("No update access to all targets!");
|
||||
}
|
||||
targetRepository.setAssignedAndInstalledDistributionSetAndUpdateStatus(
|
||||
@@ -116,6 +106,26 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> cancelActiveActions(final List<List<Long>> targetIds) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeActiveActions(final List<List<Long>> targetIds) {
|
||||
// Not supported by offline case
|
||||
}
|
||||
|
||||
@Override
|
||||
void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult) {
|
||||
// no need to send deployment events in the offline case
|
||||
}
|
||||
|
||||
@Override
|
||||
void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults) {
|
||||
// no need to send deployment events in the offline case
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType,
|
||||
final List<JpaTarget> targets, final JpaDistributionSet set) {
|
||||
@@ -135,14 +145,4 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult) {
|
||||
// no need to send deployment events in the offline case
|
||||
}
|
||||
|
||||
@Override
|
||||
void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults) {
|
||||
// no need to send deployment events in the offline case
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,46 +47,18 @@ import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* AbstractDsAssignmentStrategy for online assignments, i.e. managed by hawkBit.
|
||||
*
|
||||
*/
|
||||
public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
|
||||
OnlineDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
||||
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties) {
|
||||
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
||||
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties) {
|
||||
super(targetRepository, afterCommit, eventPublisherHolder, actionRepository, actionStatusRepository,
|
||||
quotaManagement, multiAssignmentsConfig, confirmationFlowConfig, repositoryProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTargetUpdatedEvents(final DistributionSet set, final List<JpaTarget> targets) {
|
||||
targets.forEach(target -> {
|
||||
target.setUpdateStatus(TargetUpdateStatus.PENDING);
|
||||
sendTargetUpdatedEvent(target);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult) {
|
||||
if (isMultiAssignmentsEnabled()) {
|
||||
sendDeploymentEvents(Collections.singletonList(assignmentResult));
|
||||
} else {
|
||||
sendDistributionSetAssignedEvent(assignmentResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults) {
|
||||
if (isMultiAssignmentsEnabled()) {
|
||||
sendDeploymentEvent(assignmentResults.stream().flatMap(result -> result.getAssignedEntity().stream())
|
||||
.collect(Collectors.toList()));
|
||||
} else {
|
||||
assignmentResults.forEach(this::sendDistributionSetAssignedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendDeploymentEvents(final long distributionSetId, final List<Action> actions) {
|
||||
if (isMultiAssignmentsEnabled()) {
|
||||
sendDeploymentEvent(actions);
|
||||
@@ -114,14 +86,11 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> cancelActiveActions(final List<List<Long>> targetIds) {
|
||||
return targetIds.stream().map(this::overrideObsoleteUpdateActions).flatMap(Collection::stream)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeActiveActions(final List<List<Long>> targetIds) {
|
||||
targetIds.forEach(this::closeObsoleteUpdateActions);
|
||||
public void sendTargetUpdatedEvents(final DistributionSet set, final List<JpaTarget> targets) {
|
||||
targets.forEach(target -> {
|
||||
target.setUpdateStatus(TargetUpdateStatus.PENDING);
|
||||
sendTargetUpdatedEvent(target);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,7 +98,8 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
final String currentUser) {
|
||||
final long now = System.currentTimeMillis();
|
||||
targetIds.forEach(targetIdsChunk -> {
|
||||
if (targetRepository.count(AccessController.Operation.UPDATE, targetRepository.byIdsSpec(targetIdsChunk)) != targetIdsChunk.size()) {
|
||||
if (targetRepository.count(AccessController.Operation.UPDATE,
|
||||
targetRepository.byIdsSpec(targetIdsChunk)) != targetIdsChunk.size()) {
|
||||
throw new InsufficientPermissionException("No update access to all targets!");
|
||||
}
|
||||
targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING,
|
||||
@@ -149,6 +119,36 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> cancelActiveActions(final List<List<Long>> targetIds) {
|
||||
return targetIds.stream().map(this::overrideObsoleteUpdateActions).flatMap(Collection::stream)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeActiveActions(final List<List<Long>> targetIds) {
|
||||
targetIds.forEach(this::closeObsoleteUpdateActions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult) {
|
||||
if (isMultiAssignmentsEnabled()) {
|
||||
sendDeploymentEvents(Collections.singletonList(assignmentResult));
|
||||
} else {
|
||||
sendDistributionSetAssignedEvent(assignmentResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults) {
|
||||
if (isMultiAssignmentsEnabled()) {
|
||||
sendDeploymentEvent(assignmentResults.stream().flatMap(result -> result.getAssignedEntity().stream())
|
||||
.collect(Collectors.toList()));
|
||||
} else {
|
||||
assignmentResults.forEach(this::sendDistributionSetAssignedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType,
|
||||
final List<JpaTarget> targets, final JpaDistributionSet set) {
|
||||
@@ -187,6 +187,20 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream<Action> filterCancellations(final List<Action> actions) {
|
||||
return actions.stream().filter(action -> {
|
||||
final Status actionStatus = action.getStatus();
|
||||
return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus;
|
||||
});
|
||||
}
|
||||
|
||||
private static List<Action> getActionsWithoutCancellations(final List<Action> actions) {
|
||||
if (actions == null || actions.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return filterCancellations(actions).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void sendMultiActionCancelEvent(final Action action) {
|
||||
sendMultiActionCancelEvent(action.getTenant(), Collections.singletonList(action));
|
||||
}
|
||||
@@ -224,10 +238,8 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
* Helper to fire a {@link MultiActionCancelEvent}. This method may only be
|
||||
* called if the Multi-Assignments feature is enabled.
|
||||
*
|
||||
* @param tenant
|
||||
* the event is scoped to
|
||||
* @param actions
|
||||
* assigned to the targets
|
||||
* @param tenant the event is scoped to
|
||||
* @param actions assigned to the targets
|
||||
*/
|
||||
private void sendMultiActionCancelEvent(final String tenant, final List<Action> actions) {
|
||||
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher()
|
||||
@@ -238,28 +250,12 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
* Helper to fire a {@link MultiActionAssignEvent}. This method may only be
|
||||
* called if the Multi-Assignments feature is enabled.
|
||||
*
|
||||
* @param tenant
|
||||
* the event is scoped to
|
||||
* @param actions
|
||||
* assigned to the targets
|
||||
* @param tenant the event is scoped to
|
||||
* @param actions assigned to the targets
|
||||
*/
|
||||
private void sendMultiActionAssignEvent(final String tenant, final List<Action> actions) {
|
||||
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher()
|
||||
.publishEvent(new MultiActionAssignEvent(tenant, eventPublisherHolder.getApplicationId(), actions)));
|
||||
}
|
||||
|
||||
private static Stream<Action> filterCancellations(final List<Action> actions) {
|
||||
return actions.stream().filter(action -> {
|
||||
final Status actionStatus = action.getStatus();
|
||||
return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus;
|
||||
});
|
||||
}
|
||||
|
||||
private static List<Action> getActionsWithoutCancellations(final List<Action> actions) {
|
||||
if (actions == null || actions.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return filterCancellations(actions).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,15 +30,14 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
/**
|
||||
* Holder of the base attributes common to all entities.
|
||||
*
|
||||
*/
|
||||
@MappedSuperclass
|
||||
@Access(AccessType.FIELD)
|
||||
@EntityListeners({ AuditingEntityListener.class, EntityPropertyChangeListener.class, EntityInterceptorListener.class })
|
||||
public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
protected static final int USERNAME_FIELD_LENGTH = 64;
|
||||
|
||||
protected static final int USERNAME_FIELD_LENGTH = 64;
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
@@ -88,6 +87,34 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
||||
return lastModifiedBy;
|
||||
}
|
||||
|
||||
@LastModifiedBy
|
||||
public void setLastModifiedBy(final String lastModifiedBy) {
|
||||
if (isController()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastModifiedBy = lastModifiedBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOptLockRevision() {
|
||||
return optLockRevision;
|
||||
}
|
||||
|
||||
public void setOptLockRevision(final int optLockRevision) {
|
||||
this.optLockRevision = optLockRevision;
|
||||
}
|
||||
|
||||
@LastModifiedDate
|
||||
public void setLastModifiedAt(final long lastModifiedAt) {
|
||||
|
||||
if (isController()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastModifiedAt = lastModifiedAt;
|
||||
}
|
||||
|
||||
@CreatedBy
|
||||
public void setCreatedBy(final String createdBy) {
|
||||
if (isController()) {
|
||||
@@ -115,52 +142,11 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
||||
}
|
||||
}
|
||||
|
||||
@LastModifiedDate
|
||||
public void setLastModifiedAt(final long lastModifiedAt) {
|
||||
|
||||
if (isController()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastModifiedAt = lastModifiedAt;
|
||||
}
|
||||
|
||||
@LastModifiedBy
|
||||
public void setLastModifiedBy(final String lastModifiedBy) {
|
||||
if (isController()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastModifiedBy = lastModifiedBy;
|
||||
}
|
||||
|
||||
private boolean isController() {
|
||||
return SecurityContextHolder.getContext().getAuthentication() != null
|
||||
&& SecurityContextHolder.getContext().getAuthentication()
|
||||
.getDetails() instanceof TenantAwareAuthenticationDetails
|
||||
&& ((TenantAwareAuthenticationDetails) SecurityContextHolder.getContext().getAuthentication()
|
||||
.getDetails()).isController();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOptLockRevision() {
|
||||
return optLockRevision;
|
||||
}
|
||||
|
||||
public void setOptLockRevision(final int optLockRevision) {
|
||||
this.optLockRevision = optLockRevision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + " [id=" + id + "]";
|
||||
}
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
@@ -169,7 +155,7 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
||||
* Defined equals/hashcode strategy for the repository in general is that an
|
||||
* entity is equal if it has the same {@link #getId()} and
|
||||
* {@link #getOptLockRevision()} and class.
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
@@ -188,7 +174,7 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
||||
* Defined equals/hashcode strategy for the repository in general is that an
|
||||
* entity is equal if it has the same {@link #getId()} and
|
||||
* {@link #getOptLockRevision()} and class.
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@@ -213,4 +199,17 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
||||
return optLockRevision == other.optLockRevision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + " [id=" + id + "]";
|
||||
}
|
||||
|
||||
private boolean isController() {
|
||||
return SecurityContextHolder.getContext().getAuthentication() != null
|
||||
&& SecurityContextHolder.getContext().getAuthentication()
|
||||
.getDetails() instanceof TenantAwareAuthenticationDetails
|
||||
&& ((TenantAwareAuthenticationDetails) SecurityContextHolder.getContext().getAuthentication()
|
||||
.getDetails()).isController();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
|
||||
/**
|
||||
* Meta data for entities.
|
||||
*
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class AbstractJpaMetaData implements MetaData {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@@ -66,6 +66,11 @@ public abstract class AbstractJpaMetaData implements MetaData {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o)
|
||||
@@ -75,9 +80,4 @@ public abstract class AbstractJpaMetaData implements MetaData {
|
||||
final AbstractJpaMetaData that = (AbstractJpaMetaData) o;
|
||||
return Objects.equals(key, that.key) && Objects.equals(value, that.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
@@ -17,8 +19,6 @@ import jakarta.validation.constraints.Size;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* {@link TenantAwareBaseEntity} extension for all entities that are named in
|
||||
* addition to their technical ID.
|
||||
@@ -51,10 +51,8 @@ public abstract class AbstractJpaNamedEntity extends AbstractJpaTenantAwareBaseE
|
||||
/**
|
||||
* Parameterized constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the {@link NamedEntity}
|
||||
* @param description
|
||||
* of the {@link NamedEntity}
|
||||
* @param name of the {@link NamedEntity}
|
||||
* @param description of the {@link NamedEntity}
|
||||
*/
|
||||
AbstractJpaNamedEntity(final String name, final String description) {
|
||||
this.name = name;
|
||||
@@ -71,11 +69,11 @@ public abstract class AbstractJpaNamedEntity extends AbstractJpaTenantAwareBaseE
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setDescription(final String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setDescription(final String description) {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ import org.eclipse.hawkbit.repository.model.NamedVersionedEntity;
|
||||
|
||||
/**
|
||||
* Extension for {@link NamedEntity} that are versioned.
|
||||
*
|
||||
*/
|
||||
@MappedSuperclass
|
||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
|
||||
// sub entities
|
||||
@SuppressWarnings("squid:S2160")
|
||||
public abstract class AbstractJpaNamedVersionedEntity extends AbstractJpaNamedEntity implements NamedVersionedEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "version", nullable = false, length = NamedVersionedEntity.VERSION_MAX_SIZE)
|
||||
@@ -36,10 +36,8 @@ public abstract class AbstractJpaNamedVersionedEntity extends AbstractJpaNamedEn
|
||||
/**
|
||||
* parameterized constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the entity
|
||||
* @param version
|
||||
* of the entity
|
||||
* @param name of the entity
|
||||
* @param version of the entity
|
||||
* @param description
|
||||
*/
|
||||
AbstractJpaNamedVersionedEntity(final String name, final String version, final String description) {
|
||||
|
||||
@@ -25,12 +25,12 @@ import org.eclipse.persistence.annotations.TenantDiscriminatorColumn;
|
||||
|
||||
/**
|
||||
* Holder of the base attributes common to all tenant aware entities.
|
||||
*
|
||||
*/
|
||||
@MappedSuperclass
|
||||
@TenantDiscriminatorColumn(name = "tenant", length = 40)
|
||||
@Multitenant(MultitenantType.SINGLE_TABLE)
|
||||
public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEntity implements TenantAwareBaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "tenant", nullable = false, insertable = false, updatable = false, length = 40)
|
||||
@@ -45,24 +45,6 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
||||
// Default constructor needed for JPA entities.
|
||||
}
|
||||
|
||||
/**
|
||||
* PrePersist listener method for all {@link TenantAwareBaseEntity}
|
||||
* entities.
|
||||
*/
|
||||
@PrePersist
|
||||
void prePersist() {
|
||||
// before persisting the entity check the current ID of the tenant by
|
||||
// using the TenantAware
|
||||
// service
|
||||
final String currentTenant = SystemManagementHolder.getInstance().currentTenant();
|
||||
if (currentTenant == null) {
|
||||
throw new TenantNotExistException("Tenant "
|
||||
+ TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant()
|
||||
+ " does not exists, cannot create entity " + this.getClass() + " with id " + super.getId());
|
||||
}
|
||||
setTenant(currentTenant.toUpperCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTenant() {
|
||||
return tenant;
|
||||
@@ -72,17 +54,12 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BaseEntity [id=" + super.getId() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenant aware entities extend the equals/hashcode strategy with the tenant
|
||||
* name. That would allow for instance in a multi-schema based data
|
||||
* separation setup to have the same primary key for different entities of
|
||||
* different tenants.
|
||||
*
|
||||
*
|
||||
* @see org.eclipse.hawkbit.repository.model.BaseEntity#hashCode()
|
||||
*/
|
||||
@Override
|
||||
@@ -98,7 +75,7 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
||||
* name. That would allow for instance in a multi-schema based data
|
||||
* separation setup to have the same primary key for different entities of
|
||||
* different tenants.
|
||||
*
|
||||
*
|
||||
* @see org.eclipse.hawkbit.repository.model.BaseEntity#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@@ -119,4 +96,27 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BaseEntity [id=" + super.getId() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* PrePersist listener method for all {@link TenantAwareBaseEntity}
|
||||
* entities.
|
||||
*/
|
||||
@PrePersist
|
||||
void prePersist() {
|
||||
// before persisting the entity check the current ID of the tenant by
|
||||
// using the TenantAware
|
||||
// service
|
||||
final String currentTenant = SystemManagementHolder.getInstance().currentTenant();
|
||||
if (currentTenant == null) {
|
||||
throw new TenantNotExistException("Tenant "
|
||||
+ TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant()
|
||||
+ " does not exists, cannot create entity " + this.getClass() + " with id " + super.getId());
|
||||
}
|
||||
setTenant(currentTenant.toUpperCase());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Type;
|
||||
import java.io.Serial;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.io.Serial;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Type;
|
||||
|
||||
/**
|
||||
* {@link TenantAwareBaseEntity} extension for all entities that are named in
|
||||
@@ -49,10 +50,8 @@ public abstract class AbstractJpaTypeEntity extends AbstractJpaNamedEntity imple
|
||||
/**
|
||||
* Parameterized constructor.
|
||||
*
|
||||
* @param key
|
||||
* of the {@link Type}
|
||||
* @param colour
|
||||
* of the {@link Type}
|
||||
* @param key of the {@link Type}
|
||||
* @param colour of the {@link Type}
|
||||
*/
|
||||
AbstractJpaTypeEntity(final String name, final String description, final String key, final String colour) {
|
||||
super(name, description);
|
||||
@@ -70,11 +69,11 @@ public abstract class AbstractJpaTypeEntity extends AbstractJpaNamedEntity imple
|
||||
return colour;
|
||||
}
|
||||
|
||||
public void setKey(final String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void setColour(final String colour) {
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
public void setKey(final String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||
/**
|
||||
* Relation element between a {@link DistributionSetType} and its
|
||||
* {@link SoftwareModuleType} elements.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_ds_type_element")
|
||||
public class DistributionSetTypeElement implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@EmbeddedId
|
||||
@@ -62,13 +62,10 @@ public class DistributionSetTypeElement implements Serializable {
|
||||
/**
|
||||
* Standard constructor.
|
||||
*
|
||||
* @param dsType
|
||||
* of the element
|
||||
* @param smType
|
||||
* of the element
|
||||
* @param mandatory
|
||||
* to <code>true</code> if the {@link SoftwareModuleType} if
|
||||
* mandatory element in the {@link DistributionSet}.
|
||||
* @param dsType of the element
|
||||
* @param smType of the element
|
||||
* @param mandatory to <code>true</code> if the {@link SoftwareModuleType} if
|
||||
* mandatory element in the {@link DistributionSet}.
|
||||
*/
|
||||
DistributionSetTypeElement(final JpaDistributionSetType dsType, final JpaSoftwareModuleType smType,
|
||||
final boolean mandatory) {
|
||||
@@ -78,15 +75,15 @@ public class DistributionSetTypeElement implements Serializable {
|
||||
this.mandatory = mandatory;
|
||||
}
|
||||
|
||||
public boolean isMandatory() {
|
||||
return mandatory;
|
||||
}
|
||||
|
||||
DistributionSetTypeElement setMandatory(final boolean mandatory) {
|
||||
this.mandatory = mandatory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isMandatory() {
|
||||
return mandatory;
|
||||
}
|
||||
|
||||
public DistributionSetType getDsType() {
|
||||
return dsType;
|
||||
}
|
||||
@@ -99,11 +96,6 @@ public class DistributionSetTypeElement implements Serializable {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DistributionSetTypeElement [mandatory=" + mandatory + ", dsType=" + dsType + ", smType=" + smType + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
@@ -134,4 +126,9 @@ public class DistributionSetTypeElement implements Serializable {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DistributionSetTypeElement [mandatory=" + mandatory + ", dsType=" + dsType + ", smType=" + smType + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import jakarta.persistence.Embeddable;
|
||||
*/
|
||||
@Embeddable
|
||||
public class DistributionSetTypeElementCompositeKey implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "distribution_set_type", nullable = false, updatable = false)
|
||||
@@ -36,10 +37,8 @@ public class DistributionSetTypeElementCompositeKey implements Serializable {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param dsType
|
||||
* in the key
|
||||
* @param smType
|
||||
* in the key
|
||||
* @param dsType in the key
|
||||
* @param smType in the key
|
||||
*/
|
||||
DistributionSetTypeElementCompositeKey(final JpaDistributionSetType dsType, final JpaSoftwareModuleType smType) {
|
||||
this.dsType = dsType.getId();
|
||||
|
||||
@@ -14,9 +14,9 @@ import java.io.Serializable;
|
||||
/**
|
||||
* The DistributionSet Metadata composite key which contains the meta data key
|
||||
* and the ID of the DistributionSet itself.
|
||||
*
|
||||
*/
|
||||
public final class DsMetadataCompositeKey implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String key;
|
||||
@@ -28,10 +28,8 @@ public final class DsMetadataCompositeKey implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param distributionSet
|
||||
* the distribution set for this meta data
|
||||
* @param key
|
||||
* the key of the meta data
|
||||
* @param distributionSet the distribution set for this meta data
|
||||
* @param key the key of the meta data
|
||||
*/
|
||||
public DsMetadataCompositeKey(final Long distributionSet, final String key) {
|
||||
this.distributionSet = distributionSet;
|
||||
|
||||
@@ -31,8 +31,7 @@ public class EntityInterceptorListener {
|
||||
/**
|
||||
* Callback for lifecyle event <i>pre persist</i>.
|
||||
*
|
||||
* @param entity
|
||||
* the JPA entity which this listener is associated with
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PrePersist
|
||||
public void prePersist(final Object entity) {
|
||||
@@ -42,8 +41,7 @@ public class EntityInterceptorListener {
|
||||
/**
|
||||
* Callback for lifecyle event <i>post persist</i>.
|
||||
*
|
||||
* @param entity
|
||||
* the JPA entity which this listener is associated with
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PostPersist
|
||||
public void postPersist(final Object entity) {
|
||||
@@ -53,8 +51,7 @@ public class EntityInterceptorListener {
|
||||
/**
|
||||
* Callback for lifecyle event <i>post remove</i>.
|
||||
*
|
||||
* @param entity
|
||||
* the JPA entity which this listener is associated with
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PostRemove
|
||||
public void postRemove(final Object entity) {
|
||||
@@ -64,8 +61,7 @@ public class EntityInterceptorListener {
|
||||
/**
|
||||
* Callback for lifecyle event <i>pre remove</i>.
|
||||
*
|
||||
* @param entity
|
||||
* the JPA entity which this listener is associated with
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PreRemove
|
||||
public void preRemove(final Object entity) {
|
||||
@@ -75,8 +71,7 @@ public class EntityInterceptorListener {
|
||||
/**
|
||||
* Callback for lifecyle event <i>post load</i>.
|
||||
*
|
||||
* @param entity
|
||||
* the JPA entity which this listener is associated with
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PostLoad
|
||||
public void postLoad(final Object entity) {
|
||||
@@ -86,8 +81,7 @@ public class EntityInterceptorListener {
|
||||
/**
|
||||
* Callback for lifecyle event <i>pre update</i>.
|
||||
*
|
||||
* @param entity
|
||||
* the JPA entity which this listener is associated with
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PreUpdate
|
||||
public void preUpdate(final Object entity) {
|
||||
@@ -97,8 +91,7 @@ public class EntityInterceptorListener {
|
||||
/**
|
||||
* Callback for lifecyle event <i>post update</i>.
|
||||
*
|
||||
* @param entity
|
||||
* the JPA entity which this listener is associated with
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PostUpdate
|
||||
public void postUpdate(final Object entity) {
|
||||
|
||||
@@ -17,10 +17,17 @@ import org.eclipse.persistence.queries.UpdateObjectQuery;
|
||||
/**
|
||||
* Listens to change in property values of an entity and calls the corresponding
|
||||
* {@link EventAwareEntity}.
|
||||
*
|
||||
*/
|
||||
public class EntityPropertyChangeListener extends DescriptorEventAdapter {
|
||||
|
||||
@Override
|
||||
public void postDelete(final DescriptorEvent event) {
|
||||
final Object object = event.getObject();
|
||||
if (isEventAwareEntity(object)) {
|
||||
doNotifiy(() -> ((EventAwareEntity) object).fireDeleteEvent(event));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInsert(final DescriptorEvent event) {
|
||||
final Object object = event.getObject();
|
||||
@@ -40,14 +47,6 @@ public class EntityPropertyChangeListener extends DescriptorEventAdapter {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDelete(final DescriptorEvent event) {
|
||||
final Object object = event.getObject();
|
||||
if (isEventAwareEntity(object)) {
|
||||
doNotifiy(() -> ((EventAwareEntity) object).fireDeleteEvent(event));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isEventAwareEntity(final Object object) {
|
||||
return object instanceof EventAwareEntity;
|
||||
}
|
||||
|
||||
@@ -22,21 +22,21 @@ public interface EventAwareEntity {
|
||||
|
||||
/**
|
||||
* Fired for the Entity creation.
|
||||
*
|
||||
*
|
||||
* @param descriptorEvent
|
||||
*/
|
||||
void fireCreateEvent(DescriptorEvent descriptorEvent);
|
||||
|
||||
/**
|
||||
* Fired for the Entity updation.
|
||||
*
|
||||
*
|
||||
* @param descriptorEvent
|
||||
*/
|
||||
void fireUpdateEvent(DescriptorEvent descriptorEvent);
|
||||
|
||||
/**
|
||||
* Fired for the Entity deletion.
|
||||
*
|
||||
*
|
||||
* @param descriptorEvent
|
||||
*/
|
||||
void fireDeleteEvent(DescriptorEvent descriptorEvent);
|
||||
|
||||
@@ -67,6 +67,7 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
// sub entities
|
||||
@SuppressWarnings("squid:S2160")
|
||||
public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Action, EventAwareEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@@ -113,7 +114,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
@ConversionValue(objectValue = "SCHEDULED", dataValue = "8"),
|
||||
@ConversionValue(objectValue = "CANCEL_REJECTED", dataValue = "9"),
|
||||
@ConversionValue(objectValue = "DOWNLOADED", dataValue = "10"),
|
||||
@ConversionValue(objectValue = "WAIT_FOR_CONFIRMATION", dataValue = "11")})
|
||||
@ConversionValue(objectValue = "WAIT_FOR_CONFIRMATION", dataValue = "11") })
|
||||
@Convert("status")
|
||||
@NotNull
|
||||
private Status status;
|
||||
@@ -157,10 +158,6 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
this.distributionSet = (JpaDistributionSet) distributionSet;
|
||||
}
|
||||
|
||||
public void setActive(final boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
@@ -175,8 +172,8 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActionType(final ActionType actionType) {
|
||||
this.actionType = actionType;
|
||||
public void setActive(final boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,16 +181,8 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public List<ActionStatus> getActionStatus() {
|
||||
if (actionStatus == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(actionStatus);
|
||||
}
|
||||
|
||||
public void setTarget(final Target target) {
|
||||
this.target = (JpaTarget) target;
|
||||
public void setActionType(final ActionType actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -201,6 +190,10 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget(final Target target) {
|
||||
this.target = (JpaTarget) target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getForcedTime() {
|
||||
return forcedTime;
|
||||
@@ -237,35 +230,6 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
this.rollout = (JpaRollout) rollout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JpaAction [distributionSet=" + distributionSet.getId() + ", version=" + getOptLockRevision() + ", id="
|
||||
+ getId() + ", actionType=" + getActionType() + ", weight=" + getWeight() + ", isActive=" + isActive()
|
||||
+ ", createdAt=" + getCreatedAt() + ", lastModifiedAt=" + getLastModifiedAt() + ", status="
|
||||
+ getStatus().name() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new ActionCreatedEvent(this, BaseEntity.getIdOrNull(target),
|
||||
BaseEntity.getIdOrNull(rollout), BaseEntity.getIdOrNull(rolloutGroup),
|
||||
EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new ActionUpdatedEvent(this, BaseEntity.getIdOrNull(target),
|
||||
BaseEntity.getIdOrNull(rollout), BaseEntity.getIdOrNull(rolloutGroup),
|
||||
EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
|
||||
// there is no action deletion
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMaintenanceWindowSchedule() {
|
||||
return maintenanceWindowSchedule;
|
||||
@@ -274,8 +238,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
/**
|
||||
* Sets the maintenance schedule.
|
||||
*
|
||||
* @param maintenanceWindowSchedule
|
||||
* is a cron expression to be used for scheduling.
|
||||
* @param maintenanceWindowSchedule is a cron expression to be used for scheduling.
|
||||
*/
|
||||
public void setMaintenanceWindowSchedule(final String maintenanceWindowSchedule) {
|
||||
this.maintenanceWindowSchedule = maintenanceWindowSchedule;
|
||||
@@ -289,9 +252,8 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
/**
|
||||
* Sets the maintenance window duration.
|
||||
*
|
||||
* @param maintenanceWindowDuration
|
||||
* is the duration of an available maintenance schedule in
|
||||
* HH:mm:ss format.
|
||||
* @param maintenanceWindowDuration is the duration of an available maintenance schedule in
|
||||
* HH:mm:ss format.
|
||||
*/
|
||||
public void setMaintenanceWindowDuration(final String maintenanceWindowDuration) {
|
||||
this.maintenanceWindowDuration = maintenanceWindowDuration;
|
||||
@@ -305,34 +267,45 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
/**
|
||||
* Sets the time zone to be used for maintenance window.
|
||||
*
|
||||
* @param maintenanceWindowTimeZone
|
||||
* is the time zone specified as +/-hh:mm offset from UTC for
|
||||
* example +02:00 for CET summer time and +00:00 for UTC. The
|
||||
* start time of a maintenance window calculated based on the
|
||||
* cron expression is relative to this time zone.
|
||||
* @param maintenanceWindowTimeZone is the time zone specified as +/-hh:mm offset from UTC for
|
||||
* example +02:00 for CET summer time and +00:00 for UTC. The
|
||||
* start time of a maintenance window calculated based on the
|
||||
* cron expression is relative to this time zone.
|
||||
*/
|
||||
public void setMaintenanceWindowTimeZone(final String maintenanceWindowTimeZone) {
|
||||
this.maintenanceWindowTimeZone = maintenanceWindowTimeZone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExternalRef() {
|
||||
return externalRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExternalRef(final String externalRef) {
|
||||
this.externalRef = externalRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInitiatedBy() {
|
||||
return initiatedBy;
|
||||
}
|
||||
|
||||
public void setInitiatedBy(final String initiatedBy) {
|
||||
this.initiatedBy = initiatedBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> getLastActionStatusCode() {
|
||||
return Optional.ofNullable(lastActionStatusCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ZonedDateTime> getMaintenanceWindowStartTime() {
|
||||
return MaintenanceScheduleHelper.getNextMaintenanceWindow(maintenanceWindowSchedule, maintenanceWindowDuration,
|
||||
maintenanceWindowTimeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end time of next available or active maintenance window for
|
||||
* the {@link Action} as {@link ZonedDateTime}. If a maintenance window is
|
||||
* already active, the end time of currently active window is returned.
|
||||
*
|
||||
* @return the end time of window as { @link Optional<ZonedDateTime>}.
|
||||
*/
|
||||
private Optional<ZonedDateTime> getMaintenanceWindowEndTime() {
|
||||
return getMaintenanceWindowStartTime()
|
||||
.map(start -> start.plus(MaintenanceScheduleHelper.convertToISODuration(maintenanceWindowDuration)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMaintenanceSchedule() {
|
||||
return this.maintenanceWindowSchedule != null;
|
||||
@@ -366,35 +339,60 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExternalRef(final String externalRef) {
|
||||
this.externalRef = externalRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExternalRef() {
|
||||
return externalRef;
|
||||
}
|
||||
|
||||
public void setInitiatedBy(final String initiatedBy) {
|
||||
this.initiatedBy = initiatedBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInitiatedBy() {
|
||||
return initiatedBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> getLastActionStatusCode() {
|
||||
return Optional.ofNullable(lastActionStatusCode);
|
||||
public boolean isWaitingConfirmation() {
|
||||
return status == Status.WAIT_FOR_CONFIRMATION;
|
||||
}
|
||||
|
||||
public void setLastActionStatusCode(final Integer lastActionStatusCode) {
|
||||
this.lastActionStatusCode = lastActionStatusCode;
|
||||
}
|
||||
|
||||
public boolean isWaitingConfirmation() {
|
||||
return status == Status.WAIT_FOR_CONFIRMATION;
|
||||
public List<ActionStatus> getActionStatus() {
|
||||
if (actionStatus == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(actionStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JpaAction [distributionSet=" + distributionSet.getId() + ", version=" + getOptLockRevision() + ", id="
|
||||
+ getId() + ", actionType=" + getActionType() + ", weight=" + getWeight() + ", isActive=" + isActive()
|
||||
+ ", createdAt=" + getCreatedAt() + ", lastModifiedAt=" + getLastModifiedAt() + ", status="
|
||||
+ getStatus().name() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new ActionCreatedEvent(this, BaseEntity.getIdOrNull(target),
|
||||
BaseEntity.getIdOrNull(rollout), BaseEntity.getIdOrNull(rolloutGroup),
|
||||
EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new ActionUpdatedEvent(this, BaseEntity.getIdOrNull(target),
|
||||
BaseEntity.getIdOrNull(rollout), BaseEntity.getIdOrNull(rolloutGroup),
|
||||
EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
|
||||
// there is no action deletion
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end time of next available or active maintenance window for
|
||||
* the {@link Action} as {@link ZonedDateTime}. If a maintenance window is
|
||||
* already active, the end time of currently active window is returned.
|
||||
*
|
||||
* @return the end time of window as { @link Optional<ZonedDateTime>}.
|
||||
*/
|
||||
private Optional<ZonedDateTime> getMaintenanceWindowEndTime() {
|
||||
return getMaintenanceWindowStartTime()
|
||||
.map(start -> start.plus(MaintenanceScheduleHelper.convertToISODuration(maintenanceWindowDuration)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
import jakarta.persistence.CollectionTable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
@@ -50,6 +49,7 @@ import org.eclipse.persistence.annotations.ObjectTypeConverter;
|
||||
// sub entities
|
||||
@SuppressWarnings("squid:S2160")
|
||||
public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements ActionStatus {
|
||||
|
||||
private static final int MESSAGE_ENTRY_LENGTH = 512;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -75,7 +75,7 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
||||
@ConversionValue(objectValue = "SCHEDULED", dataValue = "8"),
|
||||
@ConversionValue(objectValue = "CANCEL_REJECTED", dataValue = "9"),
|
||||
@ConversionValue(objectValue = "DOWNLOADED", dataValue = "10"),
|
||||
@ConversionValue(objectValue = "WAIT_FOR_CONFIRMATION", dataValue = "11")})
|
||||
@ConversionValue(objectValue = "WAIT_FOR_CONFIRMATION", dataValue = "11") })
|
||||
@Convert("status")
|
||||
@NotNull
|
||||
private Status status;
|
||||
@@ -89,16 +89,13 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
||||
|
||||
@Column(name = "code", nullable = true, updatable = false)
|
||||
private Integer code;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link ActionStatus} object.
|
||||
*
|
||||
* @param action
|
||||
* the action for this action status
|
||||
* @param status
|
||||
* the status for this action status
|
||||
* @param occurredAt
|
||||
* the occurred timestamp
|
||||
* @param action the action for this action status
|
||||
* @param status the status for this action status
|
||||
* @param occurredAt the occurred timestamp
|
||||
*/
|
||||
public JpaActionStatus(final Action action, final Status status, final long occurredAt) {
|
||||
this.action = (JpaAction) action;
|
||||
@@ -109,14 +106,10 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
||||
/**
|
||||
* Creates a new {@link ActionStatus} object.
|
||||
*
|
||||
* @param action
|
||||
* the action for this action status
|
||||
* @param status
|
||||
* the status for this action status
|
||||
* @param occurredAt
|
||||
* the occurred timestamp
|
||||
* @param message
|
||||
* the message which should be added to this action status
|
||||
* @param action the action for this action status
|
||||
* @param status the status for this action status
|
||||
* @param occurredAt the occurred timestamp
|
||||
* @param message the message which should be added to this action status
|
||||
*/
|
||||
public JpaActionStatus(final JpaAction action, final Status status, final long occurredAt, final String message) {
|
||||
this.action = action;
|
||||
@@ -128,10 +121,8 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
||||
/**
|
||||
* Creates a new {@link ActionStatus} object.
|
||||
*
|
||||
* @param status
|
||||
* the status for this action status
|
||||
* @param occurredAt
|
||||
* the occurred timestamp
|
||||
* @param status the status for this action status
|
||||
* @param occurredAt the occurred timestamp
|
||||
*/
|
||||
public JpaActionStatus(final Status status, final long occurredAt) {
|
||||
this.status = status;
|
||||
@@ -154,36 +145,6 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
||||
this.occurredAt = occurredAt;
|
||||
}
|
||||
|
||||
public final void addMessage(final String message) {
|
||||
if (message != null) {
|
||||
if (messages == null) {
|
||||
messages = new ArrayList<>((message.length() / MESSAGE_ENTRY_LENGTH) + 1);
|
||||
}
|
||||
if (message.length() > MESSAGE_ENTRY_LENGTH) {
|
||||
// split
|
||||
for (int off = 0; off < message.length();) {
|
||||
final int end = off + MESSAGE_ENTRY_LENGTH;
|
||||
if (end < message.length()) {
|
||||
messages.add(message.substring(off, end));
|
||||
} else {
|
||||
messages.add(message.substring(off));
|
||||
}
|
||||
off = end;
|
||||
}
|
||||
} else {
|
||||
messages.add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getMessages() {
|
||||
if (messages == null) {
|
||||
messages = Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action getAction() {
|
||||
return action;
|
||||
@@ -209,4 +170,34 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
||||
public void setCode(final Integer code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public final void addMessage(final String message) {
|
||||
if (message != null) {
|
||||
if (messages == null) {
|
||||
messages = new ArrayList<>((message.length() / MESSAGE_ENTRY_LENGTH) + 1);
|
||||
}
|
||||
if (message.length() > MESSAGE_ENTRY_LENGTH) {
|
||||
// split
|
||||
for (int off = 0; off < message.length(); ) {
|
||||
final int end = off + MESSAGE_ENTRY_LENGTH;
|
||||
if (end < message.length()) {
|
||||
messages.add(message.substring(off, end));
|
||||
} else {
|
||||
messages.add(message.substring(off));
|
||||
}
|
||||
off = end;
|
||||
}
|
||||
} else {
|
||||
messages.add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getMessages() {
|
||||
if (messages == null) {
|
||||
messages = Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(messages);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link Artifact}.
|
||||
*
|
||||
*/
|
||||
@Table(name = "sp_artifact", indexes = { @Index(name = "sp_idx_artifact_01", columnList = "tenant,software_module"),
|
||||
@Index(name = "sp_idx_artifact_02", columnList = "tenant,sha1_hash"),
|
||||
@@ -39,6 +38,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
// sub entities
|
||||
@SuppressWarnings("squid:S2160")
|
||||
public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Artifact {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "sha1_hash", length = 40, nullable = false, updatable = false)
|
||||
@@ -74,12 +74,9 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art
|
||||
/**
|
||||
* Constructs artifact.
|
||||
*
|
||||
* @param sha1Hash
|
||||
* that is the link to the {@link AbstractDbArtifact} entity.
|
||||
* @param filename
|
||||
* that is used by {@link AbstractDbArtifact} store.
|
||||
* @param softwareModule
|
||||
* of this artifact
|
||||
* @param sha1Hash that is the link to the {@link AbstractDbArtifact} entity.
|
||||
* @param filename that is used by {@link AbstractDbArtifact} store.
|
||||
* @param softwareModule of this artifact
|
||||
*/
|
||||
public JpaArtifact(@NotEmpty final String sha1Hash, @NotNull final String filename,
|
||||
final SoftwareModule softwareModule) {
|
||||
@@ -89,6 +86,16 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art
|
||||
this.softwareModule.addArtifact(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModule getSoftwareModule() {
|
||||
return softwareModule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMd5Hash() {
|
||||
return md5Hash;
|
||||
@@ -104,19 +111,10 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art
|
||||
return sha256Hash;
|
||||
}
|
||||
|
||||
public void setMd5Hash(final String md5Hash) {
|
||||
this.md5Hash = md5Hash;
|
||||
}
|
||||
|
||||
public void setSha1Hash(final String sha1Hash) {
|
||||
this.sha1Hash = sha1Hash;
|
||||
}
|
||||
|
||||
public void setSha256Hash(final String sha256Hash) {
|
||||
this.sha256Hash = sha256Hash;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return size;
|
||||
@@ -126,13 +124,11 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModule getSoftwareModule() {
|
||||
return softwareModule;
|
||||
public void setSha1Hash(final String sha1Hash) {
|
||||
this.sha1Hash = sha1Hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
public void setMd5Hash(final String md5Hash) {
|
||||
this.md5Hash = md5Hash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.Entity;
|
||||
@@ -24,6 +19,11 @@ import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Entity
|
||||
@Table(name = "sp_target_conf_status")
|
||||
public class JpaAutoConfirmationStatus extends AbstractJpaTenantAwareBaseEntity implements AutoConfirmationStatus {
|
||||
@@ -81,11 +81,11 @@ public class JpaAutoConfirmationStatus extends AbstractJpaTenantAwareBaseEntity
|
||||
// https://docs.oracle.com/en/java/javase/17/text-blocks/index.html#normalization-of-line-terminators
|
||||
// nevertheless of the end of line of the file (\r\n, \n or \r) the result will contains \n
|
||||
return """
|
||||
Assignment automatically confirmed by initiator '%s'.\040
|
||||
|
||||
Auto confirmation activated by system user: '%s'\040
|
||||
|
||||
Remark: %s""".formatted(formattedInitiator, createdByRolloutsUser, remarkMessage);
|
||||
Assignment automatically confirmed by initiator '%s'.\040
|
||||
|
||||
Auto confirmation activated by system user: '%s'\040
|
||||
|
||||
Remark: %s""".formatted(formattedInitiator, createdByRolloutsUser, remarkMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -67,8 +67,8 @@ import org.springframework.context.ApplicationEvent;
|
||||
@Entity
|
||||
@Table(name = "sp_distribution_set", uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = { "name", "version", "tenant" }, name = "uk_distrib_set") }, indexes = {
|
||||
@Index(name = "sp_idx_distribution_set_01", columnList = "tenant,deleted,complete"),
|
||||
@Index(name = "sp_idx_distribution_set_prim", columnList = "tenant,id") })
|
||||
@Index(name = "sp_idx_distribution_set_01", columnList = "tenant,deleted,complete"),
|
||||
@Index(name = "sp_idx_distribution_set_prim", columnList = "tenant,id") })
|
||||
@NamedEntityGraph(name = "DistributionSet.detail", attributeNodes = { @NamedAttributeNode("modules"),
|
||||
@NamedAttributeNode("tags"), @NamedAttributeNode("type") })
|
||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
|
||||
@@ -90,7 +90,7 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
||||
@ManyToMany(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "sp_ds_module", joinColumns = {
|
||||
@JoinColumn(name = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_ds")) }, inverseJoinColumns = {
|
||||
@JoinColumn(name = "module_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_module")) })
|
||||
@JoinColumn(name = "module_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_module")) })
|
||||
private Set<SoftwareModule> modules;
|
||||
|
||||
@CascadeOnDelete
|
||||
@@ -284,18 +284,6 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
||||
EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
private void checkTypeCompatability(final SoftwareModule softwareModule) {
|
||||
// we cannot allow that modules are added without a type defined
|
||||
if (type == null) {
|
||||
throw new DistributionSetTypeUndefinedException();
|
||||
}
|
||||
|
||||
// check if it is allowed to such a module to this DS type
|
||||
if (!type.containsModuleType(softwareModule.getType())) {
|
||||
throw new UnsupportedSoftwareModuleForThisDistributionSetException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void publishEventWithEventPublisher(final ApplicationEvent event) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(event);
|
||||
}
|
||||
@@ -309,4 +297,16 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
||||
return changes.stream().anyMatch(changeRecord -> DELETED_PROPERTY.equals(changeRecord.getAttribute())
|
||||
&& Boolean.parseBoolean(changeRecord.getNewValue().toString()));
|
||||
}
|
||||
|
||||
private void checkTypeCompatability(final SoftwareModule softwareModule) {
|
||||
// we cannot allow that modules are added without a type defined
|
||||
if (type == null) {
|
||||
throw new DistributionSetTypeUndefinedException();
|
||||
}
|
||||
|
||||
// check if it is allowed to such a module to this DS type
|
||||
if (!type.containsModuleType(softwareModule.getType())) {
|
||||
throw new UnsupportedSoftwareModuleForThisDistributionSetException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,12 @@ import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||
|
||||
/**
|
||||
* Meta data for {@link DistributionSet}.
|
||||
*
|
||||
*/
|
||||
@IdClass(DsMetadataCompositeKey.class)
|
||||
@Entity
|
||||
@Table(name = "sp_ds_metadata")
|
||||
public class JpaDistributionSetMetadata extends AbstractJpaMetaData implements DistributionSetMetadata {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@@ -54,15 +54,15 @@ public class JpaDistributionSetMetadata extends AbstractJpaMetaData implements D
|
||||
return new DsMetadataCompositeKey(distributionSet.getId(), getKey());
|
||||
}
|
||||
|
||||
public void setDistributionSet(final DistributionSet distributionSet) {
|
||||
this.distributionSet = (JpaDistributionSet) distributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSet getDistributionSet() {
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
public void setDistributionSet(final DistributionSet distributionSet) {
|
||||
this.distributionSet = (JpaDistributionSet) distributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
@@ -31,14 +30,14 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
/**
|
||||
* A {@link DistributionSetTag} is used to describe DistributionSet attributes
|
||||
* and use them also for filtering the DistributionSet list.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_distributionset_tag", indexes = {
|
||||
@Index(name = "sp_idx_distribution_set_tag_prim", columnList = "tenant,id"),
|
||||
@Index(name = "sp_idx_distribution_set_tag_01", columnList = "tenant,name") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||
"name", "tenant" }, name = "uk_ds_tag"))
|
||||
"name", "tenant" }, name = "uk_ds_tag"))
|
||||
public class JpaDistributionSetTag extends JpaTag implements DistributionSetTag, EventAwareEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@CascadeOnDelete
|
||||
@@ -48,12 +47,9 @@ public class JpaDistributionSetTag extends JpaTag implements DistributionSetTag,
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the {@link DistributionSetTag}
|
||||
* @param description
|
||||
* of the {@link DistributionSetTag}
|
||||
* @param colour
|
||||
* of tag in UI
|
||||
* @param name of the {@link DistributionSetTag}
|
||||
* @param description of the {@link DistributionSetTag}
|
||||
* @param colour of tag in UI
|
||||
*/
|
||||
public JpaDistributionSetTag(final String name, final String description, final String colour) {
|
||||
super(name, description, colour);
|
||||
|
||||
@@ -9,6 +9,24 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.repository.event.remote.DistributionSetTypeDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTypeCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTypeUpdatedEvent;
|
||||
@@ -22,34 +40,16 @@ import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
import java.io.Serial;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A distribution set type defines which software module types can or have to be
|
||||
* {@link DistributionSet}.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_distribution_set_type", indexes = {
|
||||
@Index(name = "sp_idx_distribution_set_type_01", columnList = "tenant,deleted"),
|
||||
@Index(name = "sp_idx_distribution_set_type_prim", columnList = "tenant,id") }, uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = { "name", "tenant" }, name = "uk_dst_name"),
|
||||
@UniqueConstraint(columnNames = { "type_key", "tenant" }, name = "uk_dst_key") })
|
||||
@UniqueConstraint(columnNames = { "name", "tenant" }, name = "uk_dst_name"),
|
||||
@UniqueConstraint(columnNames = { "type_key", "tenant" }, name = "uk_dst_key") })
|
||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
|
||||
// sub entities
|
||||
@SuppressWarnings("squid:S2160")
|
||||
@@ -77,12 +77,9 @@ public class JpaDistributionSetType extends AbstractJpaTypeEntity implements Dis
|
||||
/**
|
||||
* Standard constructor.
|
||||
*
|
||||
* @param key
|
||||
* of the type (unique)
|
||||
* @param name
|
||||
* of the type (unique)
|
||||
* @param description
|
||||
* of the type
|
||||
* @param key of the type (unique)
|
||||
* @param name of the type (unique)
|
||||
* @param description of the type
|
||||
*/
|
||||
public JpaDistributionSetType(final String key, final String name, final String description) {
|
||||
this(key, name, description, null);
|
||||
@@ -91,14 +88,10 @@ public class JpaDistributionSetType extends AbstractJpaTypeEntity implements Dis
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param key
|
||||
* of the type
|
||||
* @param name
|
||||
* of the type
|
||||
* @param description
|
||||
* of the type
|
||||
* @param colour
|
||||
* of the type. It will be null by default
|
||||
* @param key of the type
|
||||
* @param name of the type
|
||||
* @param description of the type
|
||||
* @param colour of the type. It will be null by default
|
||||
*/
|
||||
public JpaDistributionSetType(final String key, final String name, final String description, final String colour) {
|
||||
super(name, description, key, colour);
|
||||
@@ -144,15 +137,14 @@ public class JpaDistributionSetType extends AbstractJpaTypeEntity implements Dis
|
||||
return new HashSet<>(((JpaDistributionSetType) dsType).elements).equals(elements);
|
||||
}
|
||||
|
||||
private boolean isOneModuleListEmpty(final DistributionSetType dsType) {
|
||||
return (!CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements)
|
||||
&& CollectionUtils.isEmpty(elements))
|
||||
|| (CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements)
|
||||
&& !CollectionUtils.isEmpty(elements));
|
||||
}
|
||||
|
||||
private boolean areBothModuleListsEmpty(final DistributionSetType dsType) {
|
||||
return CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements) && CollectionUtils.isEmpty(elements);
|
||||
@Override
|
||||
public boolean checkComplete(final DistributionSet distributionSet) {
|
||||
final List<SoftwareModuleType> smTypes = distributionSet.getModules().stream().map(SoftwareModule::getType)
|
||||
.distinct().collect(Collectors.toList());
|
||||
if (smTypes.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return smTypes.containsAll(getMandatoryModuleTypes());
|
||||
}
|
||||
|
||||
public JpaDistributionSetType addOptionalModuleType(final SoftwareModuleType smType) {
|
||||
@@ -163,26 +155,6 @@ public class JpaDistributionSetType extends AbstractJpaTypeEntity implements Dis
|
||||
return setModuleType(smType, true);
|
||||
}
|
||||
|
||||
private JpaDistributionSetType setModuleType(final SoftwareModuleType smType, final boolean mandatory) {
|
||||
if (elements == null) {
|
||||
elements = new HashSet<>();
|
||||
elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, mandatory));
|
||||
return this;
|
||||
}
|
||||
|
||||
// check if this was in the list before before
|
||||
final Optional<DistributionSetTypeElement> existing = elements.stream()
|
||||
.filter(element -> element.getSmType().getKey().equals(smType.getKey())).findAny();
|
||||
|
||||
if (existing.isPresent()) {
|
||||
existing.get().setMandatory(mandatory);
|
||||
} else {
|
||||
elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, mandatory));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JpaDistributionSetType removeModuleType(final Long smTypeId) {
|
||||
if (elements == null) {
|
||||
return this;
|
||||
@@ -195,16 +167,6 @@ public class JpaDistributionSetType extends AbstractJpaTypeEntity implements Dis
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkComplete(final DistributionSet distributionSet) {
|
||||
final List<SoftwareModuleType> smTypes = distributionSet.getModules().stream().map(SoftwareModule::getType)
|
||||
.distinct().collect(Collectors.toList());
|
||||
if (smTypes.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return smTypes.containsAll(getMandatoryModuleTypes());
|
||||
}
|
||||
|
||||
public Set<DistributionSetTypeElement> getElements() {
|
||||
if (elements == null) {
|
||||
return Collections.emptySet();
|
||||
@@ -235,4 +197,35 @@ public class JpaDistributionSetType extends AbstractJpaTypeEntity implements Dis
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new DistributionSetTypeDeletedEvent(
|
||||
getTenant(), getId(), getClass(), EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
private boolean isOneModuleListEmpty(final DistributionSetType dsType) {
|
||||
return (!CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements)
|
||||
&& CollectionUtils.isEmpty(elements))
|
||||
|| (CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements)
|
||||
&& !CollectionUtils.isEmpty(elements));
|
||||
}
|
||||
|
||||
private boolean areBothModuleListsEmpty(final DistributionSetType dsType) {
|
||||
return CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements) && CollectionUtils.isEmpty(elements);
|
||||
}
|
||||
|
||||
private JpaDistributionSetType setModuleType(final SoftwareModuleType smType, final boolean mandatory) {
|
||||
if (elements == null) {
|
||||
elements = new HashSet<>();
|
||||
elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, mandatory));
|
||||
return this;
|
||||
}
|
||||
|
||||
// check if this was in the list before before
|
||||
final Optional<DistributionSetTypeElement> existing = elements.stream()
|
||||
.filter(element -> element.getSmType().getKey().equals(smType.getKey())).findAny();
|
||||
|
||||
if (existing.isPresent()) {
|
||||
existing.get().setMandatory(mandatory);
|
||||
} else {
|
||||
elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, mandatory));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
|
||||
|
||||
/**
|
||||
* JPA implementation of a {@link Rollout}.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_rollout", uniqueConstraints = @UniqueConstraint(columnNames = { "name",
|
||||
@@ -149,6 +148,62 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
@Transient
|
||||
private transient TotalTargetCountStatus totalTargetCountStatus;
|
||||
|
||||
public List<RolloutGroup> getRolloutGroups() {
|
||||
if (rolloutGroups == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(rolloutGroups);
|
||||
}
|
||||
|
||||
public long getLastCheck() {
|
||||
return lastCheck;
|
||||
}
|
||||
|
||||
public void setLastCheck(final long lastCheck) {
|
||||
this.lastCheck = lastCheck;
|
||||
}
|
||||
|
||||
// dynamic is null only for old rollouts - could be used for distinguishing
|
||||
// old once from the other
|
||||
public boolean isNewStyleTargetPercent() {
|
||||
return dynamic != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Rollout [ targetFilterQuery=" + targetFilterQuery + ", distributionSet=" + distributionSet + ", status="
|
||||
+ status + ", lastCheck=" + lastCheck + ", getName()=" + getName() + ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new RolloutCreatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new RolloutUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
|
||||
|
||||
if (isSoftDeleted(descriptorEvent)) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutDeletedEvent(getTenant(),
|
||||
getId(), getClass(), EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutDeletedEvent(getTenant(),
|
||||
getId(), getClass(), EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSet getDistributionSet() {
|
||||
return distributionSet;
|
||||
@@ -158,14 +213,6 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
this.distributionSet = (JpaDistributionSet) distributionSet;
|
||||
}
|
||||
|
||||
public List<RolloutGroup> getRolloutGroups() {
|
||||
if (rolloutGroups == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(rolloutGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTargetFilterQuery() {
|
||||
return targetFilterQuery;
|
||||
@@ -184,23 +231,6 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public long getLastCheck() {
|
||||
return lastCheck;
|
||||
}
|
||||
|
||||
public void setLastCheck(final long lastCheck) {
|
||||
this.lastCheck = lastCheck;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getStartAt() {
|
||||
return startAt;
|
||||
}
|
||||
|
||||
public void setStartAt(final Long startAt) {
|
||||
this.startAt = startAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionType getActionType() {
|
||||
return actionType;
|
||||
@@ -215,40 +245,13 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
return forcedTime;
|
||||
}
|
||||
|
||||
public void setForcedTime(final long forcedTime) {
|
||||
this.forcedTime = forcedTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> getWeight() {
|
||||
return Optional.ofNullable(weight);
|
||||
public Long getStartAt() {
|
||||
return startAt;
|
||||
}
|
||||
|
||||
public void setWeight(final Integer weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDynamic() {
|
||||
return Boolean.TRUE.equals(dynamic);
|
||||
}
|
||||
|
||||
public void setDynamic(final Boolean dynamic) {
|
||||
this.dynamic = dynamic;
|
||||
}
|
||||
|
||||
// dynamic is null only for old rollouts - could be used for distinguishing
|
||||
// old once from the other
|
||||
public boolean isNewStyleTargetPercent() {
|
||||
return dynamic != null;
|
||||
}
|
||||
|
||||
public Optional<String> getAccessControlContext() {
|
||||
return Optional.ofNullable(accessControlContext);
|
||||
}
|
||||
|
||||
public void setAccessControlContext(final String accessControlContext) {
|
||||
this.accessControlContext = accessControlContext;
|
||||
public void setStartAt(final Long startAt) {
|
||||
this.startAt = startAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -281,54 +284,6 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
this.totalTargetCountStatus = totalTargetCountStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Rollout [ targetFilterQuery=" + targetFilterQuery + ", distributionSet=" + distributionSet + ", status="
|
||||
+ status + ", lastCheck=" + lastCheck + ", getName()=" + getName() + ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new RolloutCreatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new RolloutUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
|
||||
|
||||
if (isSoftDeleted(descriptorEvent)) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutDeletedEvent(getTenant(),
|
||||
getId(), getClass(), EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isSoftDeleted(final DescriptorEvent event) {
|
||||
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
|
||||
final List<DirectToFieldChangeRecord> changes = changeSet.getChanges().stream()
|
||||
.filter(DirectToFieldChangeRecord.class::isInstance).map(DirectToFieldChangeRecord.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return changes.stream().anyMatch(changeRecord -> DELETED_PROPERTY.equals(changeRecord.getAttribute())
|
||||
&& Boolean.parseBoolean(changeRecord.getNewValue().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutDeletedEvent(getTenant(),
|
||||
getId(), getClass(), EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public void setDeleted(final boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApprovalDecidedBy() {
|
||||
return approvalDecidedBy;
|
||||
@@ -343,7 +298,51 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
return approvalRemark;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> getWeight() {
|
||||
return Optional.ofNullable(weight);
|
||||
}
|
||||
|
||||
public void setWeight(final Integer weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDynamic() {
|
||||
return Boolean.TRUE.equals(dynamic);
|
||||
}
|
||||
|
||||
public void setDynamic(final Boolean dynamic) {
|
||||
this.dynamic = dynamic;
|
||||
}
|
||||
|
||||
public Optional<String> getAccessControlContext() {
|
||||
return Optional.ofNullable(accessControlContext);
|
||||
}
|
||||
|
||||
public void setAccessControlContext(final String accessControlContext) {
|
||||
this.accessControlContext = accessControlContext;
|
||||
}
|
||||
|
||||
public void setApprovalRemark(final String approvalRemark) {
|
||||
this.approvalRemark = approvalRemark;
|
||||
}
|
||||
|
||||
public void setForcedTime(final long forcedTime) {
|
||||
this.forcedTime = forcedTime;
|
||||
}
|
||||
|
||||
public void setDeleted(final boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
private static boolean isSoftDeleted(final DescriptorEvent event) {
|
||||
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
|
||||
final List<DirectToFieldChangeRecord> changes = changeSet.getChanges().stream()
|
||||
.filter(DirectToFieldChangeRecord.class::isInstance).map(DirectToFieldChangeRecord.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return changes.stream().anyMatch(changeRecord -> DELETED_PROPERTY.equals(changeRecord.getAttribute())
|
||||
&& Boolean.parseBoolean(changeRecord.getNewValue().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
|
||||
/**
|
||||
* JPA entity definition of persisting a group of an rollout.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_rolloutgroup", uniqueConstraints = @UniqueConstraint(columnNames = { "name", "rollout",
|
||||
@@ -121,7 +120,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
|
||||
|
||||
@Column(name = "target_percentage")
|
||||
private float targetPercentage = 100;
|
||||
|
||||
|
||||
@Column(name = "confirmation_required")
|
||||
private boolean confirmationRequired;
|
||||
|
||||
@@ -146,14 +145,6 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public List<RolloutTargetGroup> getRolloutTargetGroup() {
|
||||
if (rolloutTargetGroup == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(rolloutTargetGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutGroup getParent() {
|
||||
return parent;
|
||||
@@ -168,10 +159,6 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
|
||||
this.dynamic = dynamic;
|
||||
}
|
||||
|
||||
public void setParent(final RolloutGroup parent) {
|
||||
this.parent = (JpaRolloutGroup) parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutGroupSuccessCondition getSuccessCondition() {
|
||||
return successCondition;
|
||||
@@ -245,12 +232,15 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
|
||||
this.totalTargets = totalTargets;
|
||||
}
|
||||
|
||||
public void setSuccessAction(final RolloutGroupSuccessAction successAction) {
|
||||
this.successAction = successAction;
|
||||
}
|
||||
|
||||
public void setSuccessActionExp(final String successActionExp) {
|
||||
this.successActionExp = successActionExp;
|
||||
/**
|
||||
* @return the totalTargetCountStatus
|
||||
*/
|
||||
@Override
|
||||
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
||||
if (totalTargetCountStatus == null) {
|
||||
totalTargetCountStatus = new TotalTargetCountStatus(Long.valueOf(totalTargets), rollout.getActionType());
|
||||
}
|
||||
return totalTargetCountStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -267,38 +257,46 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
|
||||
return targetPercentage;
|
||||
}
|
||||
|
||||
public void setConfirmationRequired(final boolean confirmationRequired) {
|
||||
this.confirmationRequired = confirmationRequired;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfirmationRequired() {
|
||||
return confirmationRequired;
|
||||
}
|
||||
|
||||
public void setConfirmationRequired(final boolean confirmationRequired) {
|
||||
this.confirmationRequired = confirmationRequired;
|
||||
}
|
||||
|
||||
public void setTargetPercentage(final float targetPercentage) {
|
||||
this.targetPercentage = targetPercentage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the totalTargetCountStatus
|
||||
*/
|
||||
@Override
|
||||
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
||||
if (totalTargetCountStatus == null) {
|
||||
totalTargetCountStatus = new TotalTargetCountStatus(Long.valueOf(totalTargets), rollout.getActionType());
|
||||
}
|
||||
return totalTargetCountStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param totalTargetCountStatus
|
||||
* the totalTargetCountStatus to set
|
||||
* @param totalTargetCountStatus the totalTargetCountStatus to set
|
||||
*/
|
||||
public void setTotalTargetCountStatus(final TotalTargetCountStatus totalTargetCountStatus) {
|
||||
this.totalTargetCountStatus = totalTargetCountStatus;
|
||||
}
|
||||
|
||||
public void setSuccessActionExp(final String successActionExp) {
|
||||
this.successActionExp = successActionExp;
|
||||
}
|
||||
|
||||
public void setSuccessAction(final RolloutGroupSuccessAction successAction) {
|
||||
this.successAction = successAction;
|
||||
}
|
||||
|
||||
public void setParent(final RolloutGroup parent) {
|
||||
this.parent = (JpaRolloutGroup) parent;
|
||||
}
|
||||
|
||||
public List<RolloutTargetGroup> getRolloutTargetGroup() {
|
||||
if (rolloutTargetGroup == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(rolloutTargetGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RolloutGroup [rollout=" + (rollout != null ? rollout.getId() : "") + ", status=" + status
|
||||
|
||||
@@ -62,9 +62,9 @@ import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
|
||||
@Entity
|
||||
@Table(name = "sp_base_software_module", uniqueConstraints = @UniqueConstraint(columnNames = { "module_type", "name",
|
||||
"version", "tenant" }, name = "uk_base_sw_mod"), indexes = {
|
||||
@Index(name = "sp_idx_base_sw_module_01", columnList = "tenant,deleted,name,version"),
|
||||
@Index(name = "sp_idx_base_sw_module_02", columnList = "tenant,deleted,module_type"),
|
||||
@Index(name = "sp_idx_base_sw_module_prim", columnList = "tenant,id") })
|
||||
@Index(name = "sp_idx_base_sw_module_01", columnList = "tenant,deleted,name,version"),
|
||||
@Index(name = "sp_idx_base_sw_module_02", columnList = "tenant,deleted,module_type"),
|
||||
@Index(name = "sp_idx_base_sw_module_prim", columnList = "tenant,id") })
|
||||
@NamedEntityGraph(name = "SoftwareModule.artifacts", attributeNodes = { @NamedAttributeNode("artifacts") })
|
||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
|
||||
// sub entities
|
||||
@@ -83,7 +83,8 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
||||
private JpaSoftwareModuleType type;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "softwareModule", cascade = { CascadeType.PERSIST }, targetEntity = JpaArtifact.class, orphanRemoval = true)
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "softwareModule", cascade = {
|
||||
CascadeType.PERSIST }, targetEntity = JpaArtifact.class, orphanRemoval = true)
|
||||
private List<JpaArtifact> artifacts;
|
||||
|
||||
@Setter
|
||||
@@ -191,7 +192,8 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
||||
}
|
||||
sb.delete(sb.length() - 2, sb.length());
|
||||
throw new LockedException(JpaSoftwareModule.class, getId(), "DELETE", sb.toString());
|
||||
};
|
||||
}
|
||||
;
|
||||
}
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
@@ -25,12 +25,12 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
|
||||
/**
|
||||
* Metadata for {@link SoftwareModule}.
|
||||
*
|
||||
*/
|
||||
@IdClass(SwMetadataCompositeKey.class)
|
||||
@Entity
|
||||
@Table(name = "sp_sw_metadata")
|
||||
public class JpaSoftwareModuleMetadata extends AbstractJpaMetaData implements SoftwareModuleMetadata {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Index;
|
||||
@@ -23,18 +25,15 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* Type of a software modules.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_software_module_type", indexes = {
|
||||
@Index(name = "sp_idx_software_module_type_01", columnList = "tenant,deleted"),
|
||||
@Index(name = "sp_idx_software_module_type_prim", columnList = "tenant,id") }, uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = { "type_key", "tenant" }, name = "uk_smt_type_key"),
|
||||
@UniqueConstraint(columnNames = { "name", "tenant" }, name = "uk_smt_name") })
|
||||
@UniqueConstraint(columnNames = { "type_key", "tenant" }, name = "uk_smt_type_key"),
|
||||
@UniqueConstraint(columnNames = { "name", "tenant" }, name = "uk_smt_name") })
|
||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
|
||||
// sub entities
|
||||
@SuppressWarnings("squid:S2160")
|
||||
@@ -53,14 +52,10 @@ public class JpaSoftwareModuleType extends AbstractJpaTypeEntity implements Soft
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param key
|
||||
* of the type
|
||||
* @param name
|
||||
* of the type
|
||||
* @param description
|
||||
* of the type
|
||||
* @param maxAssignments
|
||||
* assignments to a DS
|
||||
* @param key of the type
|
||||
* @param name of the type
|
||||
* @param description of the type
|
||||
* @param maxAssignments assignments to a DS
|
||||
*/
|
||||
public JpaSoftwareModuleType(final String key, final String name, final String description,
|
||||
final int maxAssignments) {
|
||||
@@ -70,16 +65,11 @@ public class JpaSoftwareModuleType extends AbstractJpaTypeEntity implements Soft
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param key
|
||||
* of the type
|
||||
* @param name
|
||||
* of the type
|
||||
* @param description
|
||||
* of the type
|
||||
* @param maxAssignments
|
||||
* assignments to a DS
|
||||
* @param colour
|
||||
* of the type. It will be null by default
|
||||
* @param key of the type
|
||||
* @param name of the type
|
||||
* @param description of the type
|
||||
* @param maxAssignments assignments to a DS
|
||||
* @param colour of the type. It will be null by default
|
||||
*/
|
||||
public JpaSoftwareModuleType(final String key, final String name, final String description,
|
||||
final int maxAssignments, final String colour) {
|
||||
@@ -94,15 +84,15 @@ public class JpaSoftwareModuleType extends AbstractJpaTypeEntity implements Soft
|
||||
// Default Constructor for JPA.
|
||||
}
|
||||
|
||||
public void setMaxAssignments(final int maxAssignments) {
|
||||
this.maxAssignments = maxAssignments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxAssignments() {
|
||||
return maxAssignments;
|
||||
}
|
||||
|
||||
public void setMaxAssignments(final int maxAssignments) {
|
||||
this.maxAssignments = maxAssignments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Statistic;
|
||||
|
||||
public interface JpaStatistic extends Statistic {
|
||||
|
||||
@@ -18,13 +18,13 @@ import org.eclipse.hawkbit.repository.model.Tag;
|
||||
/**
|
||||
* A Tag can be used as describing and organizational meta information for any
|
||||
* kind of entity.
|
||||
*
|
||||
*/
|
||||
@MappedSuperclass
|
||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
|
||||
// sub entities
|
||||
@SuppressWarnings("squid:S2160")
|
||||
public class JpaTag extends AbstractJpaNamedEntity implements Tag {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "colour", nullable = true, length = Tag.COLOUR_MAX_SIZE)
|
||||
@@ -38,12 +38,9 @@ public class JpaTag extends AbstractJpaNamedEntity implements Tag {
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the {@link Tag}
|
||||
* @param description
|
||||
* of the {@link Tag}
|
||||
* @param colour
|
||||
* of tag in UI
|
||||
* @param name of the {@link Tag}
|
||||
* @param description of the {@link Tag}
|
||||
* @param colour of tag in UI
|
||||
*/
|
||||
public JpaTag(final String name, final String description, final String colour) {
|
||||
super(name, description);
|
||||
|
||||
@@ -77,7 +77,7 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
@Index(name = "sp_idx_target_04", columnList = "tenant,created_at"),
|
||||
@Index(name = "sp_idx_target_05", columnList = "tenant,last_modified_at"),
|
||||
@Index(name = "sp_idx_target_prim", columnList = "tenant,id") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||
"controller_id", "tenant" }, name = "uk_tenant_controller_id"))
|
||||
"controller_id", "tenant" }, name = "uk_tenant_controller_id"))
|
||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
|
||||
// sub entities
|
||||
@SuppressWarnings("squid:S2160")
|
||||
@@ -218,91 +218,25 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
return assignedDistributionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param assignedDistributionSet Distribution set
|
||||
*/
|
||||
public void setAssignedDistributionSet(final DistributionSet assignedDistributionSet) {
|
||||
this.assignedDistributionSet = (JpaDistributionSet) assignedDistributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControllerId() {
|
||||
return controllerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return tags
|
||||
*/
|
||||
public Set<TargetTag> getTags() {
|
||||
if (tags == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableSet(tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return rollouts target group
|
||||
*/
|
||||
public List<RolloutTargetGroup> getRolloutTargetGroup() {
|
||||
if (rolloutTargetGroup == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(rolloutTargetGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tag
|
||||
* to be added
|
||||
*/
|
||||
public void addTag(final TargetTag tag) {
|
||||
if (tags == null) {
|
||||
tags = new HashSet<>();
|
||||
}
|
||||
tags.add(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tag
|
||||
* the tag to be removed from the target
|
||||
*/
|
||||
public void removeTag(final TargetTag tag) {
|
||||
if (tags != null) {
|
||||
tags.remove(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param assignedDistributionSet
|
||||
* Distribution set
|
||||
*/
|
||||
public void setAssignedDistributionSet(final DistributionSet assignedDistributionSet) {
|
||||
this.assignedDistributionSet = (JpaDistributionSet) assignedDistributionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param controllerId
|
||||
* Controller ID
|
||||
* @param controllerId Controller ID
|
||||
*/
|
||||
public void setControllerId(final String controllerId) {
|
||||
this.controllerId = controllerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of action
|
||||
*/
|
||||
public List<Action> getActions() {
|
||||
if (actions == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param action
|
||||
* Action
|
||||
*/
|
||||
public void addAction(final Action action) {
|
||||
if (actions == null) {
|
||||
actions = new ArrayList<>();
|
||||
}
|
||||
actions.add((JpaAction) action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the securityToken if the current security context contains the
|
||||
* necessary permission {@link SpPermission#READ_TARGET_SEC_TOKEN}
|
||||
@@ -321,8 +255,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
}
|
||||
|
||||
/**
|
||||
* @param securityToken
|
||||
* token value
|
||||
* @param securityToken token value
|
||||
*/
|
||||
public void setSecurityToken(final String securityToken) {
|
||||
this.securityToken = securityToken;
|
||||
@@ -344,6 +277,26 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLastTargetQuery() {
|
||||
return lastTargetQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getInstallationDate() {
|
||||
return installationDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetUpdateStatus getUpdateStatus() {
|
||||
return updateStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetType getTargetType() {
|
||||
return targetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the poll time which holds the last poll time of the target, the
|
||||
* next poll time and the overdue time. In case the
|
||||
@@ -366,37 +319,117 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
}
|
||||
|
||||
public void setAutoConfirmationStatus(final JpaAutoConfirmationStatus autoConfirmationStatus) {
|
||||
this.autoConfirmationStatus = autoConfirmationStatus;
|
||||
this.autoConfirmationStatus = autoConfirmationStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLastTargetQuery() {
|
||||
return lastTargetQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getInstallationDate() {
|
||||
return installationDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetUpdateStatus getUpdateStatus() {
|
||||
return updateStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetType getTargetType() {
|
||||
return targetType;
|
||||
public boolean isRequestControllerAttributes() {
|
||||
return requestControllerAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type
|
||||
* Target type
|
||||
* @param requestControllerAttributes Attributes
|
||||
*/
|
||||
public void setRequestControllerAttributes(final boolean requestControllerAttributes) {
|
||||
this.requestControllerAttributes = requestControllerAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type Target type
|
||||
*/
|
||||
public void setTargetType(final TargetType type) {
|
||||
this.targetType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param updateStatus Status
|
||||
*/
|
||||
public void setUpdateStatus(final TargetUpdateStatus updateStatus) {
|
||||
this.updateStatus = updateStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param installationDate installation date
|
||||
*/
|
||||
public void setInstallationDate(final Long installationDate) {
|
||||
this.installationDate = installationDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lastTargetQuery last query ID
|
||||
*/
|
||||
public void setLastTargetQuery(final Long lastTargetQuery) {
|
||||
this.lastTargetQuery = lastTargetQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param address Address
|
||||
*/
|
||||
public void setAddress(final String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return tags
|
||||
*/
|
||||
public Set<TargetTag> getTags() {
|
||||
if (tags == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableSet(tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return rollouts target group
|
||||
*/
|
||||
public List<RolloutTargetGroup> getRolloutTargetGroup() {
|
||||
if (rolloutTargetGroup == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(rolloutTargetGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tag to be added
|
||||
*/
|
||||
public void addTag(final TargetTag tag) {
|
||||
if (tags == null) {
|
||||
tags = new HashSet<>();
|
||||
}
|
||||
tags.add(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tag the tag to be removed from the target
|
||||
*/
|
||||
public void removeTag(final TargetTag tag) {
|
||||
if (tags != null) {
|
||||
tags.remove(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of action
|
||||
*/
|
||||
public List<Action> getActions() {
|
||||
if (actions == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param action Action
|
||||
*/
|
||||
public void addAction(final Action action) {
|
||||
if (actions == null) {
|
||||
actions = new ArrayList<>();
|
||||
}
|
||||
actions.add((JpaAction) action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Distribution set
|
||||
*/
|
||||
@@ -404,6 +437,13 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
return installedDistributionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param installedDistributionSet Distribution set
|
||||
*/
|
||||
public void setInstalledDistributionSet(final JpaDistributionSet installedDistributionSet) {
|
||||
this.installedDistributionSet = installedDistributionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return controller attributes
|
||||
*/
|
||||
@@ -411,11 +451,6 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
return controllerAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequestControllerAttributes() {
|
||||
return requestControllerAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return target metadata
|
||||
*/
|
||||
@@ -433,65 +468,12 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
+ "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param address
|
||||
* Address
|
||||
*/
|
||||
public void setAddress(final String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lastTargetQuery
|
||||
* last query ID
|
||||
*/
|
||||
public void setLastTargetQuery(final Long lastTargetQuery) {
|
||||
this.lastTargetQuery = lastTargetQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param installationDate
|
||||
* installation date
|
||||
*/
|
||||
public void setInstallationDate(final Long installationDate) {
|
||||
this.installationDate = installationDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param installedDistributionSet
|
||||
* Distribution set
|
||||
*/
|
||||
public void setInstalledDistributionSet(final JpaDistributionSet installedDistributionSet) {
|
||||
this.installedDistributionSet = installedDistributionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param updateStatus
|
||||
* Status
|
||||
*/
|
||||
public void setUpdateStatus(final TargetUpdateStatus updateStatus) {
|
||||
this.updateStatus = updateStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requestControllerAttributes
|
||||
* Attributes
|
||||
*/
|
||||
public void setRequestControllerAttributes(final boolean requestControllerAttributes) {
|
||||
this.requestControllerAttributes = requestControllerAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new TargetCreatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUpdateIgnoreFields() {
|
||||
return TARGET_UPDATE_EVENT_IGNORE_FIELDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
@@ -504,4 +486,9 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
.publishEvent(new TargetDeletedEvent(getTenant(), getId(), getControllerId(), address,
|
||||
getClass(), EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUpdateIgnoreFields() {
|
||||
return TARGET_UPDATE_EVENT_IGNORE_FIELDS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
|
||||
/**
|
||||
* Stored target filter.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_target_filter_query", uniqueConstraints = @UniqueConstraint(columnNames = { "name",
|
||||
@@ -49,6 +48,7 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
@SuppressWarnings("squid:S2160")
|
||||
public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
|
||||
implements TargetFilterQuery, EventAwareEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "name", length = NamedEntity.NAME_MAX_SIZE, nullable = false)
|
||||
@@ -93,19 +93,13 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
|
||||
|
||||
/**
|
||||
* Construct a Target filter query with auto assign distribution set
|
||||
*
|
||||
* @param name
|
||||
* of the {@link TargetFilterQuery}.
|
||||
* @param query
|
||||
* of the {@link TargetFilterQuery}.
|
||||
* @param autoAssignDistributionSet
|
||||
* of the {@link TargetFilterQuery}.
|
||||
* @param autoAssignActionType
|
||||
* of the {@link TargetFilterQuery}.
|
||||
* @param autoAssignWeight
|
||||
* of the {@link TargetFilterQuery}.
|
||||
* @param confirmationRequired
|
||||
* of the {@link TargetFilterQuery}.
|
||||
*
|
||||
* @param name of the {@link TargetFilterQuery}.
|
||||
* @param query of the {@link TargetFilterQuery}.
|
||||
* @param autoAssignDistributionSet of the {@link TargetFilterQuery}.
|
||||
* @param autoAssignActionType of the {@link TargetFilterQuery}.
|
||||
* @param autoAssignWeight of the {@link TargetFilterQuery}.
|
||||
* @param confirmationRequired of the {@link TargetFilterQuery}.
|
||||
*/
|
||||
public JpaTargetFilterQuery(final String name, final String query, final DistributionSet autoAssignDistributionSet,
|
||||
final ActionType autoAssignActionType, final Integer autoAssignWeight, final boolean confirmationRequired) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user