Fix ui concurrent issue for rollouts validation (#482)
Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>
This commit is contained in:
committed by
Kai Zimmermann
parent
eed9ab4f3e
commit
5958d1479c
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.ui.rollout.rollout;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -30,10 +31,10 @@ import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
|
|||||||
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon;
|
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon;
|
||||||
import org.eclipse.hawkbit.ui.filtermanagement.TargetFilterBeanQuery;
|
import org.eclipse.hawkbit.ui.filtermanagement.TargetFilterBeanQuery;
|
||||||
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
|
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
|
||||||
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
|
||||||
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
|
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
|
||||||
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
|
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
|
||||||
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
||||||
|
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.util.concurrent.ListenableFuture;
|
import org.springframework.util.concurrent.ListenableFuture;
|
||||||
@@ -68,7 +69,7 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 2939193468001472916L;
|
private static final long serialVersionUID = 2939193468001472916L;
|
||||||
|
|
||||||
private VaadinMessageSource i18n;
|
private final VaadinMessageSource i18n;
|
||||||
|
|
||||||
private transient EntityFactory entityFactory;
|
private transient EntityFactory entityFactory;
|
||||||
|
|
||||||
@@ -94,9 +95,7 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
|
|
||||||
private transient RolloutGroupsValidation groupsValidation;
|
private transient RolloutGroupsValidation groupsValidation;
|
||||||
|
|
||||||
private transient ListenableFuture<RolloutGroupsValidation> runningValidation;
|
private final AtomicInteger runningValidationsCounter;
|
||||||
|
|
||||||
private boolean validationRequested;
|
|
||||||
|
|
||||||
DefineGroupsLayout(VaadinMessageSource i18n, EntityFactory entityFactory, RolloutManagement rolloutManagement,
|
DefineGroupsLayout(VaadinMessageSource i18n, EntityFactory entityFactory, RolloutManagement rolloutManagement,
|
||||||
TargetFilterQueryManagement targetFilterQueryManagement) {
|
TargetFilterQueryManagement targetFilterQueryManagement) {
|
||||||
@@ -104,6 +103,7 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
this.entityFactory = entityFactory;
|
this.entityFactory = entityFactory;
|
||||||
this.rolloutManagement = rolloutManagement;
|
this.rolloutManagement = rolloutManagement;
|
||||||
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
||||||
|
runningValidationsCounter = new AtomicInteger(0);
|
||||||
|
|
||||||
groupRows = new ArrayList<>(10);
|
groupRows = new ArrayList<>(10);
|
||||||
setSizeUndefined();
|
setSizeUndefined();
|
||||||
@@ -152,22 +152,27 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Button createAddButton() {
|
private Button createAddButton() {
|
||||||
Button button = SPUIComponentProvider.getButton(UIComponentIdProvider.ROLLOUT_GROUP_ADD_ID,
|
final Button button = SPUIComponentProvider.getButton(UIComponentIdProvider.ROLLOUT_GROUP_ADD_ID,
|
||||||
i18n.getMessage("button.rollout.add.group"), "", "", true, FontAwesome.PLUS,
|
i18n.getMessage("button.rollout.add.group"), "", "", true, FontAwesome.PLUS,
|
||||||
SPUIButtonStyleNoBorderWithIcon.class);
|
SPUIButtonStyleNoBorderWithIcon.class);
|
||||||
button.setSizeUndefined();
|
button.setSizeUndefined();
|
||||||
button.addStyleName("default-color");
|
button.addStyleName("default-color");
|
||||||
button.setEnabled(true);
|
button.setEnabled(true);
|
||||||
button.setVisible(true);
|
button.setVisible(true);
|
||||||
button.addClickListener(event -> addGroupRow());
|
button.addClickListener(event -> addGroupRowAndValidate());
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupRow addGroupRow() {
|
private GroupRow addGroupRow() {
|
||||||
int rowIndex = addRow();
|
final int rowIndex = addRow();
|
||||||
GroupRow groupRow = new GroupRow();
|
final GroupRow groupRow = new GroupRow();
|
||||||
groupRow.addToGridRow(this, rowIndex);
|
groupRow.addToGridRow(this, rowIndex);
|
||||||
groupRows.add(groupRow);
|
groupRows.add(groupRow);
|
||||||
|
return groupRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GroupRow addGroupRowAndValidate() {
|
||||||
|
final GroupRow groupRow = addGroupRow();
|
||||||
updateValidation();
|
updateValidation();
|
||||||
return groupRow;
|
return groupRow;
|
||||||
}
|
}
|
||||||
@@ -208,8 +213,7 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
groupsCount = 0;
|
groupsCount = 0;
|
||||||
|
|
||||||
removeAllRows();
|
removeAllRows();
|
||||||
addGroupRow();
|
addGroupRowAndValidate();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -231,6 +235,8 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
groupRow.populateByGroup(group);
|
groupRow.populateByGroup(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateValidation();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -269,29 +275,37 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
|
|
||||||
private void validateRemainingTargets() {
|
private void validateRemainingTargets() {
|
||||||
resetRemainingTargetsError();
|
resetRemainingTargetsError();
|
||||||
if (targetFilter == null || (runningValidation != null && !runningValidation.isDone())) {
|
if (targetFilter == null) {
|
||||||
validationRequested = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
validationRequested = false;
|
if (runningValidationsCounter.incrementAndGet() == 1) {
|
||||||
|
final ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups = rolloutManagement
|
||||||
final UI ui = UI.getCurrent();
|
.validateTargetsInGroups(savedRolloutGroups, targetFilter, System.currentTimeMillis());
|
||||||
|
final UI ui = UI.getCurrent();
|
||||||
runningValidation = rolloutManagement.validateTargetsInGroups(savedRolloutGroups, targetFilter,
|
validateTargetsInGroups.addCallback(validation -> ui.access(() -> setGroupsValidation(validation)),
|
||||||
System.currentTimeMillis());
|
throwable -> ui.access(() -> setGroupsValidation(null)));
|
||||||
runningValidation.addCallback(validation -> ui.access(() -> this.setGroupsValidation(validation)),
|
return;
|
||||||
throwable -> ui.access(() -> this.setGroupsValidation(null)));
|
}
|
||||||
|
|
||||||
|
runningValidationsCounter.incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setGroupsValidation(RolloutGroupsValidation validation) {
|
/**
|
||||||
groupsValidation = validation;
|
* YOU SHOULD NOT CALL THIS METHOD MANUALLY. It's only for the callback.
|
||||||
|
* Only 1 runningValidation should be executed. If this runningValidation is
|
||||||
|
* done, then this method is called. Maybe then a new runningValidation is
|
||||||
|
* executed.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void setGroupsValidation(final RolloutGroupsValidation validation) {
|
||||||
|
|
||||||
if (validationRequested) {
|
final int runningValidation = runningValidationsCounter.getAndSet(0);
|
||||||
|
if (runningValidation > 1) {
|
||||||
validateRemainingTargets();
|
validateRemainingTargets();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
groupsValidation = validation;
|
||||||
|
|
||||||
final GroupRow lastRow = groupRows.get(groupRows.size() - 1);
|
final GroupRow lastRow = groupRows.get(groupRows.size() - 1);
|
||||||
if (groupsValidation != null && groupsValidation.isValid() && validationStatus != ValidationStatus.INVALID) {
|
if (groupsValidation != null && groupsValidation.isValid() && validationStatus != ValidationStatus.INVALID) {
|
||||||
@@ -364,23 +378,28 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
groupName = createTextField("textfield.name", UIComponentIdProvider.ROLLOUT_GROUP_LIST_GRID_ID);
|
groupName = createTextField("textfield.name", UIComponentIdProvider.ROLLOUT_GROUP_LIST_GRID_ID);
|
||||||
groupName.setValue(i18n.getMessage("textfield.rollout.group.default.name", groupsCount));
|
groupName.setValue(i18n.getMessage("textfield.rollout.group.default.name", groupsCount));
|
||||||
groupName.setStyleName("rollout-group-name");
|
groupName.setStyleName("rollout-group-name");
|
||||||
|
groupName.addValueChangeListener(event -> valueChanged());
|
||||||
|
|
||||||
targetFilterQueryCombo = createTargetFilterQueryCombo();
|
targetFilterQueryCombo = createTargetFilterQueryCombo();
|
||||||
populateTargetFilterQuery();
|
populateTargetFilterQuery();
|
||||||
|
targetFilterQueryCombo.addValueChangeListener(event -> valueChanged());
|
||||||
|
|
||||||
targetFilterQuery = createTargetFilterQuery();
|
targetFilterQuery = createTargetFilterQuery();
|
||||||
|
|
||||||
targetPercentage = createPercentageWithDecimalsField("textfield.target.percentage",
|
targetPercentage = createPercentageWithDecimalsField("textfield.target.percentage",
|
||||||
UIComponentIdProvider.ROLLOUT_GROUP_TARGET_PERC_ID);
|
UIComponentIdProvider.ROLLOUT_GROUP_TARGET_PERC_ID);
|
||||||
targetPercentage.setValue("100");
|
targetPercentage.setValue("100");
|
||||||
|
targetPercentage.addValueChangeListener(event -> valueChanged());
|
||||||
|
|
||||||
triggerThreshold = createPercentageField("prompt.tigger.threshold",
|
triggerThreshold = createPercentageField("prompt.tigger.threshold",
|
||||||
UIComponentIdProvider.ROLLOUT_TRIGGER_THRESOLD_ID);
|
UIComponentIdProvider.ROLLOUT_TRIGGER_THRESOLD_ID);
|
||||||
triggerThreshold.setValue(defaultTriggerThreshold);
|
triggerThreshold.setValue(defaultTriggerThreshold);
|
||||||
|
triggerThreshold.addValueChangeListener(event -> valueChanged());
|
||||||
|
|
||||||
errorThreshold = createPercentageField("prompt.error.threshold",
|
errorThreshold = createPercentageField("prompt.error.threshold",
|
||||||
UIComponentIdProvider.ROLLOUT_ERROR_THRESOLD_ID);
|
UIComponentIdProvider.ROLLOUT_ERROR_THRESOLD_ID);
|
||||||
errorThreshold.setValue(defaultErrorThreshold);
|
errorThreshold.setValue(defaultErrorThreshold);
|
||||||
|
errorThreshold.addValueChangeListener(event -> valueChanged());
|
||||||
|
|
||||||
optionsLayout = new HorizontalLayout();
|
optionsLayout = new HorizontalLayout();
|
||||||
optionsLayout.addComponent(createRemoveButton());
|
optionsLayout.addComponent(createRemoveButton());
|
||||||
@@ -394,7 +413,6 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
textField.setSizeUndefined();
|
textField.setSizeUndefined();
|
||||||
textField.addValidator(
|
textField.addValidator(
|
||||||
new StringLengthValidator(i18n.getMessage("message.rollout.group.name.invalid"), 1, 64, false));
|
new StringLengthValidator(i18n.getMessage("message.rollout.group.name.invalid"), 1, 64, false));
|
||||||
textField.addValueChangeListener(event -> valueChanged());
|
|
||||||
return textField;
|
return textField;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +423,6 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
textField.setNullRepresentation("");
|
textField.setNullRepresentation("");
|
||||||
textField.setConverter(new StringToIntegerConverter());
|
textField.setConverter(new StringToIntegerConverter());
|
||||||
textField.addValidator(this::validateMandatoryPercentage);
|
textField.addValidator(this::validateMandatoryPercentage);
|
||||||
textField.addValueChangeListener(event -> valueChanged());
|
|
||||||
return textField;
|
return textField;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,8 +459,7 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
|
|
||||||
private ComboBox createTargetFilterQueryCombo() {
|
private ComboBox createTargetFilterQueryCombo() {
|
||||||
return new ComboBoxBuilder().setId(UIComponentIdProvider.ROLLOUT_TARGET_FILTER_COMBO_ID)
|
return new ComboBoxBuilder().setId(UIComponentIdProvider.ROLLOUT_TARGET_FILTER_COMBO_ID)
|
||||||
.setPrompt(i18n.getMessage("prompt.target.filter")).setValueChangeListener(event -> valueChanged())
|
.setPrompt(i18n.getMessage("prompt.target.filter")).buildCombBox();
|
||||||
.buildCombBox();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextArea createTargetFilterQuery() {
|
private TextArea createTargetFilterQuery() {
|
||||||
@@ -484,8 +500,8 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Button createRemoveButton() {
|
private Button createRemoveButton() {
|
||||||
Button button = SPUIComponentProvider.getButton(UIComponentIdProvider.ROLLOUT_GROUP_REMOVE_ID, "", "", "",
|
final Button button = SPUIComponentProvider.getButton(UIComponentIdProvider.ROLLOUT_GROUP_REMOVE_ID, "", "",
|
||||||
true, FontAwesome.MINUS, SPUIButtonStyleNoBorderWithIcon.class);
|
"", true, FontAwesome.MINUS, SPUIButtonStyleNoBorderWithIcon.class);
|
||||||
button.setSizeUndefined();
|
button.setSizeUndefined();
|
||||||
button.addStyleName("default-color");
|
button.addStyleName("default-color");
|
||||||
button.setEnabled(true);
|
button.setEnabled(true);
|
||||||
@@ -495,7 +511,7 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onRemove() {
|
private void onRemove() {
|
||||||
int index = findRowIndexFor(groupName, 0);
|
final int index = findRowIndexFor(groupName, 0);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
removeRow(index);
|
removeRow(index);
|
||||||
}
|
}
|
||||||
@@ -505,7 +521,7 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
private int findRowIndexFor(final Component component, final int col) {
|
private int findRowIndexFor(final Component component, final int col) {
|
||||||
final int rows = getRows();
|
final int rows = getRows();
|
||||||
for (int i = 0; i < rows; i++) {
|
for (int i = 0; i < rows; i++) {
|
||||||
Component rowComponent = getComponent(col, i);
|
final Component rowComponent = getComponent(col, i);
|
||||||
if (component.equals(rowComponent)) {
|
if (component.equals(rowComponent)) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@@ -559,8 +575,8 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
.errorCondition(RolloutGroup.RolloutGroupErrorCondition.THRESHOLD, errorThreshold.getValue())
|
.errorCondition(RolloutGroup.RolloutGroupErrorCondition.THRESHOLD, errorThreshold.getValue())
|
||||||
.errorAction(RolloutGroup.RolloutGroupErrorAction.PAUSE, null);
|
.errorAction(RolloutGroup.RolloutGroupErrorAction.PAUSE, null);
|
||||||
}
|
}
|
||||||
String percentageString = targetPercentage.getValue().replace(",", ".");
|
final String percentageString = targetPercentage.getValue().replace(",", ".");
|
||||||
Float percentage = Float.parseFloat(percentageString);
|
final Float percentage = Float.parseFloat(percentageString);
|
||||||
|
|
||||||
return entityFactory.rolloutGroup().create().name(groupName.getValue()).description(groupName.getValue())
|
return entityFactory.rolloutGroup().create().name(groupName.getValue()).description(groupName.getValue())
|
||||||
.targetFilterQuery(getTargetFilterQuery()).targetPercentage(percentage)
|
.targetFilterQuery(getTargetFilterQuery()).targetPercentage(percentage)
|
||||||
@@ -574,7 +590,7 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
* the data source
|
* the data source
|
||||||
*/
|
*/
|
||||||
public void populateByGroup(final RolloutGroup group) {
|
public void populateByGroup(final RolloutGroup group) {
|
||||||
|
initialized = false;
|
||||||
groupName.setValue(group.getName());
|
groupName.setValue(group.getName());
|
||||||
targetFilterQuery.setValue(group.getTargetFilterQuery());
|
targetFilterQuery.setValue(group.getTargetFilterQuery());
|
||||||
populateTargetFilterQuery(group);
|
populateTargetFilterQuery(group);
|
||||||
@@ -584,6 +600,7 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
errorThreshold.setValue(group.getErrorConditionExp());
|
errorThreshold.setValue(group.getErrorConditionExp());
|
||||||
|
|
||||||
populated = true;
|
populated = true;
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,17 +612,15 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
&& triggerThreshold.isValid() && errorThreshold.isValid();
|
&& triggerThreshold.isValid() && errorThreshold.isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void markWithLastGroupError() {
|
||||||
* Displays an error for the row
|
targetPercentage
|
||||||
*/
|
.setComponentError(new UserError(i18n.getMessage("message.rollout.remaining.targets.error")));
|
||||||
public void markWithLastGroupError() {
|
|
||||||
targetPercentage.setComponentError(new UserError(i18n.getMessage("message.rollout.remaining.targets.error")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides an error of the row
|
* Hides an error of the row
|
||||||
*/
|
*/
|
||||||
public void hideLastGroupError() {
|
private void hideLastGroupError() {
|
||||||
targetPercentage.setComponentError(null);
|
targetPercentage.setComponentError(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user