diff --git a/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java b/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java index e55cb02d2..68d3b2fd9 100644 --- a/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java +++ b/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.app; import org.eclipse.hawkbit.ui.HawkbitUI; +import org.eclipse.hawkbit.ui.UIEventProvider; import org.eclipse.hawkbit.ui.push.DelayedEventBusPushStrategy; import org.springframework.beans.factory.annotation.Autowired; @@ -33,7 +34,8 @@ public class MyUI extends HawkbitUI { private static final long serialVersionUID = 1L; @Autowired - public MyUI(final EventBus systemEventBus, final org.vaadin.spring.events.EventBus.SessionEventBus eventBus) { - super(new DelayedEventBusPushStrategy(eventBus, systemEventBus)); + public MyUI(final EventBus systemEventBus, final org.vaadin.spring.events.EventBus.SessionEventBus eventBus, + final UIEventProvider provider) { + super(new DelayedEventBusPushStrategy(eventBus, systemEventBus, provider)); } } diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/ui/UIAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/ui/UIAutoConfiguration.java index ff73eff7b..be66904d5 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/ui/UIAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/ui/UIAutoConfiguration.java @@ -9,6 +9,9 @@ package org.eclipse.hawkbit.autoconfigure.ui; import org.eclipse.hawkbit.DistributedResourceBundleMessageSource; +import org.eclipse.hawkbit.ui.HawkbitEventProvider; +import org.eclipse.hawkbit.ui.UIEventProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.vaadin.spring.annotation.EnableVaadinExtensions; @@ -17,9 +20,6 @@ import org.vaadin.spring.security.annotation.EnableVaadinSecurity; /** * The hawkbit-ui autoconfiguration. - * - * - * */ @Configuration @EnableVaadinSecurity @@ -37,4 +37,15 @@ public class UIAutoConfiguration { return new DistributedResourceBundleMessageSource(); } + /** + * A event provider bean which hold the supported events for the UI. + * + * @return the provider bean + */ + @Bean + @ConditionalOnMissingBean + public UIEventProvider eventProvider() { + return new HawkbitEventProvider(); + } + } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/HawkbitEventProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/HawkbitEventProvider.java new file mode 100644 index 000000000..12bcaacbc --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/HawkbitEventProvider.java @@ -0,0 +1,56 @@ +/** + * 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.ui; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.hawkbit.eventbus.event.DistributionSetTagCreatedBulkEvent; +import org.eclipse.hawkbit.eventbus.event.DistributionSetTagDeletedEvent; +import org.eclipse.hawkbit.eventbus.event.DistributionSetTagUpdateEvent; +import org.eclipse.hawkbit.eventbus.event.Event; +import org.eclipse.hawkbit.eventbus.event.RolloutChangeEvent; +import org.eclipse.hawkbit.eventbus.event.RolloutGroupChangeEvent; +import org.eclipse.hawkbit.eventbus.event.TargetCreatedEvent; +import org.eclipse.hawkbit.eventbus.event.TargetDeletedEvent; +import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent; +import org.eclipse.hawkbit.eventbus.event.TargetTagCreatedBulkEvent; + +/** + * The default hawkbit event provider. + */ +public class HawkbitEventProvider implements UIEventProvider { + + private static final Set> SINGLE_EVENTS = new HashSet<>(6); + private static final Set> BULK_EVENTS = new HashSet<>(3); + + static { + SINGLE_EVENTS.add(TargetTagCreatedBulkEvent.class); + SINGLE_EVENTS.add(DistributionSetTagCreatedBulkEvent.class); + SINGLE_EVENTS.add(DistributionSetTagDeletedEvent.class); + SINGLE_EVENTS.add(DistributionSetTagUpdateEvent.class); + SINGLE_EVENTS.add(RolloutGroupChangeEvent.class); + SINGLE_EVENTS.add(RolloutChangeEvent.class); + + BULK_EVENTS.add(TargetCreatedEvent.class); + BULK_EVENTS.add(TargetInfoUpdateEvent.class); + BULK_EVENTS.add(TargetDeletedEvent.class); + } + + @Override + public Set> getSingleEvents() { + return SINGLE_EVENTS; + } + + @Override + public Set> getBulkEvents() { + return BULK_EVENTS; + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UIEventProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UIEventProvider.java new file mode 100644 index 000000000..5663ce897 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UIEventProvider.java @@ -0,0 +1,60 @@ +/** + * 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.ui; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.hawkbit.eventbus.event.Event; + +/** + * The UI event provider hold all supported repository events which will + * delegated to the UI. A event type can delegated as single event or bulk + * event. Bulk event means, that all events from one type is collected by the + * provider. The delegater and delegated as a list of this events. + */ +public interface UIEventProvider { + + /** + * Return all supported repository single event types. All events which this + * type are delegated to the UI as single event. + * + * @return list of provided event types. Should not be null + */ + default Set> getSingleEvents() { + return Collections.emptySet(); + } + + /** + * Return all supported repository bulk event types. All events which this + * type are delegated to the UI as a list. This list contains all collected + * events from one type. + * + * @return list of provided bulk event types. Should not be null + */ + default Set> getBulkEvents() { + return Collections.emptySet(); + } + + /** + * Return all filtered bulk event types by the given events. The default + * maps the events by class. + * + * @param allEvents + * the events + * @return list of provided bulk event types which are filtered. Should not + * be null + */ + default Set> getFilteredBulkEventsType(final List allEvents) { + return allEvents.stream().map(Event::getClass).filter(getBulkEvents()::contains).collect(Collectors.toSet()); + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java index 1ed0a400e..4b57f48c6 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java @@ -104,12 +104,11 @@ import com.vaadin.ui.themes.ValoTheme; @ViewScope public class TargetTable extends AbstractTable implements Handler { + private static final Logger LOG = LoggerFactory.getLogger(TargetTable.class); private static final String TARGET_PINNED = "targetPinned"; private static final long serialVersionUID = -2300392868806614568L; - private static final Logger LOG = LoggerFactory.getLogger(TargetTable.class); - private static final int PROPERTY_DEPT = 3; private static final String ITEMID = "itemId"; private static final String ACTION_NOT_ALLOWED_MSG = "message.action.not.allowed"; @@ -140,8 +139,6 @@ public class TargetTable extends AbstractTable implements Handler { private Boolean isTargetPinned = Boolean.FALSE; private ShortcutAction actionSelectAll; private ShortcutAction actionUnSelectAll; - - @Override @PostConstruct @@ -328,38 +325,20 @@ public class TargetTable extends AbstractTable implements Handler { (source, itemId, columnId) -> getTagetPollTime(itemId)); } - /* - * (non-Javadoc) - * - * @see org.eclipse.hawkbit.server.ui.common.table.AbstractTable# - * isFirstRowSelectedOnLoad () - */ @Override protected boolean isFirstRowSelectedOnLoad() { return !managementUIState.getSelectedTargetIdName().isPresent() || managementUIState.getSelectedTargetIdName().get().isEmpty(); } - /* - * (non-Javadoc) - * - * @see hawkbit.server.ui.common.table.AbstractTable#getItemIdToSelect() - */ @Override protected Object getItemIdToSelect() { if (managementUIState.getSelectedTargetIdName().isPresent()) { - setCurrentPageFirstItemId(managementUIState.getLastSelectedTargetIdName()); return managementUIState.getSelectedTargetIdName().get(); } return null; } - /* - * (non-Javadoc) - * - * @see - * org.eclipse.hawkbit.server.ui.common.table.AbstractTable#onValueChange() - */ @Override protected void onValueChange() { eventBus.publish(this, DragEvent.HIDE_DROP_HINT); @@ -379,23 +358,11 @@ public class TargetTable extends AbstractTable implements Handler { } } - /* - * (non-Javadoc) - * - * @see - * org.eclipse.hawkbit.server.ui.common.table.AbstractTable#isMaximized() - */ @Override protected boolean isMaximized() { return managementUIState.isTargetTableMaximized(); } - /* - * (non-Javadoc) - * - * @see hawkbit.server.ui.common.table.AbstractTable#getTableVisibleColumns - * () - */ @Override protected List getTableVisibleColumns() { final List columnList = new ArrayList<>(); @@ -452,13 +419,29 @@ public class TargetTable extends AbstractTable implements Handler { } else { shouldRefreshTargets = true; } - unselect(targetIdName); } + if (shouldRefreshTargets) { refreshOnDelete(); } else { targetContainer.commit(); - selectRow(); + } + reSelectItemsAfterDeletionEvent(); + } + + private void reSelectItemsAfterDeletionEvent() { + Set values = new HashSet<>(); + if (isMultiSelect()) { + values = new HashSet<>((Set) getValue()); + } else { + values.add(getValue()); + } + unSelectAll(); + + for (final Object value : values) { + if (getVisibleItemIds().contains(value)) { + select(value); + } } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java index 56ffdd95b..023979ead 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java @@ -19,17 +19,9 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import org.eclipse.hawkbit.eventbus.event.DistributionSetTagCreatedBulkEvent; -import org.eclipse.hawkbit.eventbus.event.DistributionSetTagDeletedEvent; import org.eclipse.hawkbit.eventbus.event.EntityEvent; -import org.eclipse.hawkbit.eventbus.event.RolloutChangeEvent; -import org.eclipse.hawkbit.eventbus.event.RolloutGroupChangeEvent; -import org.eclipse.hawkbit.eventbus.event.TargetCreatedEvent; -import org.eclipse.hawkbit.eventbus.event.TargetDeletedEvent; -import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent; -import org.eclipse.hawkbit.eventbus.event.TargetTagCreatedBulkEvent; -import org.eclipse.hawkbit.eventbus.event.TargetTagDeletedEvent; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; +import org.eclipse.hawkbit.ui.UIEventProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.context.SecurityContext; @@ -38,7 +30,6 @@ import org.springframework.security.web.context.HttpSessionSecurityContextReposi import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.EventBus.SessionEventBus; -import com.google.common.collect.Sets; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.Subscribe; import com.vaadin.server.VaadinSession; @@ -51,15 +42,15 @@ import com.vaadin.ui.UI; * {@link com.google.common.eventbus.EventBus} and store them first in an queue * where they will dispatched every 2 seconds to the {@link EventBus} in a * Vaadin access thread {@link UI#access(Runnable)}. - * + * * This strategy avoids blocking UIs when too many events are fired and * dispatched to the UI thread. The UI will freeze in the time. To avoid that * all events are collected first and same events are merged to a list of events * before they dispatched to the UI thread. - * + * * The strategy also verifies the current tenant in the session with the tenant * in the event and only forwards event from the right tenant to the UI. - * + * */ public class DelayedEventBusPushStrategy implements EventPushStrategy { @@ -73,16 +64,11 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { private ScheduledFuture jobHandle; - /** - * only events defined in the set are dispatched to the session event bus. - */ - private static final Set> UI_EVENTS = Sets.newHashSet(TargetInfoUpdateEvent.class, - TargetCreatedEvent.class, TargetDeletedEvent.class, RolloutChangeEvent.class, RolloutGroupChangeEvent.class, - TargetTagCreatedBulkEvent.class, DistributionSetTagCreatedBulkEvent.class,TargetTagDeletedEvent.class,DistributionSetTagDeletedEvent.class); + private final UIEventProvider eventProvider; /** * Constructor. - * + * * @param eventBus * the session event bus to where the events should be dispatched * @param systemEventBus @@ -90,9 +76,10 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { * back-end */ public DelayedEventBusPushStrategy(final SessionEventBus eventBus, - final com.google.common.eventbus.EventBus systemEventBus) { + final com.google.common.eventbus.EventBus systemEventBus, final UIEventProvider eventProvider) { this.eventBus = eventBus; this.systemEventBus = systemEventBus; + this.eventProvider = eventProvider; } /** @@ -107,12 +94,22 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { @AllowConcurrentEvents public void dispatch(final org.eclipse.hawkbit.eventbus.event.Event event) { // to dispatch too many events which are not interested on the UI - if (UI_EVENTS.contains(event.getClass()) && !queue.offer(event)) { + if (!isEventProvided(event)) { + LOG.trace("Event is not supported in the UI!!! Dropped event is {}", event); + return; + } + + if (!queue.offer(event)) { LOG.warn("Deque limit is reached, cannot add more events!!! Dropped event is {}", event); return; } } + private boolean isEventProvided(final org.eclipse.hawkbit.eventbus.event.Event event) { + return eventProvider.getSingleEvents().contains(event.getClass()) + || eventProvider.getBulkEvents().contains(event.getClass()); + } + @Override public void init(final UI vaadinUI) { LOG.debug("Initialize delayed event push strategy"); @@ -133,7 +130,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { /** * Checks if the tenant within the event is equal with the current tenant in * the context. - * + * * @param userContext * the security context of the current session * @param event @@ -208,37 +205,43 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { final SecurityContext oldContext = SecurityContextHolder.getContext(); try { SecurityContextHolder.setContext(userContext); + vaadinUI.access(() -> { if (vaadinSession.getState() != State.OPEN) { return; } - fowardEvents(events, userContext); - - // send a list of events, because ui performance issues - publishEventAsList(events, userContext, TargetInfoUpdateEvent.class); - publishEventAsList(events, userContext, TargetCreatedEvent.class); - publishEventAsList(events, userContext, TargetDeletedEvent.class); + fowardSingleEvents(events, userContext); + fowardBulkEvents(events, userContext); }); } finally { SecurityContextHolder.setContext(oldContext); } } - private void publishEventAsList(final List events, - final SecurityContext userContext, final Class eventType) { - final List bulkEvents = events.stream() - .filter(event -> DelayedEventBusPushStrategy.this.eventSecurityCheck(userContext, event) - && eventType.isInstance(event)) - .collect(Collectors.toList()); - if (bulkEvents.isEmpty()) { - return; - } - eventBus.publish(vaadinUI, bulkEvents); + private void fowardBulkEvents(final List events, + final SecurityContext userContext) { + final Set> filterBulkEvenTypes = eventProvider.getFilteredBulkEventsType(events); + publishBulkEvent(events, userContext, filterBulkEvenTypes); } - private void fowardEvents(final List events, + private void publishBulkEvent(final List events, + final SecurityContext userContext, final Set> filterBulkEvenTypes) { + for (final Class bulkType : filterBulkEvenTypes) { + final List listBulkEvents = events.stream() + .filter(event -> DelayedEventBusPushStrategy.this.eventSecurityCheck(userContext, event) + && bulkType.isInstance(event)) + .collect(Collectors.toList()); + if (!listBulkEvents.isEmpty()) { + eventBus.publish(vaadinUI, listBulkEvents); + } + } + } + + private void fowardSingleEvents(final List events, final SecurityContext userContext) { - events.stream().filter(event -> DelayedEventBusPushStrategy.this.eventSecurityCheck(userContext, event)) + events.stream() + .filter(event -> DelayedEventBusPushStrategy.this.eventSecurityCheck(userContext, event) + && eventProvider.getSingleEvents().contains(event.getClass())) .forEach(event -> eventBus.publish(vaadinUI, event)); } }