diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java index e1976c9a6..aad0839cd 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java @@ -572,7 +572,7 @@ public class DeploymentManagement { @Modifying @Transactional @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) - public Action forceQuitAction(@NotNull final Action action, @NotNull final Target target) { + public Action forceQuitAction(@NotNull final Action action) { final Action mergedAction = entityManager.merge(action); if (!mergedAction.isCancelingOrCanceled()) { diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Target.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Target.java index bb7781733..07a97ebcf 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Target.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Target.java @@ -69,7 +69,7 @@ import org.springframework.data.domain.Persistable; @Index(name = "sp_idx_target_03", columnList = "tenant,controller_id,assigned_distribution_set"), @Index(name = "sp_idx_target_04", columnList = "tenant,created_at"), @Index(name = "sp_idx_target_prim", columnList = "tenant,id") }, uniqueConstraints = @UniqueConstraint(columnNames = { - "controller_id", "tenant" }, name = "uk_tenant_controller_id") ) + "controller_id", "tenant" }, name = "uk_tenant_controller_id")) @NamedEntityGraph(name = "Target.detail", attributeNodes = { @NamedAttributeNode("tags"), @NamedAttributeNode(value = "assignedDistributionSet"), @NamedAttributeNode(value = "targetInfo") }) public class Target extends NamedEntity implements Persistable { @@ -81,21 +81,21 @@ public class Target extends NamedEntity implements Persistable { private String controllerId; @Transient - private boolean isNew = false; + private boolean entityNew = false; @ManyToMany(targetEntity = TargetTag.class) @JoinTable(name = "sp_target_target_tag", joinColumns = { - @JoinColumn(name = "target", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_target") ) }, inverseJoinColumns = { - @JoinColumn(name = "tag", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag") ) }) + @JoinColumn(name = "target", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_target")) }, inverseJoinColumns = { + @JoinColumn(name = "tag", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag")) }) private Set tags = new HashSet<>(); @CascadeOnDelete @OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = { CascadeType.REMOVE }) - @JoinColumn(name = "target", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_act_hist_targ") ) + @JoinColumn(name = "target", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_act_hist_targ")) private final List actions = new ArrayList<>(); @ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = DistributionSet.class) - @JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds") ) + @JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds")) private DistributionSet assignedDistributionSet; @CascadeOnDelete @@ -217,15 +217,15 @@ public class Target extends NamedEntity implements Persistable { @Override @Transient public boolean isNew() { - return isNew; + return entityNew; } /** * @param isNew * the isNew to set */ - public void setNew(final boolean isNew) { - this.isNew = isNew; + public void setNew(final boolean entityNew) { + this.entityNew = entityNew; } /** diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java index d491319fa..e487ee85a 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java @@ -73,11 +73,11 @@ public class TargetInfo implements Persistable, Serializable { private Long targetId; @Transient - private boolean isNew = false; + private boolean entityNew = false; @CascadeOnDelete @OneToOne(cascade = { CascadeType.MERGE, CascadeType.REMOVE }, fetch = FetchType.LAZY, targetEntity = Target.class) - @JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_stat_targ") ) + @JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_stat_targ")) @MapsId // use deprecated annotation until HHH-8862 is fixed // @SuppressWarnings( "deprecation" ) @@ -98,7 +98,7 @@ public class TargetInfo implements Persistable, Serializable { private TargetUpdateStatus updateStatus = TargetUpdateStatus.UNKNOWN; @ManyToOne(optional = true, fetch = FetchType.LAZY) - @JoinColumn(name = "installed_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_inst_ds") ) + @JoinColumn(name = "installed_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_inst_ds")) private DistributionSet installedDistributionSet; /** @@ -108,7 +108,7 @@ public class TargetInfo implements Persistable, Serializable { @Column(name = "attribute_value", length = 128) @MapKeyColumn(name = "attribute_key", nullable = false, length = 32) @CollectionTable(name = "sp_target_attributes", joinColumns = { - @JoinColumn(name = "target_id") }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target") ) + @JoinColumn(name = "target_id") }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target")) // use deprecated annotation until HHH-8862 is fixed @SuppressWarnings("deprecation") // @org.hibernate.annotations.ForeignKey( name = "fk_targ_attrib_target" ) @@ -154,15 +154,15 @@ public class TargetInfo implements Persistable, Serializable { @Override @Transient public boolean isNew() { - return isNew; + return entityNew; } /** * @param isNew * the isNew to set */ - public void setNew(final boolean isNew) { - this.isNew = isNew; + public void setNew(final boolean entityNew) { + this.entityNew = entityNew; } /** diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DeploymentManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DeploymentManagementTest.java index 9530a61c3..ecf079c29 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DeploymentManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DeploymentManagementTest.java @@ -10,7 +10,6 @@ package org.eclipse.hawkbit.repository; import static org.fest.assertions.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import java.util.ArrayList; @@ -312,7 +311,7 @@ public class DeploymentManagementTest extends AbstractIntegrationTest { deploymentManagement.cancelAction(assigningAction, target); assigningAction = deploymentManagement.findActionWithDetails(assigningAction.getId()); - deploymentManagement.forceQuitAction(assigningAction, target); + deploymentManagement.forceQuitAction(assigningAction); assigningAction = deploymentManagement.findActionWithDetails(assigningAction.getId()); @@ -350,8 +349,7 @@ public class DeploymentManagementTest extends AbstractIntegrationTest { // force quit assignment try { - deploymentManagement.forceQuitAction(assigningAction, - targetManagement.findTargetByControllerID(target.getControllerId())); + deploymentManagement.forceQuitAction(assigningAction); fail("expected ForceQuitActionNotAllowedException"); } catch (final ForceQuitActionNotAllowedException ex) { } @@ -764,13 +762,16 @@ public class DeploymentManagementTest extends AbstractIntegrationTest { distributionSetManagement.findDistributionSetByIdWithDetails(dsA.getId()).getOptLockRevision()); // verifying that the assignment is correct - assertEquals("Active target actions are wrong", 1, deploymentManagement.findActiveActionsByTarget(targ).size()); - assertEquals("Target actions are wrong", 1, deploymentManagement.findActionsByTarget(targ).size()); - assertEquals("Target status is wrong", TargetUpdateStatus.PENDING, targ.getTargetInfo().getUpdateStatus()); - assertEquals("Assigned ds is wrong", dsA, targ.getAssignedDistributionSet()); - assertEquals("Active ds is wrong", dsA, - deploymentManagement.findActiveActionsByTarget(targ).get(0).getDistributionSet()); - assertNull("Installed ds should be null", targ.getTargetInfo().getInstalledDistributionSet()); + assertThat(deploymentManagement.findActiveActionsByTarget(targ).size()).as("Active target actions are wrong") + .isEqualTo(1); + assertThat(deploymentManagement.findActionsByTarget(targ).size()).as("Target actions are wrong").isEqualTo(1); + assertThat(targ.getTargetInfo().getUpdateStatus()).as("UpdateStatus of target is wrong") + .isEqualTo(TargetUpdateStatus.PENDING); + assertThat(targ.getAssignedDistributionSet()).as("Assigned distribution set of target is wrong").isEqualTo(dsA); + assertThat(deploymentManagement.findActiveActionsByTarget(targ).get(0).getDistributionSet()) + .as("Distribution set of actionn is wrong").isEqualTo(dsA); + assertThat(deploymentManagement.findActiveActionsByTarget(targ).get(0).getDistributionSet()) + .as("Installed distribution set of action should be null").isNotNull(); final Page updAct = actionRepository.findByDistributionSet(pageReq, dsA); final Action action = updAct.getContent().get(0); diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/RolloutManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/RolloutManagementTest.java index a405e7b7d..22f9ee8b4 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/RolloutManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/RolloutManagementTest.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import org.eclipse.hawkbit.AbstractIntegrationTest; import org.eclipse.hawkbit.TestDataUtil; @@ -34,6 +35,8 @@ import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus; import org.eclipse.hawkbit.repository.rsql.RSQLUtility; +import org.eclipse.hawkbit.repository.utils.MultipleInvokeHelper; +import org.eclipse.hawkbit.repository.utils.SuccessCondition; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Description; @@ -859,7 +862,7 @@ public class RolloutManagementTest extends AbstractIntegrationTest { @Test @Description("Verify the creation and the start of a rollout in asynchronous mode.") - public void createAndStartRolloutInAsync() { + public void createAndStartRolloutInAsync() throws Exception { final int amountTargetsForRollout = 500; final int amountGroups = 5; @@ -883,31 +886,18 @@ public class RolloutManagementTest extends AbstractIntegrationTest { myRollout = rolloutManagement.createRolloutAsync(myRollout, amountGroups, conditions); - int counter = 1; - int counterMax = 10; - while (!isRolloutInGivenStatus(myRollout.getId(), RolloutStatus.READY) && (counter <= counterMax)) { - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - counter++; - } + SuccessConditionRolloutStatus conditionRolloutTargetCount = new SuccessConditionRolloutStatus( + RolloutStatus.READY); + assertThat(MultipleInvokeHelper.doWithTimeout(new RolloutStatusCallable(myRollout.getId()), + conditionRolloutTargetCount, 15000, 500)).as("Rollout status").isNotNull(); myRollout = rolloutManagement.findRolloutById(myRollout.getId()); assertThat(myRollout.getStatus()).isEqualTo(RolloutStatus.READY); rolloutManagement.startRolloutAsync(myRollout); - counter = 1; - counterMax = 10; - while (!isRolloutInGivenStatus(myRollout.getId(), RolloutStatus.RUNNING) && counter <= counterMax) { - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - counter++; - } + conditionRolloutTargetCount = new SuccessConditionRolloutStatus(RolloutStatus.RUNNING); + assertThat(MultipleInvokeHelper.doWithTimeout(new RolloutStatusCallable(myRollout.getId()), + conditionRolloutTargetCount, 15000, 500)).as("Rollout status").isNotNull(); myRollout = rolloutManagement.findRolloutById(myRollout.getId()); assertThat(myRollout.getStatus()).isEqualTo(RolloutStatus.RUNNING); @@ -917,14 +907,6 @@ public class RolloutManagementTest extends AbstractIntegrationTest { validateRolloutActionStatus(myRollout.getId(), expectedTargetCountStatus); } - private boolean isRolloutInGivenStatus(final Long rolloutID, final RolloutStatus status) { - final Rollout myRollout = rolloutManagement.findRolloutById(rolloutID); - if (myRollout.getStatus() == status) { - return true; - } - return false; - } - private void validateRolloutGroupActionStatus(final RolloutGroup rolloutGroup, final Map expectedTargetCountStatus) { final RolloutGroup rolloutGroupWithDetail = rolloutGroupManagement @@ -1021,4 +1003,35 @@ public class RolloutManagementTest extends AbstractIntegrationTest { return map; } + private static class SuccessConditionRolloutStatus implements SuccessCondition { + + private final RolloutStatus rolloutStatus; + + public SuccessConditionRolloutStatus(final RolloutStatus rolloutStatus) { + this.rolloutStatus = rolloutStatus; + } + + @Override + public boolean success(final RolloutStatus result) { + return result.equals(rolloutStatus); + } + } + + private class RolloutStatusCallable implements Callable { + + final Long rolloutId; + + RolloutStatusCallable(final Long rolloutId) { + this.rolloutId = rolloutId; + } + + @Override + public RolloutStatus call() throws Exception { + + final Rollout myRollout = rolloutManagement.findRolloutById(rolloutId); + return myRollout.getStatus(); + + } + } + } diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/utils/MultipleInvokeHelper.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/utils/MultipleInvokeHelper.java new file mode 100644 index 000000000..d40fe1fd0 --- /dev/null +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/utils/MultipleInvokeHelper.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.utils; + +import java.util.concurrent.Callable; + +/** + * Helper to call a request multiple times regarding a given condition until + * timeout is reached. + * + */ +public final class MultipleInvokeHelper { + + /** + * Call with timeout until result is not null. + * + * @param callable + * class + * @param timeout + * value + * @param pollInterval + * value + * @return + * @throws Exception + */ + public static T doWithTimeoutUntilResultIsNotNull(final Callable callable, final long timeout, + final long pollInterval) throws Exception // NOPMD + { + return doWithTimeout(callable, new SuccessCondition() { + @Override + public boolean success(final T result) { + return result != null; + }; + }, timeout, pollInterval); + } + + /** + * Call with timeout. + * + * @param callable + * class + * @param successCondition + * class + * @param timeout + * value + * @param pollInterval + * value + * @return + * @throws Exception + */ + public static T doWithTimeout(final Callable callable, final SuccessCondition successCondition, + final long timeout, final long pollInterval) throws Exception // NOPMD + { + + if (pollInterval < 0) { + throw new IllegalArgumentException("pollInterval must non negative"); + } + + long duration = 0; + Exception exception = null; + T returnValue = null; + while (untilTimeoutReached(timeout, duration)) { + try { + returnValue = callable.call(); + // clear exception + exception = null; + } catch (final Exception ex) { + exception = ex; + } + Thread.sleep(pollInterval); + duration += pollInterval > 0 ? pollInterval : 1; + if (exception == null && successCondition.success(returnValue)) { + return returnValue; + } else { + returnValue = null; + } + } + if (exception != null) { + throw exception; + } + return returnValue; + } + + private static boolean untilTimeoutReached(final long timeout, final long duration) { + return duration <= timeout || timeout < 0; + } + +} diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/utils/SuccessCondition.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/utils/SuccessCondition.java new file mode 100644 index 000000000..6c9553dc8 --- /dev/null +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/utils/SuccessCondition.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.utils; + +/** + * SuccessCondition Interface. + * + * @param + * type of the value to get verified + */ +public interface SuccessCondition { + + /** + * The implementation of the success condition. + * + * @param result + * @return + */ + boolean success(final T result); + +} diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java index 69a38f87d..faa31a7cc 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java @@ -209,7 +209,7 @@ public class TargetResource implements TargetRestApi { final Action action = findActionWithExceptionIfNotFound(actionId); if (force) { - this.deploymentManagement.forceQuitAction(action, target); + this.deploymentManagement.forceQuitAction(action); } else { this.deploymentManagement.cancelAction(action, target); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/actionhistory/ActionHistoryTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/actionhistory/ActionHistoryTable.java index f8d53b1bf..c9fa8f39c 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/actionhistory/ActionHistoryTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/actionhistory/ActionHistoryTable.java @@ -844,7 +844,7 @@ public class ActionHistoryTable extends TreeTable implements Handler { if (actionId != null) { final Action activeAction = deploymentManagement.findAction(actionId); try { - deploymentManagement.forceQuitAction(activeAction, target); + deploymentManagement.forceQuitAction(activeAction); return true; } catch (final CancelActionNotAllowedException e) { LOG.info("Force Cancel action not allowed exception :{}", e);