/*********************************************************************************** * * Copyright (c) 2014 Kamil Baczkowicz * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Kamil Baczkowicz - initial API and implementation and/or initial documentation * */ package pl.baczkowicz.mqttspy.ui.connections; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import javafx.fxml.FXMLLoader; import javafx.scene.control.Label; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.control.Tooltip; import javafx.scene.layout.AnchorPane; import javafx.scene.paint.Color; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.baczkowicz.mqttspy.configuration.ConfigurationManager; import pl.baczkowicz.mqttspy.configuration.generated.TabbedSubscriptionDetails; import pl.baczkowicz.mqttspy.connectivity.MqttAsyncConnection; import pl.baczkowicz.mqttspy.connectivity.MqttConnectionStatus; import pl.baczkowicz.mqttspy.connectivity.MqttSubscription; import pl.baczkowicz.mqttspy.messages.FormattedMqttMessage; import pl.baczkowicz.mqttspy.scripts.MqttScriptManager; import pl.baczkowicz.mqttspy.ui.ConnectionController; import pl.baczkowicz.mqttspy.ui.SubscriptionController; import pl.baczkowicz.mqttspy.ui.ViewManager; import pl.baczkowicz.mqttspy.ui.events.SubscriptionStatusChangeEvent; import pl.baczkowicz.mqttspy.ui.utils.ContextMenuUtils; import pl.baczkowicz.mqttspy.ui.utils.StylingUtils; import pl.baczkowicz.spy.eventbus.IKBus; import pl.baczkowicz.spy.formatting.FormattingManager; import pl.baczkowicz.spy.ui.configuration.UiProperties; import pl.baczkowicz.spy.ui.events.queuable.EventQueueManager; import pl.baczkowicz.spy.ui.panes.PaneVisibilityStatus; import pl.baczkowicz.spy.ui.panes.SpyPerspective; import pl.baczkowicz.spy.ui.panes.TabStatus; import pl.baczkowicz.spy.ui.storage.ManagedMessageStoreWithFiltering; import pl.baczkowicz.spy.ui.utils.FxmlUtils; import pl.baczkowicz.spy.ui.utils.TabUtils; /** * Class for managing subscription tabs. */ public class SubscriptionManager { /** Title for the 'all subscriptions' tab. */ public static String ALL_SUBSCRIPTIONS_TAB_TITLE = "All"; /** Index for the 'all subscriptions' tab. */ public static int ALL_SUBSCRIPTIONS_TAB_INDEX = 0; /** Diagnostic logger. */ private final static Logger logger = LoggerFactory.getLogger(SubscriptionManager.class); /** Subscription controllers (subscription topic to controller mapping). */ private final Map<String, SubscriptionController> subscriptionControllers = new LinkedHashMap<>(); /** UI event queue to be used. */ private final EventQueueManager<FormattedMqttMessage> uiEventQueue; /** Configuration manager. */ private ConfigurationManager configurationManager; private ViewManager viewManager; private IKBus eventBus; /** * Creates a SubscriptionManager with the given parameters. * * @param eventManager The global event manager * @param eventBus The global event bus * @param configurationManager The configuration manager * @param viewManager * @param uiEventQueue The UI event queue to be used */ public SubscriptionManager(final IKBus eventBus, final ConfigurationManager configurationManager, final ViewManager viewManager, final EventQueueManager<FormattedMqttMessage> uiEventQueue) { this.eventBus = eventBus; this.configurationManager = configurationManager; this.viewManager = viewManager; this.uiEventQueue = uiEventQueue; } /** * Creates a subscription and a tab for it. * * @param color The color to use for this subscription * @param subscribe Whether to subscribe straight after creating it (true) or leave it unsubscribed (false) * @param subscriptionDetails Subscription details * @param connection The connection for which to create this subscription * @param connectionController The connection controller * @param parent The parent UI node * @param formattingManager * @param scene */ public void createSubscription(final Color color, final boolean subscribe, final TabbedSubscriptionDetails subscriptionDetails, final MqttAsyncConnection connection, final ConnectionController connectionController, final Object parent) { logger.info("Creating subscription for " + subscriptionDetails.getTopic()); final MqttSubscription subscription = new MqttSubscription(subscriptionDetails.getTopic(), subscriptionDetails.getQos(), color, connection.getProperties().getConfiguredProperties().getMinMessagesStoredPerTopic(), connection.getPreferredStoreSize(), uiEventQueue, eventBus, connection.getStore().getFormattingManager(), UiProperties.getSummaryMaxPayloadLength(configurationManager.getUiPropertyFile())); subscription.setConnection(connection); subscription.setDetails(subscriptionDetails); // Add a new tab final SubscriptionController subscriptionController = createSubscriptionTab( false, subscription.getStore(), subscription, connection, connectionController); subscriptionController.getTab().setContextMenu(ContextMenuUtils.createSubscriptionTabContextMenu( connection, subscription, eventBus, this, configurationManager, subscriptionController)); subscriptionController.setConnectionController(connectionController); subscriptionController.setFormatting(configurationManager.getConfiguration().getFormatting()); subscriptionController.setTabStatus(new TabStatus()); subscriptionController.getTabStatus().setVisibility(PaneVisibilityStatus.NOT_VISIBLE); subscriptionController.init(); subscriptionController.onSubscriptionStatusChanged(new SubscriptionStatusChangeEvent(subscription)); subscription.setSubscriptionController(subscriptionController); final SpyPerspective perspective = viewManager.getPerspective(); subscriptionController.setViewVisibility(ViewManager.getDetailedViewStatus(perspective), ViewManager.getBasicViewStatus(perspective)); subscriptionController.getTabStatus().setVisibility(PaneVisibilityStatus.ATTACHED); subscriptionController.getTabStatus().setParent(connectionController.getSubscriptionTabs()); final TabPane subscriptionTabs = connectionController.getSubscriptionTabs(); subscriptionTabs.getTabs().add(subscriptionController.getTab()); subscriptionTabs.getTabs().get(ALL_SUBSCRIPTIONS_TAB_INDEX).setDisable(false); if (subscribe) { logger.debug("Trying to subscribe {}", subscription.getTopic()); connection.subscribe(subscription); } else { connection.addSubscription(subscription); subscription.setActive(false); } } /** * Creates a subscription tab & controller with the given parameters. * * @param allTab True if this is the 'all' tab * @param observableMessageStore The message store to use * @param subscription The subscription object * @param connection Connection associated with this subscription * @param connectionController * * @return Created subscription controller for the tab */ protected SubscriptionController createSubscriptionTab(final boolean allTab, final ManagedMessageStoreWithFiltering<FormattedMqttMessage> observableMessageStore, final MqttSubscription subscription, final MqttAsyncConnection connection, final ConnectionController connectionController) { // Load a new tab and connection pane final FXMLLoader loader = FxmlUtils.createFxmlLoaderForProjectFile("SubscriptionPane.fxml"); final AnchorPane subscriptionPane = FxmlUtils.loadAnchorPane(loader); final SubscriptionController subscriptionController = ((SubscriptionController) loader.getController()); final Tab tab = new Tab(); if (subscription != null) { eventBus.subscribeWithFilterOnly(subscriptionController, subscriptionController::onSubscriptionStatusChanged, SubscriptionStatusChangeEvent.class, subscription); // eventManager.registerSubscriptionStatusObserver(subscriptionController, subscription); } subscriptionController.setStore(observableMessageStore); subscriptionController.setEventBus(eventBus); subscriptionController.setConfingurationManager(configurationManager); if (connection != null) { subscriptionController.setFormattingManager(connection.getStore().getFormattingManager()); subscriptionController.setConnectionProperties(connection.getProperties()); } else { subscriptionController.setFormattingManager(new FormattingManager(new MqttScriptManager(null, null, null))); } subscriptionController.setTab(tab); subscriptionController.toggleMessagePayloadSize(connectionController.getResizeMessageContentMenu().isSelected()); tab.setClosable(false); tab.setContent(subscriptionPane); if (subscription != null) { tab.setStyle(StylingUtils.createBaseRGBString(subscription.getColor())); } if (allTab) { subscriptionControllers.put(ALL_SUBSCRIPTIONS_TAB_TITLE, subscriptionController); tab.setGraphic(new Label(ALL_SUBSCRIPTIONS_TAB_TITLE)); tab.getGraphic().getStyleClass().add("subscribed"); } else { logger.debug("Mapping subscription topic {} to controller", subscription.getTopic()); subscriptionControllers.put(subscription.getTopic(), subscriptionController); tab.setGraphic(new Label(subscription.getTopic())); tab.getGraphic().getStyleClass().add("unsubscribed"); tab.setTooltip(new Tooltip("Status: " + "unsubscribed")); } return subscriptionController; } /** * Completely removes subscription with the given topic (unsubscribes & closes tab). * * @param topic The subscription topic to remove */ public void removeSubscription(final String topic) { synchronized (subscriptionControllers) { logger.debug("Trying to remove subscription {}", topic); final MqttSubscription subscription = subscriptionControllers.get(topic).getSubscription(); subscription.getConnection().unsubscribeAndRemove(subscription); subscription.getStore().cleanUp(); subscriptionControllers.get(topic).onClose(); TabUtils.requestClose(subscriptionControllers.get(topic).getTab()); subscriptionControllers.remove(topic); } } /** * Gets all subscription controllers. * * @return Collection of SubscriptionController instances. */ public Collection<SubscriptionController> getSubscriptionControllers() { synchronized (subscriptionControllers) { return Collections.unmodifiableCollection(subscriptionControllers.values()); } } /** * Gets all subscription controllers. * * @return Collection of SubscriptionController instances. */ public Map<String, SubscriptionController> getSubscriptionControllersMap() { synchronized (subscriptionControllers) { return Collections.unmodifiableMap(subscriptionControllers); } } /** * Updates the subscription tab's context menu. * * TODO: use the Specification interface here * * @param tab The tab for which to perform the update * @param subscription The subscription for which to perform the update */ public static void updateSubscriptionTabContextMenu(final Tab tab, final MqttSubscription subscription) { logger.debug("Updating subscription tab context menu [{}, {}, {}]", subscription.getTopic(), subscription.getConnection().getConnectionStatus(), subscription.isActive()); // Update title style tab.getGraphic().getStyleClass().remove(tab.getGraphic().getStyleClass().size() - 1); if (subscription.isActive()) { tab.getGraphic().getStyleClass().add("subscribed"); tab.getTooltip().setText("Status: " + "subscribed"); } else { tab.getGraphic().getStyleClass().add("unsubscribed"); tab.getTooltip().setText("Status: " + "unsubscribed"); } // Set menu items if (subscription.getConnection().getConnectionStatus().equals(MqttConnectionStatus.CONNECTED)) { if (subscription.isActive()) { tab.getContextMenu().getItems().get(0).setDisable(false); tab.getContextMenu().getItems().get(1).setDisable(true); } else { tab.getContextMenu().getItems().get(0).setDisable(true); tab.getContextMenu().getItems().get(1).setDisable(false); } tab.getContextMenu().getItems().get(2).setDisable(false); } else { tab.getContextMenu().getItems().get(0).setDisable(true); tab.getContextMenu().getItems().get(1).setDisable(true); tab.getContextMenu().getItems().get(2).setDisable(true); } } }