Calculate target count asynchronously (#1263)

* first prototype

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io>

* moved asynchrinous count caclucation to abstract footer support

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io>

* added asynchronous count details calculation

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io>

* fixed typo

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io>
This commit is contained in:
Bondar Bogdan
2022-07-07 09:46:46 +02:00
committed by GitHub
parent f0a7c2d07d
commit 1ec5eb6ede
9 changed files with 202 additions and 98 deletions

View File

@@ -36,7 +36,6 @@ import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload;
import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload.EntityModifiedEventType;
import org.eclipse.hawkbit.ui.common.event.EventTopics;
import org.eclipse.hawkbit.ui.utils.SpringContextHolder;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -69,8 +68,6 @@ public abstract class AbstractFileTransferHandler implements Serializable {
private final VaadinMessageSource i18n;
protected final UINotification uiNotification;
private final transient Lock uploadLock;
protected static final RegexCharacterCollection ILLEGAL_FILENAME_CHARACTERS = new RegexCharacterCollection(
@@ -82,7 +79,6 @@ public abstract class AbstractFileTransferHandler implements Serializable {
this.i18n = i18n;
this.eventBus = SpringContextHolder.getInstance().getBean(EventBus.UIEventBus.class);
this.artifactUploadState = SpringContextHolder.getInstance().getBean(ArtifactUploadState.class);
this.uiNotification = SpringContextHolder.getInstance().getBean(UINotification.class);
this.uploadLock = uploadLock;
}

View File

@@ -8,15 +8,64 @@
*/
package org.eclipse.hawkbit.ui.common.layout;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import com.vaadin.server.Sizeable.Unit;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Layout;
import com.vaadin.ui.UI;
/**
* If footer support is enabled, the footer is placed below the component
*/
public abstract class AbstractFooterSupport {
private static final Logger LOG = LoggerFactory.getLogger(AbstractFooterSupport.class);
protected final VaadinMessageSource i18n;
private final UINotification notification;
protected final Label countLabel;
private final ExecutorService countExecutor;
private Future<?> currentCountCalculation;
private Future<?> currentCountDetailsCalculation;
protected AbstractFooterSupport(final VaadinMessageSource i18n, final UINotification notification) {
this.i18n = i18n;
this.notification = notification;
this.countLabel = new Label();
this.countExecutor = Executors.newSingleThreadExecutor();
init();
}
/**
* Init footer message label, can be overriden to adapt label styling.
*
*/
protected void init() {
countLabel.setId(UIComponentIdProvider.COUNT_LABEL);
countLabel.addStyleName(SPUIStyleDefinitions.SP_LABEL_MESSAGE_STYLE);
countLabel.addDetachListener(e -> {
abortCurrentCountCalculation();
abortCurrentDetailsCountCalculation();
});
}
/**
* Creates a sub-layout for the footer.
@@ -29,15 +78,75 @@ public abstract class AbstractFooterSupport {
footerLayout.setSpacing(false);
footerLayout.setWidth(100, Unit.PERCENTAGE);
footerLayout.addComponent(getFooterMessageLabel());
footerLayout.addComponent(countLabel);
return footerLayout;
}
/**
* Get the footer message label.
* Calculates count asynchronously and updated the count label.
*
* @return footer message
* @param countValueUpdater
* callback to update count value
* @param countUiUpdater
* callback to update count label in UI
*/
protected abstract Label getFooterMessageLabel();
protected void updateCountAsynchronously(final Runnable countValueUpdater, final Runnable countUiUpdater) {
abortCurrentCountCalculation();
countLabel.setCaption(i18n.getMessage("label.calculating"));
currentCountCalculation = submitAsynchronousCountUpdate(countValueUpdater, countUiUpdater);
}
private Future<?> submitAsynchronousCountUpdate(final Runnable countValueUpdater, final Runnable countUiUpdater) {
final UI ui = UI.getCurrent();
final SecurityContext securityContext = SecurityContextHolder.getContext();
return countExecutor.submit(() -> {
try {
LOG.trace("Started calculating count asynchronously");
SecurityContextHolder.setContext(securityContext);
countValueUpdater.run();
LOG.trace("Finished calculating count asynchronously, updating UI");
ui.access(countUiUpdater);
} catch (final Exception ex) {
LOG.error("Error occurred during asynchronous count calculation", ex);
ui.access(() -> notification
.displayValidationError(i18n.getMessage(UIMessageIdProvider.MESSAGE_ERROR_COUNT_FAILED)));
}
});
}
/**
* Calculates count details asynchronously and updated the details count
* label.
*
* @param countDetailsValueUpdater
* callback to update details count value
* @param countDetailsUiUpdater
* callback to update details count label in UI
*/
protected void updateCountDetailsAsynchronously(final Runnable countDetailsValueUpdater,
final Runnable countDetailsUiUpdater) {
abortCurrentDetailsCountCalculation();
countLabel.setValue(i18n.getMessage("label.calculating"));
currentCountDetailsCalculation = submitAsynchronousCountUpdate(countDetailsValueUpdater, countDetailsUiUpdater);
}
private void abortCurrentCountCalculation() {
if (currentCountCalculation != null && !currentCountCalculation.isCancelled()) {
currentCountCalculation.cancel(true);
currentCountCalculation = null;
}
}
private void abortCurrentDetailsCountCalculation() {
if (currentCountDetailsCalculation != null && !currentCountDetailsCalculation.isCancelled()) {
currentCountDetailsCalculation.cancel(true);
currentCountDetailsCalculation = null;
}
}
}

View File

@@ -8,57 +8,54 @@
*/
package org.eclipse.hawkbit.ui.filtermanagement;
import java.util.function.IntSupplier;
import org.eclipse.hawkbit.ui.common.layout.AbstractFooterSupport;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import com.vaadin.ui.Label;
/**
* Count message label which display current filter details and details on
* pinning.
* Count message label which display current total filtered targets count.
*/
public class TargetFilterCountMessageLabel extends AbstractFooterSupport {
private final VaadinMessageSource i18n;
private final Label targetCountLabel;
private int totalFilteredTargetsCount;
/**
* Constructor for TargetFilterCountMessageLabel
*
* @param i18n
* VaadinMessageSource
* VaadinMessageSource
*/
public TargetFilterCountMessageLabel(final VaadinMessageSource i18n) {
this.i18n = i18n;
this.targetCountLabel = new Label();
init();
}
private void init() {
targetCountLabel.setId(UIComponentIdProvider.COUNT_LABEL);
targetCountLabel.addStyleName(SPUIStyleDefinitions.SP_LABEL_MESSAGE_STYLE);
updateTotalFilteredTargetsCount(0);
public TargetFilterCountMessageLabel(final VaadinMessageSource i18n, final UINotification notification) {
super(i18n, notification);
}
@Override
protected Label getFooterMessageLabel() {
return targetCountLabel;
protected void init() {
super.init();
totalFilteredTargetsCount = 0;
updateTotalFilteredTargetsCountLabel();
}
/**
* Update the total count of target filtered
* Update the total count of filtered targets asynchronously.
*
* @param fetchTotalFilteredTargetsCount
* total filtered targets count provider
*
* @param count
* Total target filtered count
*/
public void updateTotalFilteredTargetsCount(final long count) {
public void updateTotalFilteredTargetsCount(final IntSupplier fetchTotalFilteredTargetsCount) {
updateCountAsynchronously(() -> totalFilteredTargetsCount = fetchTotalFilteredTargetsCount.getAsInt(),
this::updateTotalFilteredTargetsCountLabel);
}
private void updateTotalFilteredTargetsCountLabel() {
final StringBuilder targetMessage = new StringBuilder(i18n.getMessage("label.target.filtered.total"));
targetMessage.append(": ");
targetMessage.append(count);
targetCountLabel.setCaption(targetMessage.toString());
targetMessage.append(totalFilteredTargetsCount);
countLabel.setCaption(targetMessage.toString());
}
}

View File

@@ -62,7 +62,8 @@ public class TargetFilterDetailsLayout extends AbstractGridComponentLayout {
uiProperties, rsqlValidationOracle, uiState);
this.targetFilterTargetGrid = new TargetFilterTargetGrid(uiDependencies, targetFilterStateDataSupplier,
uiState);
this.targetFilterCountMessageLabel = new TargetFilterCountMessageLabel(uiDependencies.getI18n());
this.targetFilterCountMessageLabel = new TargetFilterCountMessageLabel(uiDependencies.getI18n(),
uiDependencies.getUiNotification());
initGridDataUpdatedListener();
@@ -81,7 +82,7 @@ public class TargetFilterDetailsLayout extends AbstractGridComponentLayout {
private void initGridDataUpdatedListener() {
targetFilterTargetGrid.addDataChangedListener(event -> targetFilterCountMessageLabel
.updateTotalFilteredTargetsCount(targetFilterTargetGrid.getDataSize()));
.updateTotalFilteredTargetsCount(targetFilterTargetGrid::getDataSize));
}
@Override

View File

@@ -18,82 +18,75 @@ import org.eclipse.hawkbit.ui.common.grid.support.FilterSupport;
import org.eclipse.hawkbit.ui.common.layout.AbstractFooterSupport;
import org.eclipse.hawkbit.ui.common.layout.CountAwareComponent;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import com.vaadin.data.provider.Query;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.ui.Label;
/**
* Count message label which display current filter details and details on
* pinning.
*/
public class TargetCountMessageLabel extends AbstractFooterSupport implements CountAwareComponent {
private final VaadinMessageSource i18n;
private final TargetManagement targetManagement;
private final FilterSupport<ProxyTarget, TargetManagementFilterParams> gridFilterSupport;
private final Label targetCountLabel;
private int totalCount;
private int filteredCount;
private long targetsWithAssignedDsCount;
private long targetsWithInstalledDsCount;
/**
* Constructor
*
* @param i18n
* I18N
*/
public TargetCountMessageLabel(final VaadinMessageSource i18n, final TargetManagement targetManagement,
public TargetCountMessageLabel(final VaadinMessageSource i18n, final UINotification notification,
final TargetManagement targetManagement,
final FilterSupport<ProxyTarget, TargetManagementFilterParams> gridFilterSupport) {
this.i18n = i18n;
super(i18n, notification);
this.targetManagement = targetManagement;
this.gridFilterSupport = gridFilterSupport;
this.targetCountLabel = new Label();
init();
}
private void init() {
targetCountLabel.setId(UIComponentIdProvider.COUNT_LABEL);
targetCountLabel.addStyleName(SPUIStyleDefinitions.SP_LABEL_MESSAGE_STYLE);
targetCountLabel.setContentMode(ContentMode.HTML);
targetCountLabel.setIcon(null);
targetCountLabel.setDescription(null);
}
@Override
protected void init() {
super.init();
public void updateTotalCount() {
totalCount = fetchTotalCount();
updateCountLabel();
}
private int fetchTotalCount() {
return gridFilterSupport.getOriginalDataProvider().size(new Query<>());
}
public void updateFilteredCount() {
if (gridFilterSupport.getFilter().isAnyFilterSelected()) {
filteredCount = fetchFilteredCount();
}
updateCountLabel();
}
private int fetchFilteredCount() {
return gridFilterSupport.getFilterDataProvider().size(new Query<>());
countLabel.setContentMode(ContentMode.HTML);
countLabel.setIcon(null);
countLabel.setDescription(null);
}
public void updateTotalAndFilteredCount() {
totalCount = fetchTotalCount();
if (gridFilterSupport.getFilter().isAnyFilterSelected()) {
filteredCount = fetchFilteredCount();
}
updateCountAsynchronously(this::fetchTotalAndFilteredCount, this::updateCountLabel);
}
updateCountLabel();
private void fetchTotalAndFilteredCount() {
fetchTotalCount();
fetchFilteredCount();
}
private void fetchTotalCount() {
totalCount = gridFilterSupport.getOriginalDataProvider().size(new Query<>());
}
private void fetchFilteredCount() {
if (gridFilterSupport.getFilter().isAnyFilterSelected()) {
filteredCount = gridFilterSupport.getFilterDataProvider().size(new Query<>());
} else {
filteredCount = 0;
}
}
public void updateFilteredCount() {
updateCountAsynchronously(this::fetchFilteredCount, this::updateCountLabel);
}
private void updateCountLabel() {
@@ -104,7 +97,7 @@ public class TargetCountMessageLabel extends AbstractFooterSupport implements Co
appendFilteredTargetsMessage(countMessageBuilder, targetFilterParams);
}
targetCountLabel.setCaption(countMessageBuilder.toString());
countLabel.setCaption(countMessageBuilder.toString());
}
private StringBuilder getTotalTargetsMessage() {
@@ -134,7 +127,8 @@ public class TargetCountMessageLabel extends AbstractFooterSupport implements Co
appendSearchMsg(filterMessageBuilder, targetFilterParams.getSearchText());
appendDsMsg(filterMessageBuilder, targetFilterParams.getDistributionId());
appendCustomFilterQueryMsg(filterMessageBuilder, targetFilterParams.getTargetFilterQueryId());
appendTargetTypeFilterMsg(filterMessageBuilder, targetFilterParams.isNoTargetTypeClicked(), targetFilterParams.getTargetTypeId());
appendTargetTypeFilterMsg(filterMessageBuilder, targetFilterParams.isNoTargetTypeClicked(),
targetFilterParams.getTargetTypeId());
String filterMessage = filterMessageBuilder.toString().trim();
if (filterMessage.endsWith(",")) {
@@ -187,7 +181,8 @@ public class TargetCountMessageLabel extends AbstractFooterSupport implements Co
}
}
private void appendTargetTypeFilterMsg(final StringBuilder filterMessageBuilder, boolean noTargetTypeClicked, final Long targetTypeId) {
private void appendTargetTypeFilterMsg(final StringBuilder filterMessageBuilder, final boolean noTargetTypeClicked,
final Long targetTypeId) {
if (targetTypeId != null || noTargetTypeClicked) {
appendFilterMsg(filterMessageBuilder, i18n.getMessage("label.filter.target.type"));
}
@@ -200,27 +195,28 @@ public class TargetCountMessageLabel extends AbstractFooterSupport implements Co
public void updatePinningDetails() {
final Long pinnedDsId = gridFilterSupport.getFilter().getPinnedDistId();
if (pinnedDsId == null) {
targetCountLabel.setValue("");
countLabel.setValue("");
return;
}
final Long targetsWithAssigedDsCount = targetManagement.countByAssignedDistributionSet(pinnedDsId);
final Long targetsWithInstalledDsCount = targetManagement.countByInstalledDistributionSet(pinnedDsId);
updateCountDetailsAsynchronously(() -> fetchPinningCounts(pinnedDsId), this::updatePinningCountLabel);
}
private void fetchPinningCounts(final Long pinnedDsId) {
targetsWithAssignedDsCount = targetManagement.countByAssignedDistributionSet(pinnedDsId);
targetsWithInstalledDsCount = targetManagement.countByInstalledDistributionSet(pinnedDsId);
}
private void updatePinningCountLabel() {
final StringBuilder message = new StringBuilder(i18n.getMessage("label.target.count"));
message.append(" : ");
message.append("<span class=\"assigned-count-message\">");
message.append(i18n.getMessage("label.assigned.count", targetsWithAssigedDsCount));
message.append(i18n.getMessage("label.assigned.count", targetsWithAssignedDsCount));
message.append("</span>, <span class=\"installed-count-message\"> ");
message.append(i18n.getMessage("label.installed.count", targetsWithInstalledDsCount));
message.append("</span>");
targetCountLabel.setValue(message.toString());
}
@Override
protected Label getFooterMessageLabel() {
return targetCountLabel;
countLabel.setValue(message.toString());
}
@Override

View File

@@ -141,8 +141,8 @@ public class TargetGridLayout extends AbstractGridComponentLayout {
this.targetDetails = new TargetDetails(uiDependencies, targetTagManagement, targetManagement,
deploymentManagement, targetMetaDataWindowBuilder);
this.countMessageLabel = new TargetCountMessageLabel(uiDependencies.getI18n(), targetManagement,
targetGrid.getFilterSupport());
this.countMessageLabel = new TargetCountMessageLabel(uiDependencies.getI18n(),
uiDependencies.getUiNotification(), targetManagement, targetGrid.getFilterSupport());
final EventLayoutViewAware layoutViewAware = new EventLayoutViewAware(EventLayout.TARGET_LIST,
EventView.DEPLOYMENT);

View File

@@ -54,7 +54,8 @@ public class RolloutGroupTargetGridLayout extends AbstractGridComponentLayout {
this.rolloutGroupTargetsListHeader = new RolloutGroupTargetGridHeader(uiDependencies, rolloutManagementUIState);
this.rolloutGroupTargetsListGrid = new RolloutGroupTargetGrid(uiDependencies, rolloutGroupManagement,
rolloutManagementUIState);
this.rolloutGroupTargetCountMessageLabel = new TargetFilterCountMessageLabel(uiDependencies.getI18n());
this.rolloutGroupTargetCountMessageLabel = new TargetFilterCountMessageLabel(uiDependencies.getI18n(),
uiDependencies.getUiNotification());
initGridDataUpdatedListener();
@@ -73,7 +74,7 @@ public class RolloutGroupTargetGridLayout extends AbstractGridComponentLayout {
private void initGridDataUpdatedListener() {
rolloutGroupTargetsListGrid.addDataChangedListener(event -> rolloutGroupTargetCountMessageLabel
.updateTotalFilteredTargetsCount(rolloutGroupTargetsListGrid.getDataSize()));
.updateTotalFilteredTargetsCount(rolloutGroupTargetsListGrid::getDataSize));
}
private List<MasterEntityAwareComponent<ProxyRolloutGroup>> getMasterEntityAwareComponents() {

View File

@@ -175,6 +175,8 @@ public final class UIMessageIdProvider {
public static final String MESSAGE_ERROR_DECRYPTION_FAILED = "message.decryption.failed";
public static final String MESSAGE_ERROR_COUNT_FAILED = "message.count.failed";
public static final String CRON_VALIDATION_ERROR = "message.maintenancewindow.schedule.validation.error";
public static final String TOOLTIP_OVERDUE = "tooltip.overdue";

View File

@@ -269,6 +269,7 @@ label.invalidate.ds.cancelation.type = Type of cancelation:
label.cancel.action.none = None
label.cancel.action.force = Forced
label.cancel.action.soft = Soft
label.calculating = Calculating...
# TextFields prefix with - textfield
textfield.name = Name
@@ -573,6 +574,7 @@ message.encryption.unsupported = Artifact encryption not supported
message.encryption.secrets.failed = Artifact encryption secrets generation failed
message.encryption.failed = Artifact encryption failed
message.decryption.failed = Artifact decryption failed
message.count.failed = There was an error during count calculation, please contact administrator
artifact.upload.popup.caption = Upload status
artifact.upload.status.caption = Status