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();
- }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/EntityInterceptor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/EntityInterceptor.java
index 2a8b4c8f7..9ac7bfe18 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/EntityInterceptor.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/EntityInterceptor.java
@@ -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) {
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkBitEclipseLinkJpaDialect.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkBitEclipseLinkJpaDialect.java
index 885b778db..087dcca53 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkBitEclipseLinkJpaDialect.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkBitEclipseLinkJpaDialect.java
@@ -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:
- *
+ *
*
* 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;
*
* 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();
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java
index 7187d3c88..04f6bcf04 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java
@@ -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;
}
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaManagementHelper.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaManagementHelper.java
index ee6accc9f..880e69a62 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaManagementHelper.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaManagementHelper.java
@@ -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() {
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java
index 48ff6d134..bd06edacb 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java
@@ -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 DOWNLOAD_ONLY_ACTION_TERMINATION_STATUSES = Arrays.asList(Status.ERROR,
Status.FINISHED, Status.CANCELED, Status.DOWNLOADED);
-
+ private static final Comparator 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 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 DESC_COMP = Comparator.comparingLong(RolloutGroup::getId).reversed();
private void executeLatestRolloutGroup(final JpaRollout rollout) {
// was - rolloutGroupRepository.findByRolloutAndStatusNotOrderByIdDesc(rollout, RolloutGroupStatus.SCHEDULED);
final List 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 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 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);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java
index 05b2b93ef..0f4277ce4 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java
@@ -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.",
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
index 89d0ba309..be9248de2 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
@@ -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) {
+ 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 getVendorProperties() {
+ final Map 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 artifactAccessController,
+ @Autowired(required = false) final AccessController softwareModuleTypeAccessController,
+ @Autowired(required = false) final AccessController softwareModuleAccessController,
+ @Autowired(required = false) final AccessController distributionSetTypeAccessController,
+ @Autowired(required = false) final AccessController distributionSetAccessController,
+ @Autowired(required = false) final AccessController targetTypeAccessControlManager,
+ @Autowired(required = false) final AccessController targetAccessControlManager,
+ @Autowired(required = false) final AccessController 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 getVendorProperties() {
- final Map 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) {
- 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 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 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 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 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 artifactAccessController,
- @Autowired(required = false) final AccessController softwareModuleTypeAccessController,
- @Autowired(required = false) final AccessController softwareModuleAccessController,
- @Autowired(required = false) final AccessController distributionSetTypeAccessController,
- @Autowired(required = false) final AccessController distributionSetAccessController,
- @Autowired(required = false) final AccessController targetTypeAccessControlManager,
- @Autowired(required = false) final AccessController targetAccessControlManager,
- @Autowired(required = false) final AccessController 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);
- }
- };
- }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/SystemManagementCacheKeyGenerator.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/SystemManagementCacheKeyGenerator.java
index ffe36abff..1060e3049 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/SystemManagementCacheKeyGenerator.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/SystemManagementCacheKeyGenerator.java
@@ -26,26 +26,10 @@ import org.springframework.context.annotation.Bean;
*/
public class SystemManagementCacheKeyGenerator implements CurrentTenantCacheKeyGenerator {
+ private final ThreadLocal createInitialTenant = new ThreadLocal<>();
@Autowired
private TenantAware tenantAware;
- private final ThreadLocal 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);
+ }
+ }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantKeyGenerator.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantKeyGenerator.java
index 813de05c0..29b0655ae 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantKeyGenerator.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantKeyGenerator.java
@@ -18,7 +18,6 @@ import org.springframework.stereotype.Service;
/**
* {@link KeyGenerator} for tenant related caches.
- *
*/
@Service
public class TenantKeyGenerator implements KeyGenerator {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessController.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessController.java
index 4cbdf3a5a..1cc10c022 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessController.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessController.java
@@ -24,7 +24,7 @@ import org.springframework.lang.Nullable;
* additional restrictions (e.g. entity based) could be applied.
*
* Note: Experimental, only
- *
+ *
* @param the domain type the repository manages
*/
public interface AccessController {
@@ -33,16 +33,15 @@ public interface AccessController {
* 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> 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 {
/**
* 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);
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java
index e0c57a634..9788376ee 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java
@@ -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;
- }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AbstractAutoAssignExecutor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AbstractAutoAssignExecutor.java
index 3e23fb0a5..44ab49f0c 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AbstractAutoAssignExecutor.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AbstractAutoAssignExecutor.java
@@ -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 mapToDeploymentRequests(final List 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();
- }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java
index 37bb100a2..2bbe35ab6 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java
@@ -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",
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java
index d02228050..0d2b6b09e 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java
@@ -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,
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoActionCleanup.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoActionCleanup.java
index a2894ad8c..e186d631f 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoActionCleanup.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoActionCleanup.java
@@ -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) {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupScheduler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupScheduler.java
index 0b9c6168c..e283c961a 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupScheduler.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupScheduler.java
@@ -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,
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaActionStatusBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaActionStatusBuilder.java
index 5946eb8b8..8a2c791c4 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaActionStatusBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaActionStatusBuilder.java
@@ -15,7 +15,6 @@ import org.eclipse.hawkbit.repository.model.ActionStatus;
/**
* Builder implementation for {@link ActionStatus}.
- *
*/
public class JpaActionStatusBuilder implements ActionStatusBuilder {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaActionStatusCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaActionStatusCreate.java
index a4151e936..0b5b930cc 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaActionStatusCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaActionStatusCreate.java
@@ -15,7 +15,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
/**
* Create/build implementation.
- *
*/
public class JpaActionStatusCreate extends AbstractActionStatusCreate
implements ActionStatusCreate {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetBuilder.java
index bd06c8dc1..6169e93af 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetBuilder.java
@@ -19,7 +19,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
/**
* Builder implementation for {@link DistributionSet}.
- *
*/
public class JpaDistributionSetBuilder implements DistributionSetBuilder {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetCreate.java
index 708563d73..35afc2669 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetCreate.java
@@ -27,16 +27,14 @@ import org.springframework.util.StringUtils;
/**
* Create/build implementation.
- *
*/
public class JpaDistributionSetCreate extends AbstractDistributionSetUpdateCreate
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;
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeBuilder.java
index 134b1bba2..f992808db 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeBuilder.java
@@ -18,7 +18,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
/**
* Builder implementation for {@link DistributionSetType}.
- *
*/
public class JpaDistributionSetTypeBuilder implements DistributionSetTypeBuilder {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeCreate.java
index 110cc5456..27fbf422c 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeCreate.java
@@ -22,7 +22,6 @@ import org.springframework.util.CollectionUtils;
/**
* Create/build implementation.
- *
*/
public class JpaDistributionSetTypeCreate extends AbstractDistributionSetTypeUpdateCreate
implements DistributionSetTypeCreate {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutBuilder.java
index 11dbcb278..11811de71 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutBuilder.java
@@ -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) {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutCreate.java
index f686a95ee..649dc4e70 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutCreate.java
@@ -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 implements RolloutCreate {
- private final DistributionSetManagement distributionSetManagement;
-
protected Long distributionSetId;
@ValidString
protected String targetFilterQuery;
@@ -35,12 +34,13 @@ public class JpaRolloutCreate extends AbstractNamedEntityBuilder
@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
this.targetFilterQuery = StringUtils.trimWhitespace(targetFilterQuery);
return this;
}
+
/**
* {@link Action.ActionType} used for {@link Action}s
*
@@ -95,6 +96,11 @@ public class JpaRolloutCreate extends AbstractNamedEntityBuilder
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
return this;
}
- public Optional getDistributionSetId() {
- return Optional.ofNullable(distributionSetId);
- }
-
- public Optional getActionType() {
- return Optional.ofNullable(actionType);
- }
-
- public Optional getForcedTime() {
- return Optional.ofNullable(forcedTime);
- }
-
- public Optional getWeight() {
- return Optional.ofNullable(weight);
- }
-
- public Optional 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
return rollout;
}
+
+ public Optional getDistributionSetId() {
+ return Optional.ofNullable(distributionSetId);
+ }
+
+ public Optional getActionType() {
+ return Optional.ofNullable(actionType);
+ }
+
+ public Optional getForcedTime() {
+ return Optional.ofNullable(forcedTime);
+ }
+
+ public Optional getWeight() {
+ return Optional.ofNullable(weight);
+ }
+
+ public Optional getStartAt() {
+ return Optional.ofNullable(startAt);
+ }
}
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutGroupBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutGroupBuilder.java
index 4afa5128a..ea937e2ee 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutGroupBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutGroupBuilder.java
@@ -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();
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutGroupCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutGroupCreate.java
index 21a33b328..5cb56821a 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutGroupCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutGroupCreate.java
@@ -18,6 +18,51 @@ import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
public class JpaRolloutGroupCreate extends AbstractRolloutGroupCreate
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
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");
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataBuilder.java
index 004f777e7..0ee173f7b 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataBuilder.java
@@ -18,7 +18,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
/**
* Builder implementation for {@link SoftwareModuleMetadata}.
- *
*/
public class JpaSoftwareModuleMetadataBuilder implements SoftwareModuleMetadataBuilder {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataCreate.java
index 31536c5c5..8b83dccb9 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataCreate.java
@@ -18,7 +18,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
/**
* Create/build implementation.
- *
*/
public class JpaSoftwareModuleMetadataCreate
extends AbstractSoftwareModuleMetadataUpdateCreate
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleTypeBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleTypeBuilder.java
index 3df3eb843..4ad36bae1 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleTypeBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleTypeBuilder.java
@@ -17,7 +17,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
/**
* Builder implementation for {@link SoftwareModuleType}.
- *
*/
public class JpaSoftwareModuleTypeBuilder implements SoftwareModuleTypeBuilder {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleTypeCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleTypeCreate.java
index 7045f338e..9f618205b 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleTypeCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleTypeCreate.java
@@ -15,7 +15,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
/**
* Create/build implementation.
- *
*/
public class JpaSoftwareModuleTypeCreate extends AbstractSoftwareModuleTypeUpdateCreate
implements SoftwareModuleTypeCreate {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTagBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTagBuilder.java
index 336767449..b6b3c1c08 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTagBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTagBuilder.java
@@ -17,7 +17,6 @@ import org.eclipse.hawkbit.repository.model.Tag;
/**
* Builder implementation for {@link Tag}.
- *
*/
public class JpaTagBuilder implements TagBuilder {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTagCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTagCreate.java
index 0739fb5aa..049e668de 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTagCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTagCreate.java
@@ -18,9 +18,9 @@ import org.eclipse.hawkbit.repository.model.Tag;
/**
* Create/build implementation.
- *
*/
public class JpaTagCreate extends AbstractTagUpdateCreate implements TagCreate {
+
JpaTagCreate() {
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetBuilder.java
index 4245453e9..20f6b1339 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetBuilder.java
@@ -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;
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetCreate.java
index 4594e8e62..03ea57b87 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetCreate.java
@@ -20,7 +20,6 @@ import org.springframework.util.StringUtils;
/**
* Create/build implementation.
- *
*/
public class JpaTargetCreate extends AbstractTargetUpdateCreate implements TargetCreate {
@@ -29,8 +28,7 @@ public class JpaTargetCreate extends AbstractTargetUpdateCreate 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 im
target.setName(name);
}
- if (targetTypeId != null){
+ if (targetTypeId != null) {
TargetType targetType = targetTypeManagement.get(targetTypeId)
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, targetTypeId));
target.setTargetType(targetType);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryBuilder.java
index fc4150755..0981f6c8e 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryBuilder.java
@@ -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);
+ }
+
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryCreate.java
index fa35bb83b..a7f6c806a 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryCreate.java
@@ -19,7 +19,6 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
/**
* Create/build implementation.
- *
*/
public class JpaTargetFilterQueryCreate extends AbstractTargetFilterQueryUpdateCreate
implements TargetFilterQueryCreate {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetTypeBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetTypeBuilder.java
index 662ed84c7..46a57bace 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetTypeBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetTypeBuilder.java
@@ -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;
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetTypeCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetTypeCreate.java
index 43ce272b7..fc7a7c935 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetTypeCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetTypeCreate.java
@@ -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
implements TargetTypeCreate {
@@ -34,8 +33,7 @@ public class JpaTargetTypeCreate extends AbstractTargetTypeUpdateCreate> 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();
- }
-
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/executor/AfterTransactionCommitExecutor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/executor/AfterTransactionCommitExecutor.java
index 024b55703..a63a6ba34 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/executor/AfterTransactionCommitExecutor.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/executor/AfterTransactionCommitExecutor.java
@@ -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);
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractDsAssignmentStrategy.java
index 04c0a3be4..b2ea16f65 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractDsAssignmentStrategy.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractDsAssignmentStrategy.java
@@ -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 findTargetsForAssignment(final List controllerIDs, final long distributionSetId);
+ public JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType,
+ final List targets, final JpaDistributionSet set) {
+ final Optional optTarget = targets.stream()
+ .filter(t -> t.getControllerId().equals(targetWithActionType.getControllerId())).findFirst();
- /**
- *
- * @param set
- * @param targets
- */
- abstract void sendTargetUpdatedEvents(final DistributionSet set, final List 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> 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 false})
- * 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 cancelActiveActions(List> 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 false})
- * 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> targetIds);
-
- abstract void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult);
-
- abstract void sendDeploymentEvents(final List 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 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 targets, final JpaDistributionSet set) {
- final Optional 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 findTargetsForAssignment(final List 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 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> targetIds, final String currentUser);
+
+ /**
+ * Cancels actions that can be canceled (i.e.
+ * {@link DistributionSet#isRequiredMigrationStep() is false})
+ * 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 cancelActiveActions(List> targetIds);
+
+ /**
+ * Cancels actions that can be canceled (i.e.
+ * {@link DistributionSet#isRequiredMigrationStep() is false})
+ * 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> targetIds);
+
+ abstract void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult);
+
+ abstract void sendDeploymentEvents(final List 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 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();
- }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaActionManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaActionManagement.java
index dda243e02..a8344bd1b 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaActionManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaActionManagement.java
@@ -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 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 findActiveActionsWithHighestWeightConsideringDefault(final String controllerId,
final int maxActionCount) {
final List actions = new ArrayList<>();
@@ -84,23 +132,8 @@ public class JpaActionManagement {
return actions.stream().sorted(actionImportance).limit(maxActionCount).collect(Collectors.toList());
}
- protected List 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();
- }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java
index 977a2dfce..fd88026fa 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java
@@ -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.
- *
- * 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 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.
+ *
+ * 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;
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java
index a1f91f717..3dd974d1c 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java
@@ -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 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 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 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);
- }
- }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java
index d5a51d672..e82a9f292 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java
@@ -9,12 +9,37 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
+import static org.eclipse.hawkbit.repository.model.Action.Status.DOWNLOADED;
+import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED;
+import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_KEY_SIZE;
+import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE;
+
+import java.net.URI;
+import java.time.Duration;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import jakarta.validation.constraints.NotEmpty;
+
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.eclipse.hawkbit.repository.ConfirmationManagement;
@@ -90,30 +115,6 @@ import org.springframework.transaction.support.TransactionCallback;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
-import java.net.URI;
-import java.time.Duration;
-import java.time.ZonedDateTime;
-import java.time.temporal.ChronoUnit;
-import java.time.temporal.TemporalUnit;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.BlockingDeque;
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import static org.eclipse.hawkbit.repository.model.Action.Status.DOWNLOADED;
-import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED;
-import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_KEY_SIZE;
-import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE;
-
/**
* JPA based {@link ControllerManagement} implementation.
*/
@@ -170,8 +171,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
private DistributionSetManagement distributionSetManagement;
public JpaControllerManagement(final ScheduledExecutorService executorService,
- final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
- final QuotaManagement quotaManagement, final RepositoryProperties repositoryProperties) {
+ final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
+ final QuotaManagement quotaManagement, final RepositoryProperties repositoryProperties) {
super(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties);
if (!repositoryProperties.isEagerPollPersistence()) {
@@ -185,6 +186,172 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
}
}
+ @Override
+ public int getWeightConsideringDefault(final Action action) {
+ return super.getWeightConsideringDefault(action);
+ }
+
+ @Override
+ protected void onActionStatusUpdate(final Action.Status updatedActionStatus, final JpaAction action) {
+ switch (updatedActionStatus) {
+ case ERROR:
+ final JpaTarget target = (JpaTarget) action.getTarget();
+ target.setUpdateStatus(TargetUpdateStatus.ERROR);
+ handleErrorOnAction(action, target);
+ break;
+ case FINISHED:
+ handleFinishedAndStoreInTargetStatus(action).ifPresent(this::requestControllerAttributes);
+ break;
+ case DOWNLOADED:
+ handleDownloadedActionStatus(action).ifPresent(this::requestControllerAttributes);
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ @Transactional(isolation = Isolation.READ_COMMITTED)
+ @Retryable(include = {
+ ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ public Action addCancelActionStatus(final ActionStatusCreate c) {
+ final JpaActionStatusCreate create = (JpaActionStatusCreate) c;
+
+ final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
+
+ if (!action.isCancelingOrCanceled()) {
+ throw new CancelActionNotAllowedException("The action is not in canceling state.");
+ }
+
+ final JpaActionStatus actionStatus = create.build();
+
+ switch (actionStatus.getStatus()) {
+ case CANCELED:
+ case FINISHED:
+ handleFinishedCancelation(actionStatus, action);
+ break;
+ case ERROR:
+ case CANCEL_REJECTED:
+ // Cancellation rejected. Back to running.
+ action.setStatus(Status.RUNNING);
+ break;
+ default:
+ // information status entry - check for a potential DOS attack
+ assertActionStatusQuota(actionStatus, action);
+ assertActionStatusMessageQuota(actionStatus);
+ break;
+ }
+
+ actionStatus.setAction(actionRepository.save(action));
+ actionStatusRepository.save(actionStatus);
+
+ return action;
+ }
+
+ @Override
+ public Optional getSoftwareModule(final long id) {
+ return softwareModuleRepository.findById(id).map(s -> (SoftwareModule) s);
+ }
+
+ @Override
+ public Map> findTargetVisibleMetaDataBySoftwareModuleId(
+ final Collection moduleId) {
+
+ return softwareModuleMetadataRepository
+ .findBySoftwareModuleIdInAndTargetVisible(PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT),
+ moduleId, true)
+ .getContent().stream().collect(Collectors.groupingBy(o -> (Long) o[0],
+ Collectors.mapping(o -> (SoftwareModuleMetadata) o[1], Collectors.toList())));
+ }
+
+ @Override
+ @Transactional
+ @Retryable(include = {
+ ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ public ActionStatus addInformationalActionStatus(final ActionStatusCreate c) {
+ final JpaActionStatusCreate create = (JpaActionStatusCreate) c;
+ final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
+ final JpaActionStatus statusMessage = create.build();
+ statusMessage.setAction(action);
+
+ assertActionStatusQuota(statusMessage, action);
+ assertActionStatusMessageQuota(statusMessage);
+
+ return actionStatusRepository.save(statusMessage);
+ }
+
+ @Override
+ @Transactional(isolation = Isolation.READ_COMMITTED)
+ @Retryable(include = {
+ ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ public Action addUpdateActionStatus(final ActionStatusCreate statusCreate) {
+ return addActionStatus((JpaActionStatusCreate) statusCreate);
+ }
+
+ @Override
+ public Optional findActiveActionWithHighestWeight(final String controllerId) {
+ return findActiveActionsWithHighestWeight(controllerId, 1).stream().findFirst();
+ }
+
+ @Override
+ public List findActiveActionsWithHighestWeight(final String controllerId, final int maxActionCount) {
+ return findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount);
+ }
+
+ @Override
+ public Optional findActionWithDetails(final long actionId) {
+ return actionRepository.findWithDetailsById(actionId);
+ }
+
+ @Override
+ public Page findActionStatusByAction(final Pageable pageReq, final long actionId) {
+ if (!actionRepository.existsById(actionId)) {
+ throw new EntityNotFoundException(Action.class, actionId);
+ }
+
+ return actionStatusRepository.findByActionId(pageReq, actionId);
+ }
+
+ @Override
+ @Transactional(isolation = Isolation.READ_COMMITTED)
+ @Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address) {
+ return findOrRegisterTargetIfItDoesNotExist(controllerId, address, null, null);
+ }
+
+ @Override
+ @Transactional(isolation = Isolation.READ_COMMITTED)
+ @Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address,
+ final String name, final String type) {
+ final Specification spec =
+ (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
+
+ return targetRepository.findOne(spec).map(target -> updateTarget(target, address, name, type))
+ .orElseGet(() -> createTarget(controllerId, address, name, type));
+ }
+
+ @Override
+ public Optional getActionForDownloadByTargetAndSoftwareModule(final String controllerId,
+ final long moduleId) {
+ throwExceptionIfTargetDoesNotExist(controllerId);
+ throwExceptionIfSoftwareModuleDoesNotExist(moduleId);
+
+ // it used to perform 3-table join query
+ // @Query("Select a from JpaAction a join a.distributionSet ds join ds.modules modul where a.target.controllerId = :target and modul.id = :module order by a.id desc")
+ // final List actions = actionRepository.findActionByTargetAndSoftwareModule(controllerId, moduleId);
+ // TODO AC - we could fetch distribution sets in order to skip calls to serarch for modules
+ return actionRepository
+ .findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, true))
+ .stream()
+ .filter(action -> !action.isCancelingOrCanceled())
+ .filter(action -> action.getDistributionSet().getModules()
+ .stream()
+ .anyMatch(module -> module.getId() == moduleId))
+ .map(Action.class::cast)
+ .findFirst();
+ }
+
@Override
public String getPollingTime() {
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
@@ -228,108 +395,265 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
.timeToNextEvent(getMaintenanceWindowPollCount(), action.getMaintenanceWindowStartTime().orElse(null));
}
- /**
- * EventTimer to handle reduction of polling interval based on maintenance
- * window start time. Class models the next polling time as an event to be
- * raised and time to next polling as a timer. The event, in this case the
- * polling, should happen when timer expires. Class makes use of java.time
- * package to manipulate and calculate timer duration.
- */
- private static class EventTimer {
+ @Override
+ public boolean hasTargetArtifactAssigned(final String controllerId, final String sha1Hash) {
+ throwExceptionIfTargetDoesNotExist(controllerId);
+ return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(controllerId, sha1Hash)) > 0;
+ }
- private final String defaultEventInterval;
- private final Duration defaultEventIntervalDuration;
+ @Override
+ public boolean hasTargetArtifactAssigned(final long targetId, final String sha1Hash) {
+ throwExceptionIfTargetDoesNotExist(targetId);
+ return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(targetId, sha1Hash)) > 0;
+ }
- private final String minimumEventInterval;
- private final Duration minimumEventIntervalDuration;
+ @Override
+ @Transactional
+ @Retryable(include = {
+ ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ public Action registerRetrieved(final long actionId, final String message) {
+ return handleRegisterRetrieved(actionId, message);
+ }
- private final TemporalUnit timeUnit;
+ @Override
+ @Transactional
+ @Retryable(include = {
+ ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ public Target updateControllerAttributes(final String controllerId, final Map data,
+ final UpdateMode mode) {
- /**
- * Constructor.
- *
- * @param defaultEventInterval
- * default timer value to use for interval between events.
- * This puts an upper bound for the timer value
- * @param minimumEventInterval
- * for loading {@link DistributionSet#getModules()}. This
- * puts a lower bound to the timer value
- * @param timeUnit
- * representing the unit of time to be used for timer.
+ /*
+ * Constraints on attribute keys & values are not validated by
+ * EclipseLink. Hence, they are validated here.
*/
- EventTimer(final String defaultEventInterval, final String minimumEventInterval, final TemporalUnit timeUnit) {
- this.defaultEventInterval = defaultEventInterval;
- this.defaultEventIntervalDuration = MaintenanceScheduleHelper.convertToISODuration(defaultEventInterval);
-
- this.minimumEventInterval = minimumEventInterval;
- this.minimumEventIntervalDuration = MaintenanceScheduleHelper.convertToISODuration(minimumEventInterval);
-
- this.timeUnit = timeUnit;
+ if (data.entrySet().stream().anyMatch(e -> !isAttributeEntryValid(e))) {
+ throw new InvalidTargetAttributeException();
}
- /**
- * This method calculates the time interval until the next event based
- * on the desired number of events before the time when interval is
- * reset to default. The return value is bounded by
- * {@link EventTimer#defaultEventInterval} and
- * {@link EventTimer#minimumEventInterval}.
- *
- * @param eventCount
- * number of events desired until the interval is reset to
- * default. This is not guaranteed as the interval between
- * events cannot be less than the minimum interval
- * @param timerResetTime
- * time when exponential forwarding should reset to default
- *
- * @return String in HH:mm:ss format for time to next event.
- */
- String timeToNextEvent(final int eventCount, final ZonedDateTime timerResetTime) {
- final ZonedDateTime currentTime = ZonedDateTime.now();
+ final JpaTarget target = targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId))
+ .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
- // If there is no reset time, or if we already past the reset time,
- // return the default interval.
- if (timerResetTime == null || currentTime.compareTo(timerResetTime) > 0) {
- return defaultEventInterval;
- }
+ // get the modifiable attribute map
+ final Map controllerAttributes = target.getControllerAttributes();
+ final UpdateMode updateMode = mode != null ? mode : UpdateMode.MERGE;
+ switch (updateMode) {
+ case REMOVE:
+ // remove the addressed attributes
+ data.keySet().forEach(controllerAttributes::remove);
+ break;
+ case REPLACE:
+ // clear the attributes before adding the new attributes
+ controllerAttributes.clear();
+ copy(data, controllerAttributes);
+ target.setRequestControllerAttributes(false);
+ break;
+ case MERGE:
+ // just merge the attributes in
+ copy(data, controllerAttributes);
+ target.setRequestControllerAttributes(false);
+ break;
+ default:
+ // unknown update mode
+ throw new IllegalStateException("The update mode " + updateMode + " is not supported.");
+ }
+ assertTargetAttributesQuota(target);
- // Calculate the interval timer based on desired event count.
- final Duration currentIntervalDuration = Duration.of(currentTime.until(timerResetTime, timeUnit), timeUnit)
- .dividedBy(eventCount);
+ return targetRepository.save(target);
+ }
- // Need not return interval greater than the default.
- if (currentIntervalDuration.compareTo(defaultEventIntervalDuration) > 0) {
- return defaultEventInterval;
- }
+ @Override
+ public Optional getByControllerId(final String controllerId) {
+ return targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId)).map(Target.class::cast);
+ }
- // Should not return interval less than minimum.
- if (currentIntervalDuration.compareTo(minimumEventIntervalDuration) < 0) {
- return minimumEventInterval;
- }
+ @Override
+ public Optional get(final long targetId) {
+ return targetRepository.findById(targetId).map(t -> (Target) t);
+ }
- return String.format("%02d:%02d:%02d", currentIntervalDuration.toHours(),
- currentIntervalDuration.toMinutes() % 60, currentIntervalDuration.getSeconds() % 60);
+ @Override
+ public List getActionHistoryMessages(final long actionId, final int messageCount) {
+ // Just return empty list in case messageCount is zero.
+ if (messageCount == 0) {
+ return Collections.emptyList();
+ }
+
+ // For negative and large value of messageCount, limit the number of
+ // messages.
+ final int limit = messageCount < 0 || messageCount >= RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
+ ? RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
+ : messageCount;
+
+ final PageRequest pageable = PageRequest.of(0, limit, Sort.by(Direction.DESC, "occurredAt"));
+ final Page messages = actionStatusRepository.findMessagesByActionIdAndMessageNotLike(pageable, actionId,
+ RepositoryConstants.SERVER_MESSAGE_PREFIX + "%");
+
+ log.debug("Retrieved {} message(s) from action history for action {}.", messages.getNumberOfElements(),
+ actionId);
+
+ return messages.getContent();
+ }
+
+ /**
+ * Cancels given {@link Action} for this {@link Target}. The method will
+ * immediately add a {@link Status#CANCELED} status to the action. However,
+ * it might be possible that the controller will continue to work on the
+ * cancellation. The controller needs to acknowledge or reject the
+ * cancellation using {@link DdiRootController#postCancelActionFeedback}.
+ *
+ * @param actionId to be canceled
+ * @return canceled {@link Action}
+ * @throws CancelActionNotAllowedException in case the given action is not active or is already canceled
+ * @throws EntityNotFoundException if action with given actionId does not exist.
+ */
+ @Override
+ @Modifying
+ @Transactional(isolation = Isolation.READ_COMMITTED)
+ 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");
+ }
+
+ 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(),
+ "manual cancelation requested"));
+ final Action saveAction = actionRepository.save(action);
+ cancelAssignDistributionSetEvent(action);
+
+ return saveAction;
+ } else {
+ throw new CancelActionNotAllowedException(
+ "Action [id: " + action.getId() + "] is not active and cannot be canceled");
}
}
@Override
- public Optional getActionForDownloadByTargetAndSoftwareModule(final String controllerId,
- final long moduleId) {
- throwExceptionIfTargetDoesNotExist(controllerId);
- throwExceptionIfSoftwareModuleDoesNotExist(moduleId);
+ public void updateActionExternalRef(final long actionId, @NotEmpty final String externalRef) {
+ // if access control for target repository is present check that caller has
+ // UPDATE access to the target of the action
+ targetRepository.getAccessController().ifPresent(
+ accessController -> accessController.assertOperationAllowed(
+ AccessController.Operation.UPDATE,
+ (JpaTarget) actionRepository
+ .findById(actionId)
+ .orElseThrow(() -> new EntityNotFoundException(Action.class, actionId))
+ .getTarget()));
+ actionRepository.updateExternalRef(actionId, externalRef);
+ }
- // it used to perform 3-table join query
- // @Query("Select a from JpaAction a join a.distributionSet ds join ds.modules modul where a.target.controllerId = :target and modul.id = :module order by a.id desc")
- // final List actions = actionRepository.findActionByTargetAndSoftwareModule(controllerId, moduleId);
- // TODO AC - we could fetch distribution sets in order to skip calls to serarch for modules
- return actionRepository
- .findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, true))
- .stream()
- .filter(action -> !action.isCancelingOrCanceled())
- .filter(action -> action.getDistributionSet().getModules()
- .stream()
- .anyMatch(module -> module.getId() == moduleId))
- .map(Action.class::cast)
- .findFirst();
+ @Override
+ public Optional getActionByExternalRef(@NotEmpty final String externalRef) {
+ return actionRepository.findByExternalRef(externalRef);
+ }
+
+ @Override
+ public void deleteExistingTarget(@NotEmpty final String controllerId) {
+ final Target target = targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId))
+ .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
+ targetRepository.deleteById(target.getId());
+ }
+
+ @Override
+ public Optional getInstalledActionByTarget(final String controllerId) {
+ final JpaTarget jpaTarget = targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId))
+ .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
+
+ final JpaDistributionSet installedDistributionSet = jpaTarget.getInstalledDistributionSet();
+ if (null != installedDistributionSet) {
+ return actionRepository.findFirstByTargetIdAndDistributionSetIdAndStatusOrderByIdDesc(jpaTarget.getId(),
+ installedDistributionSet.getId(), FINISHED);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public AutoConfirmationStatus activateAutoConfirmation(final String controllerId, final String initiator,
+ final String remark) {
+ return confirmationManagement.activateAutoConfirmation(controllerId, initiator, remark);
+ }
+
+ @Override
+ public void deactivateAutoConfirmation(final String controllerId) {
+ confirmationManagement.deactivateAutoConfirmation(controllerId);
+ }
+
+ @Override
+ public boolean updateOfflineAssignedVersion(@NotEmpty final String controllerId, final String distributionName, final String version) {
+ List distributionSetAssignmentResults =
+ systemSecurityContext.runAsSystem(() ->
+ distributionSetManagement.getByNameAndVersion(distributionName, version).map(
+ distributionSet -> deploymentManagement.offlineAssignedDistributionSets(
+ List.of(Map.entry(controllerId, distributionSet.getId())), controllerId))
+ .orElseThrow(() ->
+ new EntityNotFoundException(DistributionSet.class, Map.entry(distributionName, version))));
+
+ return distributionSetAssignmentResults.stream().findFirst()
+ .map(result -> result.getAlreadyAssigned() == 0)
+ .orElseThrow();
+ }
+
+ Optional getTargetType(String targetTypeName) {
+ return systemSecurityContext.runAsSystem(() -> targetTypeManagement.getByName(targetTypeName));
+ }
+
+ // for testing
+ void setTargetRepository(final TargetRepository targetRepositorySpy) {
+ this.targetRepository = targetRepositorySpy;
+ }
+
+ private static String formatQueryInStatementParams(final Collection paramNames) {
+ return "#" + String.join(",#", paramNames);
+ }
+
+ private static boolean isAddressChanged(final URI addressToUpdate, final URI address) {
+ return addressToUpdate == null || !addressToUpdate.equals(address);
+ }
+
+ private static boolean isNameChanged(final String nameToUpdate, final String name) {
+ return StringUtils.hasText(name) && !nameToUpdate.equals(name);
+ }
+
+ private static boolean isTypeChanged(final TargetType targetTypeToUpdate, final String type) {
+ return (type != null) && (targetTypeToUpdate == null || !targetTypeToUpdate.getName().equals(type));
+ }
+
+ private static boolean isStatusUnknown(final TargetUpdateStatus statusToUpdate) {
+ return TargetUpdateStatus.UNKNOWN == statusToUpdate;
+ }
+
+ private static boolean isAttributeEntryValid(final Map.Entry e) {
+ return isAttributeKeyValid(e.getKey()) && isAttributeValueValid(e.getValue());
+ }
+
+ private static boolean isAttributeKeyValid(final String key) {
+ return key != null && key.length() <= CONTROLLER_ATTRIBUTE_KEY_SIZE;
+ }
+
+ private static boolean isAttributeValueValid(final String value) {
+ return value == null || value.length() <= CONTROLLER_ATTRIBUTE_VALUE_SIZE;
+ }
+
+ private static void copy(final Map src, final Map trg) {
+ if (src == null || src.isEmpty()) {
+ return;
+ }
+ src.forEach((key, value) -> {
+ if (value != null) {
+ trg.put(key, value);
+ } else {
+ trg.remove(key);
+ }
+ });
}
private void throwExceptionIfTargetDoesNotExist(final String controllerId) {
@@ -350,64 +674,6 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
}
}
- @Override
- public boolean hasTargetArtifactAssigned(final String controllerId, final String sha1Hash) {
- throwExceptionIfTargetDoesNotExist(controllerId);
- return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(controllerId, sha1Hash)) > 0;
- }
-
- @Override
- public boolean hasTargetArtifactAssigned(final long targetId, final String sha1Hash) {
- throwExceptionIfTargetDoesNotExist(targetId);
- return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(targetId, sha1Hash)) > 0;
- }
-
- @Override
- public Optional findActiveActionWithHighestWeight(final String controllerId) {
- return findActiveActionsWithHighestWeight(controllerId, 1).stream().findFirst();
- }
-
- @Override
- public List findActiveActionsWithHighestWeight(final String controllerId, final int maxActionCount) {
- return findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount);
- }
-
- @Override
- public int getWeightConsideringDefault(final Action action) {
- return super.getWeightConsideringDefault(action);
- }
-
- @Override
- public Optional findActionWithDetails(final long actionId) {
- return actionRepository.findWithDetailsById(actionId);
- }
-
- @Override
- public void deleteExistingTarget(@NotEmpty final String controllerId) {
- final Target target = targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId))
- .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
- targetRepository.deleteById(target.getId());
- }
-
- @Override
- @Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
- public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address) {
- return findOrRegisterTargetIfItDoesNotExist(controllerId, address, null, null);
- }
-
- @Override
- @Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
- public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address,
- final String name, final String type) {
- final Specification spec =
- (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
-
- return targetRepository.findOne(spec).map(target -> updateTarget(target, address, name, type))
- .orElseGet(() -> createTarget(controllerId, address, name, type));
- }
-
private Target createTarget(final String controllerId, final URI address, final String name, final String type) {
log.debug("Creating target for thing ID \"{}\".", controllerId);
@@ -436,10 +702,6 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
return result;
}
- Optional getTargetType(String targetTypeName) {
- return systemSecurityContext.runAsSystem(() -> targetTypeManagement.getByName(targetTypeName));
- }
-
/**
* Flush the update queue by means to persisting
* {@link Target#getLastTargetQuery()}.
@@ -518,15 +780,10 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
}
}
- private static String formatQueryInStatementParams(final Collection paramNames) {
- return "#" + String.join(",#", paramNames);
- }
-
/**
* Stores target directly to DB in case either {@link Target#getAddress()}
* or {@link Target#getUpdateStatus()} or {@link Target#getName()} changes
* or the buffer queue is full.
- *
*/
private Target updateTarget(final JpaTarget toUpdate, final URI address, final String name, final String type) {
if (isStoreEager(toUpdate, address, name, type) || !queue.offer(new TargetPoll(toUpdate))) {
@@ -569,60 +826,6 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|| isStatusUnknown(toUpdate.getUpdateStatus());
}
- private static boolean isAddressChanged(final URI addressToUpdate, final URI address) {
- return addressToUpdate == null || !addressToUpdate.equals(address);
- }
-
- private static boolean isNameChanged(final String nameToUpdate, final String name) {
- return StringUtils.hasText(name) && !nameToUpdate.equals(name);
- }
-
- private static boolean isTypeChanged(final TargetType targetTypeToUpdate, final String type) {
- return (type != null) && (targetTypeToUpdate == null || !targetTypeToUpdate.getName().equals(type));
- }
-
- private static boolean isStatusUnknown(final TargetUpdateStatus statusToUpdate) {
- return TargetUpdateStatus.UNKNOWN == statusToUpdate;
- }
-
- @Override
- @Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(include = {
- ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
- public Action addCancelActionStatus(final ActionStatusCreate c) {
- final JpaActionStatusCreate create = (JpaActionStatusCreate) c;
-
- final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
-
- if (!action.isCancelingOrCanceled()) {
- throw new CancelActionNotAllowedException("The action is not in canceling state.");
- }
-
- final JpaActionStatus actionStatus = create.build();
-
- switch (actionStatus.getStatus()) {
- case CANCELED:
- case FINISHED:
- handleFinishedCancelation(actionStatus, action);
- break;
- case ERROR:
- case CANCEL_REJECTED:
- // Cancellation rejected. Back to running.
- action.setStatus(Status.RUNNING);
- break;
- default:
- // information status entry - check for a potential DOS attack
- assertActionStatusQuota(actionStatus, action);
- assertActionStatusMessageQuota(actionStatus);
- break;
- }
-
- actionStatus.setAction(actionRepository.save(action));
- actionStatusRepository.save(actionStatus);
-
- return action;
- }
-
private void handleFinishedCancelation(final JpaActionStatus actionStatus, final JpaAction action) {
// in case of successful cancellation we also report the success at
// the canceled action itself.
@@ -631,40 +834,12 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
DeploymentHelper.successCancellation(action, actionRepository, targetRepository);
}
- @Override
- @Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(include = {
- ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
- public Action addUpdateActionStatus(final ActionStatusCreate statusCreate) {
- return addActionStatus((JpaActionStatusCreate) statusCreate);
- }
-
- @Override
- protected void onActionStatusUpdate(final Action.Status updatedActionStatus, final JpaAction action) {
- switch (updatedActionStatus) {
- case ERROR:
- final JpaTarget target = (JpaTarget) action.getTarget();
- target.setUpdateStatus(TargetUpdateStatus.ERROR);
- handleErrorOnAction(action, target);
- break;
- case FINISHED:
- handleFinishedAndStoreInTargetStatus(action).ifPresent(this::requestControllerAttributes);
- break;
- case DOWNLOADED:
- handleDownloadedActionStatus(action).ifPresent(this::requestControllerAttributes);
- break;
- default:
- break;
- }
- }
-
/**
* Handles the case where the {@link Action.Status#DOWNLOADED} status is
* reported by the device. In case the update is finished, a controllerId
* will be returned to trigger a request for attributes.
*
- * @param action
- * updated action
+ * @param action updated action
* @return a present controllerId in case the attributes needs to be
* requested.
*/
@@ -707,8 +882,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
* reported by the device. In case the update is finished, a controllerId
* will be returned to trigger a request for attributes.
*
- * @param action
- * updated action
+ * @param action updated action
* @return a present controllerId in case the attributes needs to be
* requested.
*/
@@ -742,99 +916,18 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
return Optional.of(target.getControllerId());
}
- @Override
- @Transactional
- @Retryable(include = {
- ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
- public Target updateControllerAttributes(final String controllerId, final Map data,
- final UpdateMode mode) {
-
- /*
- * Constraints on attribute keys & values are not validated by
- * EclipseLink. Hence, they are validated here.
- */
- if (data.entrySet().stream().anyMatch(e -> !isAttributeEntryValid(e))) {
- throw new InvalidTargetAttributeException();
- }
-
- final JpaTarget target = targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId))
- .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
-
- // get the modifiable attribute map
- final Map controllerAttributes = target.getControllerAttributes();
- final UpdateMode updateMode = mode != null ? mode : UpdateMode.MERGE;
- switch (updateMode) {
- case REMOVE:
- // remove the addressed attributes
- data.keySet().forEach(controllerAttributes::remove);
- break;
- case REPLACE:
- // clear the attributes before adding the new attributes
- controllerAttributes.clear();
- copy(data, controllerAttributes);
- target.setRequestControllerAttributes(false);
- break;
- case MERGE:
- // just merge the attributes in
- copy(data, controllerAttributes);
- target.setRequestControllerAttributes(false);
- break;
- default:
- // unknown update mode
- throw new IllegalStateException("The update mode " + updateMode + " is not supported.");
- }
- assertTargetAttributesQuota(target);
-
- return targetRepository.save(target);
- }
-
- private static boolean isAttributeEntryValid(final Map.Entry e) {
- return isAttributeKeyValid(e.getKey()) && isAttributeValueValid(e.getValue());
- }
-
- private static boolean isAttributeKeyValid(final String key) {
- return key != null && key.length() <= CONTROLLER_ATTRIBUTE_KEY_SIZE;
- }
-
- private static boolean isAttributeValueValid(final String value) {
- return value == null || value.length() <= CONTROLLER_ATTRIBUTE_VALUE_SIZE;
- }
-
- private static void copy(final Map src, final Map trg) {
- if (src == null || src.isEmpty()) {
- return;
- }
- src.forEach((key, value) -> {
- if (value != null) {
- trg.put(key, value);
- } else {
- trg.remove(key);
- }
- });
- }
-
private void assertTargetAttributesQuota(final JpaTarget target) {
final int limit = quotaManagement.getMaxAttributeEntriesPerTarget();
QuotaHelper.assertAssignmentQuota(target.getId(), target.getControllerAttributes().size(), limit, "Attribute",
Target.class.getSimpleName(), null);
}
- @Override
- @Transactional
- @Retryable(include = {
- ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
- public Action registerRetrieved(final long actionId, final String message) {
- return handleRegisterRetrieved(actionId, message);
- }
-
/**
* Registers retrieved status for given {@link Target} and {@link Action} if
* it does not exist yet.
*
- * @param actionId
- * to the handle status for
- * @param message
- * for the status
+ * @param actionId to the handle status for
+ * @param message for the status
* @return the updated action in case the status has been changed to
* {@link Status#RETRIEVED}
*/
@@ -880,78 +973,86 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
return action;
}
- @Override
- @Transactional
- @Retryable(include = {
- ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
- public ActionStatus addInformationalActionStatus(final ActionStatusCreate c) {
- final JpaActionStatusCreate create = (JpaActionStatusCreate) c;
- final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
- final JpaActionStatus statusMessage = create.build();
- statusMessage.setAction(action);
-
- assertActionStatusQuota(statusMessage, action);
- assertActionStatusMessageQuota(statusMessage);
-
- return actionStatusRepository.save(statusMessage);
+ private void cancelAssignDistributionSetEvent(final Action action) {
+ afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher()
+ .publishEvent(new CancelTargetAssignmentEvent(action, eventPublisherHolder.getApplicationId())));
}
- @Override
- public Optional getByControllerId(final String controllerId) {
- return targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId)).map(Target.class::cast);
- }
+ /**
+ * EventTimer to handle reduction of polling interval based on maintenance
+ * window start time. Class models the next polling time as an event to be
+ * raised and time to next polling as a timer. The event, in this case the
+ * polling, should happen when timer expires. Class makes use of java.time
+ * package to manipulate and calculate timer duration.
+ */
+ private static class EventTimer {
- @Override
- public Optional get(final long targetId) {
- return targetRepository.findById(targetId).map(t -> (Target) t);
- }
+ private final String defaultEventInterval;
+ private final Duration defaultEventIntervalDuration;
- @Override
- public Page findActionStatusByAction(final Pageable pageReq, final long actionId) {
- if (!actionRepository.existsById(actionId)) {
- throw new EntityNotFoundException(Action.class, actionId);
+ private final String minimumEventInterval;
+ private final Duration minimumEventIntervalDuration;
+
+ private final TemporalUnit timeUnit;
+
+ /**
+ * Constructor.
+ *
+ * @param defaultEventInterval default timer value to use for interval between events.
+ * This puts an upper bound for the timer value
+ * @param minimumEventInterval for loading {@link DistributionSet#getModules()}. This
+ * puts a lower bound to the timer value
+ * @param timeUnit representing the unit of time to be used for timer.
+ */
+ EventTimer(final String defaultEventInterval, final String minimumEventInterval, final TemporalUnit timeUnit) {
+ this.defaultEventInterval = defaultEventInterval;
+ this.defaultEventIntervalDuration = MaintenanceScheduleHelper.convertToISODuration(defaultEventInterval);
+
+ this.minimumEventInterval = minimumEventInterval;
+ this.minimumEventIntervalDuration = MaintenanceScheduleHelper.convertToISODuration(minimumEventInterval);
+
+ this.timeUnit = timeUnit;
}
- return actionStatusRepository.findByActionId(pageReq, actionId);
- }
+ /**
+ * This method calculates the time interval until the next event based
+ * on the desired number of events before the time when interval is
+ * reset to default. The return value is bounded by
+ * {@link EventTimer#defaultEventInterval} and
+ * {@link EventTimer#minimumEventInterval}.
+ *
+ * @param eventCount number of events desired until the interval is reset to
+ * default. This is not guaranteed as the interval between
+ * events cannot be less than the minimum interval
+ * @param timerResetTime time when exponential forwarding should reset to default
+ * @return String in HH:mm:ss format for time to next event.
+ */
+ String timeToNextEvent(final int eventCount, final ZonedDateTime timerResetTime) {
+ final ZonedDateTime currentTime = ZonedDateTime.now();
- @Override
- public List getActionHistoryMessages(final long actionId, final int messageCount) {
- // Just return empty list in case messageCount is zero.
- if (messageCount == 0) {
- return Collections.emptyList();
+ // If there is no reset time, or if we already past the reset time,
+ // return the default interval.
+ if (timerResetTime == null || currentTime.compareTo(timerResetTime) > 0) {
+ return defaultEventInterval;
+ }
+
+ // Calculate the interval timer based on desired event count.
+ final Duration currentIntervalDuration = Duration.of(currentTime.until(timerResetTime, timeUnit), timeUnit)
+ .dividedBy(eventCount);
+
+ // Need not return interval greater than the default.
+ if (currentIntervalDuration.compareTo(defaultEventIntervalDuration) > 0) {
+ return defaultEventInterval;
+ }
+
+ // Should not return interval less than minimum.
+ if (currentIntervalDuration.compareTo(minimumEventIntervalDuration) < 0) {
+ return minimumEventInterval;
+ }
+
+ return String.format("%02d:%02d:%02d", currentIntervalDuration.toHours(),
+ currentIntervalDuration.toMinutes() % 60, currentIntervalDuration.getSeconds() % 60);
}
-
- // For negative and large value of messageCount, limit the number of
- // messages.
- final int limit = messageCount < 0 || messageCount >= RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
- ? RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
- : messageCount;
-
- final PageRequest pageable = PageRequest.of(0, limit, Sort.by(Direction.DESC, "occurredAt"));
- final Page messages = actionStatusRepository.findMessagesByActionIdAndMessageNotLike(pageable, actionId,
- RepositoryConstants.SERVER_MESSAGE_PREFIX + "%");
-
- log.debug("Retrieved {} message(s) from action history for action {}.", messages.getNumberOfElements(),
- actionId);
-
- return messages.getContent();
- }
-
- @Override
- public Optional getSoftwareModule(final long id) {
- return softwareModuleRepository.findById(id).map(s -> (SoftwareModule) s);
- }
-
- @Override
- public Map> findTargetVisibleMetaDataBySoftwareModuleId(
- final Collection moduleId) {
-
- return softwareModuleMetadataRepository
- .findBySoftwareModuleIdInAndTargetVisible(PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT),
- moduleId, true)
- .getContent().stream().collect(Collectors.groupingBy(o -> (Long) o[0],
- Collectors.mapping(o -> (SoftwareModuleMetadata) o[1], Collectors.toList())));
}
private static class TargetPoll {
@@ -1011,120 +1112,4 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
}
}
-
- /**
- * Cancels given {@link Action} for this {@link Target}. The method will
- * immediately add a {@link Status#CANCELED} status to the action. However,
- * it might be possible that the controller will continue to work on the
- * cancellation. The controller needs to acknowledge or reject the
- * cancellation using {@link DdiRootController#postCancelActionFeedback}.
- *
- * @param actionId
- * to be canceled
- *
- * @return canceled {@link Action}
- *
- * @throws CancelActionNotAllowedException
- * in case the given action is not active or is already canceled
- * @throws EntityNotFoundException
- * if action with given actionId does not exist.
- */
- @Override
- @Modifying
- @Transactional(isolation = Isolation.READ_COMMITTED)
- 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");
- }
-
- 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(),
- "manual cancelation requested"));
- final Action saveAction = actionRepository.save(action);
- cancelAssignDistributionSetEvent(action);
-
- return saveAction;
- } else {
- throw new CancelActionNotAllowedException(
- "Action [id: " + action.getId() + "] is not active and cannot be canceled");
- }
- }
-
- @Override
- public void updateActionExternalRef(final long actionId, @NotEmpty final String externalRef) {
- // if access control for target repository is present check that caller has
- // UPDATE access to the target of the action
- targetRepository.getAccessController().ifPresent(
- accessController -> accessController.assertOperationAllowed(
- AccessController.Operation.UPDATE,
- (JpaTarget) actionRepository
- .findById(actionId)
- .orElseThrow(() -> new EntityNotFoundException(Action.class, actionId))
- .getTarget()));
- actionRepository.updateExternalRef(actionId, externalRef);
- }
-
- @Override
- public Optional getActionByExternalRef(@NotEmpty final String externalRef) {
- return actionRepository.findByExternalRef(externalRef);
- }
-
- @Override
- public Optional getInstalledActionByTarget(final String controllerId) {
- final JpaTarget jpaTarget = targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId))
- .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
-
- final JpaDistributionSet installedDistributionSet = jpaTarget.getInstalledDistributionSet();
- if (null != installedDistributionSet) {
- return actionRepository.findFirstByTargetIdAndDistributionSetIdAndStatusOrderByIdDesc(jpaTarget.getId(),
- installedDistributionSet.getId(), FINISHED);
- } else {
- return Optional.empty();
- }
- }
-
- @Override
- public AutoConfirmationStatus activateAutoConfirmation(final String controllerId, final String initiator,
- final String remark) {
- return confirmationManagement.activateAutoConfirmation(controllerId, initiator, remark);
- }
-
- @Override
- public void deactivateAutoConfirmation(final String controllerId) {
- confirmationManagement.deactivateAutoConfirmation(controllerId);
- }
-
- @Override
- public boolean updateOfflineAssignedVersion(@NotEmpty final String controllerId, final String distributionName, final String version){
- List distributionSetAssignmentResults =
- systemSecurityContext.runAsSystem(() ->
- distributionSetManagement.getByNameAndVersion(distributionName,version).map(
- distributionSet -> deploymentManagement.offlineAssignedDistributionSets(
- List.of(Map.entry(controllerId, distributionSet.getId())),controllerId))
- .orElseThrow(() ->
- new EntityNotFoundException(DistributionSet.class, Map.entry(distributionName, version))));
-
- return distributionSetAssignmentResults.stream().findFirst()
- .map(result-> result.getAlreadyAssigned()==0)
- .orElseThrow();
- }
-
- private void cancelAssignDistributionSetEvent(final Action action) {
- afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher()
- .publishEvent(new CancelTargetAssignmentEvent(action, eventPublisherHolder.getApplicationId())));
- }
-
- // for testing
- void setTargetRepository(final TargetRepository targetRepositorySpy) {
- this.targetRepository = targetRepositorySpy;
- }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java
index 69f9bb1b4..93a43ca7f 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java
@@ -127,16 +127,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
+ ACTION_PAGE_LIMIT;
private static final EnumMap 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 auditorProvider,
@@ -183,9 +182,18 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
- public List offlineAssignedDistributionSets(
- final Collection> assignments) {
- return offlineAssignedDistributionSets(assignments,tenantAware.getCurrentUsername());
+ public List assignDistributionSets(
+ final List deploymentRequests) {
+ return assignDistributionSets(tenantAware.getCurrentUsername(), deploymentRequests, null);
+ }
+
+ @Override
+ @Transactional(isolation = Isolation.READ_COMMITTED)
+ public List assignDistributionSets(final String initiatedBy,
+ final List 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 assignDistributionSets(
- final List deploymentRequests) {
- return assignDistributionSets(tenantAware.getCurrentUsername(), deploymentRequests, null);
+ public List offlineAssignedDistributionSets(
+ final Collection> assignments) {
+ return offlineAssignedDistributionSets(assignments, tenantAware.getCurrentUsername());
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
- public List assignDistributionSets(final String initiatedBy,
- final List 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> 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> 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 findAction(final long actionId) {
+ return actionRepository.findById(actionId)
+ .filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId())))
+ .map(JpaAction.class::cast);
+ }
+
+ @Override
+ public Slice findActionsAll(final Pageable pageable) {
+ return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, null);
+ }
+
+ @Override
+ public Slice findActions(final String rsqlParam, final Pageable pageable) {
+ final List> specList = List.of(
+ RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database));
+ return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, specList);
+ }
+
+ @Override
+ public Page findActionsByTarget(final String rsqlParam, final String controllerId,
+ final Pageable pageable) {
+ assertTargetReadAllowed(controllerId);
+
+ final List> specList = Arrays.asList(
+ RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database),
+ ActionSpecifications.byTargetControllerId(controllerId));
+
+ return JpaManagementHelper.findAllWithCountBySpec(actionRepository, pageable, specList);
+ }
+
+ @Override
+ public Slice findActionsByTarget(final String controllerId, final Pageable pageable) {
+ assertTargetReadAllowed(controllerId);
+ return actionRepository.findAll(ActionSpecifications.byTargetControllerId(controllerId), pageable)
+ .map(Action.class::cast);
+ }
+
+ @Override
+ public Page 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 findMessagesByActionStatusId(final Pageable pageable, final long actionStatusId) {
+ final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
+
+ final CriteriaQuery msgQuery = cb.createQuery(String.class);
+ final Root as = msgQuery.from(JpaActionStatus.class);
+ final ListJoin join = as.joinList("messages", JoinType.LEFT);
+ final CriteriaQuery selMsgQuery = msgQuery.select(join);
+ selMsgQuery.where(cb.equal(as.get(JpaActionStatus_.id), actionStatusId));
+
+ final List result = new ArrayList<>(entityManager.createQuery(selMsgQuery)
+ .setFirstResult((int) pageable.getOffset()).setMaxResults(pageable.getPageSize()).getResultList());
+
+ return new PageImpl<>(result, pageable, result.size());
+ }
+
+ @Override
+ public Optional findActionWithDetails(final long actionId) {
+ return actionRepository.findWithDetailsById(actionId)
+ .filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId())));
+ }
+
+ @Override
+ public Page findActiveActionsByTarget(final Pageable pageable, final String controllerId) {
+ assertTargetReadAllowed(controllerId);
+ return actionRepository
+ .findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, true), pageable)
+ .map(Action.class::cast);
+ }
+
+ @Override
+ public Page findInActiveActionsByTarget(final Pageable pageable, final String controllerId) {
+ assertTargetReadAllowed(controllerId);
+ return actionRepository
+ .findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, false), pageable)
+ .map(Action.class::cast);
+ }
+
+ @Override
+ public List 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 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 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 rolloutGroupActions) {
+ // Close actions already assigned and collect pending assignments
+ final List 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 newTargetAssignments = startScheduledActionsAndHandleOpenCancellationFirst(pendingTargetAssignments);
+ if (!newTargetAssignments.isEmpty()) {
+ onlineDsAssignmentStrategy.sendDeploymentEvents(newTargetAssignments.get(0).getDistributionSet().getId(), newTargetAssignments);
+ }
+ }
+
+ @Override
+ public Optional getAssignedDistributionSet(final String controllerId) {
+ return targetRepository
+ .findOne(TargetSpecifications.hasControllerId(controllerId))
+ .map(JpaTarget::getAssignedDistributionSet);
+ }
+
+ @Override
+ public Optional getInstalledDistributionSet(final String controllerId) {
+ return targetRepository
+ .findOne(TargetSpecifications.hasControllerId(controllerId))
+ .map(JpaTarget::getInstalledDistributionSet);
+ }
+
+ @Override
+ @Transactional(readOnly = false)
+ public int deleteActionsByStatusAndLastModifiedBefore(final Set 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> convertRequest(
+ final Collection 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> getTargetEntitiesAsChunks(final List 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 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 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 assignDistributionSets(final String initiatedBy,
@@ -327,12 +739,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
return deploymentRequests;
}
- private static Map> convertRequest(
- final Collection 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 targetsWithActionType, final String actionMessage,
final AbstractDsAssignmentStrategy assignmentStrategy) {
@@ -355,21 +761,14 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
* to {@link TargetUpdateStatus#IN_SYNC}
* D. does not send a {@link TargetAssignDistributionSetEvent}.
*
- * @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 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> getTargetEntitiesAsChunks(final List 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 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 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> 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 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 rolloutGroupActions) {
- // Close actions already assigned and collect pending assignments
- final List 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 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 findAction(final long actionId) {
- return actionRepository.findById(actionId)
- .filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId())))
- .map(JpaAction.class::cast);
- }
-
- @Override
- public Optional findActionWithDetails(final long actionId) {
- return actionRepository.findWithDetailsById(actionId)
- .filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId())));
- }
-
- @Override
- public Slice findActionsByTarget(final String controllerId, final Pageable pageable) {
- assertTargetReadAllowed(controllerId);
- return actionRepository.findAll(ActionSpecifications.byTargetControllerId(controllerId), pageable)
- .map(Action.class::cast);
- }
-
- @Override
- public Page findActionsByTarget(final String rsqlParam, final String controllerId,
- final Pageable pageable) {
- assertTargetReadAllowed(controllerId);
-
- final List> specList = Arrays.asList(
- RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database),
- ActionSpecifications.byTargetControllerId(controllerId));
-
- return JpaManagementHelper.findAllWithCountBySpec(actionRepository, pageable, specList);
- }
-
- @Override
- public Page findActiveActionsByTarget(final Pageable pageable, final String controllerId) {
- assertTargetReadAllowed(controllerId);
- return actionRepository
- .findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, true), pageable)
- .map(Action.class::cast);
- }
-
- @Override
- public Page findInActiveActionsByTarget(final Pageable pageable, final String controllerId) {
- assertTargetReadAllowed(controllerId);
- return actionRepository
- .findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, false), pageable)
- .map(Action.class::cast);
- }
-
- @Override
- public List 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> 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 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 findMessagesByActionStatusId(final Pageable pageable, final long actionStatusId) {
- final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
-
- final CriteriaQuery msgQuery = cb.createQuery(String.class);
- final Root as = msgQuery.from(JpaActionStatus.class);
- final ListJoin join = as.joinList("messages", JoinType.LEFT);
- final CriteriaQuery selMsgQuery = msgQuery.select(join);
- selMsgQuery.where(cb.equal(as.get(JpaActionStatus_.id), actionStatusId));
-
- final List 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> specList = List.of(
- RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database));
- return JpaManagementHelper.countBySpec(actionRepository, specList);
- }
-
- @Override
- public Slice findActionsAll(final Pageable pageable) {
- return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, null);
- }
-
- @Override
- public Slice findActions(final String rsqlParam, final Pageable pageable) {
- final List> specList = List.of(
- RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database));
- return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, specList);
- }
-
- @Override
- public Optional getAssignedDistributionSet(final String controllerId) {
- return targetRepository
- .findOne(TargetSpecifications.hasControllerId(controllerId))
- .map(JpaTarget::getAssignedDistributionSet);
- }
-
- @Override
- public Optional getInstalledDistributionSet(final String controllerId) {
- return targetRepository
- .findOne(TargetSpecifications.hasControllerId(controllerId))
- .map(JpaTarget::getInstalledDistributionSet);
- }
-
- @Override
- @Transactional(readOnly = false)
- public int deleteActionsByStatusAndLastModifiedBefore(final Set 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 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);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java
index 8be6a0ad6..ce418ac79 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java
@@ -97,6 +97,26 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
}
}
+ @Override
+ public DistributionSetInvalidationCount countEntitiesForInvalidation(
+ final DistributionSetInvalidation distributionSetInvalidation) {
+ return systemSecurityContext.runAsSystem(() -> {
+ final Collection 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 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 setIds) {
return setIds.stream().mapToLong(rolloutManagement::countByDistributionSetIdAndRolloutIsStoppable).sum();
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java
index 7b36423fd..4a530b3e1 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java
@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
+import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -75,7 +77,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Statistic;
-import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.dao.ConcurrencyFailureException;
@@ -92,11 +93,8 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
-import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
-
/**
* JPA implementation of {@link DistributionSetManagement}.
- *
*/
@Transactional(readOnly = true)
@Validated
@@ -152,91 +150,25 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
@Override
- public Optional getWithDetails(final long id) {
- return distributionSetRepository.findById(id).map(DistributionSet.class::cast);
- }
+ @Transactional
+ @Retryable(include = {
+ ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ public List create(final Collection creates) {
+ final List toCreate = creates.stream().map(JpaDistributionSetCreate.class::cast)
+ .map(this::setDefaultTypeIfMissing).map(JpaDistributionSetCreate::build).toList();
- @Override
- public long countByTypeId(final long typeId) {
- if (!distributionSetTypeManagement.exists(typeId)) {
- throw new EntityNotFoundException(DistributionSetType.class, typeId);
- }
-
- return distributionSetRepository.count(DistributionSetSpecification.byType(typeId));
- }
-
- @Override
- public List countRolloutsByStatusForDistributionSet(final Long id) {
- assertDistributionSetExists(id);
-
- return distributionSetRepository.countRolloutsByStatusForDistributionSet(id).stream()
- .map(Statistic.class::cast).toList();
- }
-
- @Override
- public List countActionsByStatusForDistributionSet(final Long id) {
- assertDistributionSetExists(id);
-
- return distributionSetRepository.countActionsByStatusForDistributionSet(id).stream()
- .map(Statistic.class::cast).toList();
- }
-
- @Override
- public Long countAutoAssignmentsForDistributionSet(final Long id) {
- assertDistributionSetExists(id);
-
- return distributionSetRepository.countAutoAssignmentsForDistributionSet(id);
+ return Collections.unmodifiableList(distributionSetRepository.saveAll(AccessController.Operation.CREATE, toCreate));
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
- public List assignTag(final Collection ids, final long dsTagId) {
- return updateTag(ids, dsTagId, (tag, distributionSet) -> {
- if (distributionSet.getTags().contains(tag)) {
- return distributionSet;
- } else {
- distributionSet.addTag(tag);
- return distributionSetRepository.save(distributionSet);
- }
- });
- }
- @Override
- @Transactional
- @Retryable(include = {
- ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
- public List