org.opennms.features.topology.app.internal.TopologyUI.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.features.topology.app.internal.TopologyUI.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2012-2014 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2014 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.features.topology.app.internal;

import static org.opennms.features.topology.api.support.VertexHopGraphProvider.getWrappedVertexHopCriteria;
import static org.opennms.features.topology.app.internal.operations.TopologySelectorOperation.createOperationForDefaultGraphProvider;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.opennms.features.topology.api.CheckedOperation;
import org.opennms.features.topology.api.GraphContainer;
import org.opennms.features.topology.api.HasExtraComponents;
import org.opennms.features.topology.api.HistoryManager;
import org.opennms.features.topology.api.IViewContribution;
import org.opennms.features.topology.api.MapViewManager;
import org.opennms.features.topology.api.MapViewManagerListener;
import org.opennms.features.topology.api.OperationContext;
import org.opennms.features.topology.api.OperationContext.DisplayLocation;
import org.opennms.features.topology.api.SelectionContext;
import org.opennms.features.topology.api.SelectionListener;
import org.opennms.features.topology.api.SelectionManager;
import org.opennms.features.topology.api.SelectionNotifier;
import org.opennms.features.topology.api.VerticesUpdateManager;
import org.opennms.features.topology.api.WidgetContext;
import org.opennms.features.topology.api.WidgetManager;
import org.opennms.features.topology.api.WidgetUpdateListener;
import org.opennms.features.topology.api.browsers.ContentType;
import org.opennms.features.topology.api.browsers.SelectionAwareTable;
import org.opennms.features.topology.api.info.InfoPanelItem;
import org.opennms.features.topology.api.support.VertexHopGraphProvider;
import org.opennms.features.topology.api.support.VertexHopGraphProvider.VertexHopCriteria;
import org.opennms.features.topology.api.topo.Criteria;
import org.opennms.features.topology.api.topo.DefaultMetaInfo;
import org.opennms.features.topology.api.topo.MetaInfo;
import org.opennms.features.topology.api.topo.Vertex;
import org.opennms.features.topology.api.topo.VertexRef;
import org.opennms.features.topology.app.internal.CommandManager.DefaultOperationContext;
import org.opennms.features.topology.app.internal.TopologyComponent.VertexUpdateListener;
import org.opennms.features.topology.app.internal.jung.TopoFRLayoutAlgorithm;
import org.opennms.features.topology.app.internal.operations.RedoLayoutOperation;
import org.opennms.features.topology.app.internal.operations.TopologySelectorOperation;
import org.opennms.features.topology.app.internal.support.CategoryHopCriteria;
import org.opennms.features.topology.app.internal.support.FontAwesomeIcons;
import org.opennms.features.topology.app.internal.support.IconRepositoryManager;
import org.opennms.features.topology.app.internal.ui.HudDisplay;
import org.opennms.features.topology.app.internal.ui.InfoPanel;
import org.opennms.features.topology.app.internal.ui.LastUpdatedLabel;
import org.opennms.features.topology.app.internal.ui.NoContentAvailableWindow;
import org.opennms.features.topology.app.internal.ui.SearchBox;
import org.opennms.osgi.EventConsumer;
import org.opennms.osgi.OnmsServiceManager;
import org.opennms.osgi.VaadinApplicationContext;
import org.opennms.osgi.VaadinApplicationContextCreator;
import org.opennms.osgi.VaadinApplicationContextImpl;
import org.opennms.osgi.locator.OnmsServiceManagerLocator;
import org.opennms.web.api.OnmsHeaderProvider;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.wolfie.refresher.Refresher;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.annotations.StyleSheet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Title;
import com.vaadin.data.Property;
import com.vaadin.event.FieldEvents;
import com.vaadin.event.ShortcutAction;
import com.vaadin.server.DefaultErrorHandler;
import com.vaadin.server.FontAwesome;
import com.vaadin.server.Page.UriFragmentChangedEvent;
import com.vaadin.server.Page.UriFragmentChangedListener;
import com.vaadin.server.RequestHandler;
import com.vaadin.server.SessionDestroyListener;
import com.vaadin.server.ThemeResource;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinResponse;
import com.vaadin.server.VaadinServletRequest;
import com.vaadin.server.VaadinSession;
import com.vaadin.shared.ui.slider.SliderOrientation;
import com.vaadin.ui.AbsoluteLayout;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.MenuBar.MenuItem;
import com.vaadin.ui.NativeButton;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Slider;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.TabSheet.SelectedTabChangeEvent;
import com.vaadin.ui.TabSheet.SelectedTabChangeListener;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.VerticalSplitPanel;
import com.vaadin.ui.Window;

@SuppressWarnings("serial")
@Theme("topo_default")
@Title("OpenNMS Topology Map")
@PreserveOnRefresh
@StyleSheet(value = { "theme://font-awesome/css/font-awesome.min.css", "theme://ionicons/css/ionicons.min.css" })
public class TopologyUI extends UI
        implements CommandUpdateListener, MenuItemUpdateListener, ContextMenuHandler, WidgetUpdateListener,
        WidgetContext, UriFragmentChangedListener, GraphContainer.ChangeListener, MapViewManagerListener,
        VertexUpdateListener, SelectionListener, VerticesUpdateManager.VerticesUpdateListener {

    private class DynamicUpdateRefresher implements Refresher.RefreshListener {
        private final Object lockObject = new Object();
        private boolean m_refreshInProgress = false;
        private long m_lastUpdateTime = 0;

        @Override
        public void refresh(Refresher refresher) {
            if (needsRefresh()) {
                refreshUI();
            }
        }

        private void refreshUI() {
            synchronized (lockObject) {
                m_refreshInProgress = true;
                m_topologyComponent.blockSelectionEvents();

                final RedoLayoutOperation op = new RedoLayoutOperation();
                op.execute(getGraphContainer());

                m_lastUpdateTime = System.currentTimeMillis();
                updateTimestamp(m_lastUpdateTime);

                m_topologyComponent.unblockSelectionEvents();
                m_refreshInProgress = false;
            }
        }

        private boolean needsRefresh() {
            if (m_refreshInProgress) {
                return false;
            }

            if (!m_graphContainer.getAutoRefreshSupport().isEnabled()) {
                return false;
            }

            return true;
        }

    }

    private interface RequestParameterHandler {
        boolean handleRequest(VaadinRequest request);
    }

    /**
     * Class to handle Request Parameters, such as SZL, Vertices in Focus, Layout Selection, Graph Provider
     * Selection, Status Provider selection, etc...
     */
    private class TopologyUIRequestHandler implements RequestHandler {

        private final List<RequestParameterHandler> requestHandlerList;

        private static final String PARAMETER_LAYOUT = "layout";
        private static final String PARAMETER_FOCUS_NODES = "focusNodes";
        private static final String PARAMETER_FOCUS_VERTICES = "focus-vertices";
        private static final String PARAMETER_SEMANTIC_ZOOM_LEVEL = "szl";
        private static final String PARAMETER_GRAPH_PROVIDER = "provider";
        protected static final String PARAMETER_HISTORY_FRAGMENT = "ui-fragment";

        private TopologyUIRequestHandler() {
            requestHandlerList = Lists.newArrayList(
                    // The order matters
                    request -> loadHistoryFragment(request), request -> loadGraphProvider(request),
                    request -> loadVertexHopCriteria(request), request -> loadSemanticZoomLevel(request),
                    request -> loadLayout(request));
        }

        @Override
        public boolean handleRequest(VaadinSession session, VaadinRequest request, VaadinResponse response)
                throws IOException {
            handleRequestParameter(request);
            return false;
        }

        public void handleRequestParameter(VaadinRequest request) {
            boolean updateURL = false;
            for (RequestParameterHandler handler : requestHandlerList) {
                if (handler.handleRequest(request)) {
                    updateURL = true;
                }
            }
            // we redo the layout before we save the history
            m_graphContainer.redoLayout();
            m_topologyComponent.getState().setPhysicalWidth(0);
            m_topologyComponent.getState().setPhysicalHeight(0);
            m_topologyComponent.markAsDirtyRecursive();

            // Close all open Windows/Dialogs if it is not the "NO VERTICES IN FOCUS"-Window
            for (Window eachWindow : getWindows()) {
                if (eachWindow != m_noContentWindow) {
                    eachWindow.close();
                }
            }

            if (updateURL) {
                // If we have a new location, we reload the page.
                // This needs to be done to set the fragment of the page correctly
                // Otherwise the Request-Parameters would still be there

                // we must overwrite the existing saved history for this user, otherwise updateURL does not work
                String fragment = m_historyManager.createHistory(m_applicationContext.getUsername(),
                        m_graphContainer);
                LOG.info("Redirect user {} to topology fragment url with fragment {}",
                        m_applicationContext.getUsername(), fragment);
                getPage().setLocation(
                        String.format("%s#%s", ((VaadinServletRequest) request).getRequestURI(), fragment));
            }
        }

        private boolean loadLayout(VaadinRequest request) {
            String layoutName = request.getParameter(PARAMETER_LAYOUT);
            return executeOperationWithLabel(layoutName);
        }

        private boolean loadGraphProvider(VaadinRequest request) {
            String graphProviderName = request.getParameter(PARAMETER_GRAPH_PROVIDER);
            return executeOperationWithLabel(graphProviderName);
        }

        private boolean executeOperationWithLabel(String operationLabel) {
            final CheckedOperation operation = m_commandManager.findOperationByLabel(CheckedOperation.class,
                    operationLabel);
            if (operation != null) {
                final DefaultOperationContext operationContext = new DefaultOperationContext(TopologyUI.this,
                        m_graphContainer, DisplayLocation.MENUBAR);
                final List<VertexRef> targets = Collections.<VertexRef>emptyList();
                // CheckedOperations may toggle its state when execute is invoked.
                // We do not execute if already checked, as this would disable an already checked operation.
                if (!operation.isChecked(targets, operationContext)) {
                    operation.execute(targets, operationContext);
                    return true;
                }
            }
            return false;
        }

        private boolean loadHistoryFragment(VaadinRequest request) {
            String fragment = request.getParameter(PARAMETER_HISTORY_FRAGMENT);
            if (!Strings.isNullOrEmpty(fragment) && getPage() != null) {
                applyHistory(m_applicationContext.getUsername(), fragment);
                return true;
            }
            return false;
        }

        private boolean loadSemanticZoomLevel(VaadinRequest request) {
            String szl = request.getParameter(PARAMETER_SEMANTIC_ZOOM_LEVEL);
            if (szl != null) {
                try {
                    m_graphContainer.setSemanticZoomLevel(Integer.parseInt(szl));
                    return true;
                } catch (NumberFormatException e) {
                    LOG.warn("Invalid SZL found in {} parameter: {}", PARAMETER_SEMANTIC_ZOOM_LEVEL, szl);
                }
            }
            return false;
        }

        private boolean loadVertexHopCriteria(VaadinRequest request) {
            final String nodeIds = request.getParameter(PARAMETER_FOCUS_NODES);
            String vertexIdInFocus = request.getParameter(PARAMETER_FOCUS_VERTICES);
            if (nodeIds != null && vertexIdInFocus != null) {
                LOG.warn("Usage of parameter '{1}' and '{2}'. This is not supported. Skipping parameter '{2}'",
                        PARAMETER_FOCUS_NODES, PARAMETER_FOCUS_VERTICES);
            }
            if (nodeIds != null) {
                LOG.warn("Usage of deprecated parameter '{}'. Please use '{}' instead.", PARAMETER_FOCUS_NODES,
                        PARAMETER_FOCUS_VERTICES);
                vertexIdInFocus = nodeIds;
            }
            if (vertexIdInFocus != null) {
                // Build the VertexRef elements
                final TreeSet<VertexRef> refs = new TreeSet<>();
                for (String vertexId : vertexIdInFocus.split(",")) {
                    String namespace = m_graphContainer.getBaseTopology().getVertexNamespace();
                    Vertex vertex = m_graphContainer.getBaseTopology().getVertex(namespace, vertexId);
                    if (vertex == null) {
                        LOG.warn(
                                "Vertex with namespace {} and id {} do not exist in the selected Graph Provider {}",
                                namespace, vertexId, m_graphContainer.getBaseTopology().getClass().getSimpleName());
                    } else {
                        refs.add(vertex);
                    }
                }
                // We have to update the vertices in focus (in our case only nodes) only if the focus has changed
                VertexHopGraphProvider.VertexHopCriteria criteria = VertexHopGraphProvider
                        .getWrappedVertexHopCriteria(m_graphContainer);
                if (!criteria.getVertices().equals(refs)) {
                    m_graphContainer.clearCriteria();
                    refs.forEach(vertexRef -> m_graphContainer
                            .addCriteria(new VertexHopGraphProvider.DefaultVertexHopCriteria(vertexRef)));
                    m_graphContainer.setSemanticZoomLevel(1);
                }
                return true;
            }
            return false;
        }
    }

    /**
     * Helper class to load components to show in the info panel.
     */
    public class InfoPanelItemProvider
            implements SelectionListener, MenuItemUpdateListener, GraphContainer.ChangeListener {

        // Panel Item to visualize the selection context
        private final InfoPanelItem selectionContextPanelItem = new InfoPanelItem() {

            @Override
            public Component getComponent(GraphContainer container) {
                synchronized (m_currentHudDisplayLock) {
                    m_currentHudDisplay = new HudDisplay();
                    m_currentHudDisplay.setImmediate(true);
                    m_currentHudDisplay.setProvider(
                            m_graphContainer.getBaseTopology().getVertexNamespace().equals("nodes") ? "Linkd"
                                    : m_graphContainer.getBaseTopology().getVertexNamespace());
                    m_currentHudDisplay.setVertexFocusCount(getFocusVertices(m_graphContainer));
                    m_currentHudDisplay.setEdgeFocusCount(0);
                    m_currentHudDisplay.setVertexSelectionCount(
                            m_graphContainer.getSelectionManager().getSelectedVertexRefs().size());
                    m_currentHudDisplay.setEdgeSelectionCount(
                            m_graphContainer.getSelectionManager().getSelectedEdgeRefs().size());
                    m_currentHudDisplay
                            .setVertexContextCount(m_graphContainer.getGraph().getDisplayVertices().size());
                    m_currentHudDisplay.setEdgeContextCount(m_graphContainer.getGraph().getDisplayEdges().size());
                    m_currentHudDisplay
                            .setVertexTotalCount(m_graphContainer.getBaseTopology().getVertexTotalCount());
                    m_currentHudDisplay.setEdgeTotalCount(m_graphContainer.getBaseTopology().getEdges().size());
                    return m_currentHudDisplay;
                }
            }

            @Override
            public boolean contributesTo(GraphContainer container) {
                // only show if no selection
                return container.getSelectionManager().getSelectedEdgeRefs().isEmpty()
                        && container.getSelectionManager().getSelectedVertexRefs().isEmpty();
            }

            @Override
            public String getTitle(GraphContainer container) {
                return "Selection Context";
            }

            @Override
            public int getOrder() {
                return 1;
            }
        };

        // Panel Item to visualize the meta info
        private final InfoPanelItem metaInfoPanelItem = new InfoPanelItem() {

            private MetaInfo getMetaInfo() {
                MetaInfo metaInfo = getGraphContainer().getBaseTopology().getMetaInfo();

                if (Objects.isNull(metaInfo)) {
                    metaInfo = new DefaultMetaInfo();
                }

                return metaInfo;
            }

            @Override
            public Component getComponent(GraphContainer container) {
                return new Label(getMetaInfo().getDescription());
            }

            @Override
            public boolean contributesTo(GraphContainer container) {
                // only show if no selection
                return container.getSelectionManager().getSelectedEdgeRefs().isEmpty()
                        && container.getSelectionManager().getSelectedVertexRefs().isEmpty();
            }

            @Override
            public String getTitle(GraphContainer container) {
                return getMetaInfo().getName();
            }

            @Override
            public int getOrder() {
                return 0;
            }
        };

        private Component wrap(InfoPanelItem item) {
            return wrap(item.getComponent(m_graphContainer), item.getTitle(m_graphContainer));
        }

        /**
         * Wraps the provided component in order to fit it better in the Info Panel.
         * E.g. a caption is added to better difference between components.
         *
         * @param component The component to wrap.
         * @param title the title of the component to wrap.
         * @return The wrapped component.
         */
        private Component wrap(Component component, String title) {
            Label label = new Label();
            label.addStyleName("info-panel-item-label");
            if (title != null) {
                label.setValue(title);
            }

            VerticalLayout layout = new VerticalLayout();
            layout.addStyleName("info-panel-item");
            layout.addComponent(label);
            layout.addComponent(component);
            layout.setMargin(true);

            return layout;
        }

        private List<Component> getInfoPanelComponents() {
            final List<InfoPanelItem> infoPanelItems = findInfoPanelItems();
            infoPanelItems.add(selectionContextPanelItem); // manually add this, as it is not exposed via osgi
            infoPanelItems.add(metaInfoPanelItem); // same here
            return infoPanelItems.stream().filter(item -> {
                try {
                    return item.contributesTo(m_graphContainer);
                } catch (Throwable t) {
                    // See NMS-8394
                    LOG.error("An error occured while determining if info panel item {} should be displayed. "
                            + "The component will not be displayed.", item.getClass(), t);
                    return false;
                }
            }).sorted().map(item -> {
                try {
                    return wrap(item);
                } catch (Throwable t) {
                    // See NMS-8394
                    LOG.error("An error occured while retriveing the component from info panel item {}. "
                            + "The component will not be displayed.", item.getClass(), t);
                    return null;
                }
            }).filter(component -> component != null) // Skip any nulls from components with exceptions
                    .collect(Collectors.toList());
        }

        private List<InfoPanelItem> findInfoPanelItems() {
            try {
                return m_bundlecontext.getServiceReferences(InfoPanelItem.class, null).stream()
                        .map(eachRef -> m_bundlecontext.getService(eachRef)).collect(Collectors.toList());
            } catch (InvalidSyntaxException e) {
                LOG.error(e.getMessage(), e);
                return Collections.emptyList();
            }
        }

        private void refreshInfoPanel() {
            List<Component> components = Lists.newArrayList();
            components.addAll(getInfoPanelComponents());
            m_infoPanel.setDynamicComponents(components);
        }

        @Override
        public void selectionChanged(SelectionContext selectionContext) {
            refreshInfoPanel();
        }

        @Override
        public void updateMenuItems() {
            refreshInfoPanel();
        }

        @Override
        public void graphChanged(GraphContainer graphContainer) {
            refreshInfoPanel();
        }
    }

    private static final long serialVersionUID = 6837501987137310938L;
    private static final Logger LOG = LoggerFactory.getLogger(TopologyUI.class);

    private TopologyComponent m_topologyComponent;
    private Window m_noContentWindow;
    private InfoPanel m_infoPanel;
    private final GraphContainer m_graphContainer;
    private SelectionManager m_selectionManager;
    private final CommandManager m_commandManager;
    private MenuBar m_menuBar;
    private TopoContextMenu m_contextMenu;
    private VerticalLayout m_layout;
    private VerticalLayout m_rootLayout;
    private final IconRepositoryManager m_iconRepositoryManager;
    private WidgetManager m_widgetManager;
    private AbsoluteLayout m_treeMapSplitPanel;
    private final TextField m_zoomLevelLabel = new TextField();
    private final HistoryManager m_historyManager;
    private String m_headerHtml;
    private boolean m_showHeader = true;
    private OnmsHeaderProvider m_headerProvider = null;
    private OnmsServiceManager m_serviceManager;
    private VaadinApplicationContext m_applicationContext;
    private VerticesUpdateManager m_verticesUpdateManager;
    private Button m_panBtn;
    private Button m_selectBtn;
    private Button m_szlOutBtn;
    private LastUpdatedLabel m_lastUpdatedTimeLabel;
    int m_settingFragment = 0;
    private SearchBox m_searchBox;
    private TabSheet tabSheet;
    private BundleContext m_bundlecontext;
    private final Object m_currentHudDisplayLock = new Object();
    private HudDisplay m_currentHudDisplay;

    private String getHeader(HttpServletRequest request) throws Exception {
        if (m_headerProvider == null) {
            return "";
        } else {
            return m_headerProvider.getHeaderHtml(request);
        }
    }

    public TopologyUI(CommandManager commandManager, HistoryManager historyManager, GraphContainer graphContainer,
            IconRepositoryManager iconRepoManager) {
        // Ensure that selection changes trigger a history save operation
        m_commandManager = commandManager;
        m_historyManager = historyManager;
        m_iconRepositoryManager = iconRepoManager;

        // We set it programmatically, as we require a GraphContainer instance per Topology UI instance.
        // Scope Prototype would create too many GraphContainers, as scope singleton would create too few.
        m_selectionManager = new DefaultSelectionManager(graphContainer);

        // Create a per-session GraphContainer instance
        m_graphContainer = graphContainer;
        m_graphContainer.setSelectionManager(m_selectionManager);
        m_graphContainer.setIconManager(m_iconRepositoryManager);
    }

    @Override
    protected void init(final VaadinRequest request) {
        // Register a cleanup
        request.getService().addSessionDestroyListener(
                (SessionDestroyListener) event -> m_widgetManager.removeUpdateListener(TopologyUI.this));

        try {
            m_headerHtml = getHeader(((VaadinServletRequest) request).getHttpServletRequest());
        } catch (final Exception e) {
            LOG.error("failed to get header HTML for request " + request.getPathInfo(), e.getCause());
        }

        //create VaadinApplicationContext
        m_applicationContext = m_serviceManager.createApplicationContext(new VaadinApplicationContextCreator() {
            @Override
            public VaadinApplicationContext create(OnmsServiceManager manager) {
                VaadinApplicationContextImpl context = new VaadinApplicationContextImpl();
                context.setSessionId(request.getWrappedSession().getId());
                context.setUiId(getUIId());
                context.setUsername(request.getRemoteUser());
                return context;
            }
        });
        m_verticesUpdateManager = new OsgiVerticesUpdateManager(m_serviceManager, m_applicationContext);
        m_serviceManager.getEventRegistry().addPossibleEventConsumer(this, m_applicationContext);

        // Set the algorithm last so that the criteria and SZLs are
        // in place before we run the layout algorithm.
        m_graphContainer.setSessionId(m_applicationContext.getSessionId());
        m_graphContainer.setLayoutAlgorithm(new TopoFRLayoutAlgorithm());

        createLayouts();
        setupErrorHandler(); // Set up an error handler for UI-level exceptions
        setupAutoRefresher(); // Add an auto refresh handler to the GraphContainer

        loadUserSettings();
        // the layout must be created BEFORE loading the hop criteria and the semantic zoom level
        TopologyUIRequestHandler handler = new TopologyUIRequestHandler();
        getSession().addRequestHandler(handler); // Add a request handler that parses incoming focusNode and szl query parameters
        handler.handleRequestParameter(request); // deal with those in init case

        // Add the default criteria if we do not have already a criteria set
        if (getWrappedVertexHopCriteria(m_graphContainer).isEmpty() && noAdditionalFocusCriteria()) {
            m_graphContainer.addCriteria(m_graphContainer.getBaseTopology().getDefaultCriteria()); // set default
        }

        // If no Topology Provider was selected (due to loadUserSettings(), fallback to default
        if (m_graphContainer.getBaseTopology() == null
                || m_graphContainer.getBaseTopology() == MergingGraphProvider.NULL_PROVIDER) {
            TopologySelectorOperation defaultTopologySelectorOperation = createOperationForDefaultGraphProvider(
                    m_bundlecontext, "(|(label=Enhanced Linkd)(label=Linkd))");
            Objects.requireNonNull(defaultTopologySelectorOperation, "No default GraphProvider found."); // no default found, abort
            defaultTopologySelectorOperation.execute(m_graphContainer);
        }

        // We set the listeners at the end, to not fire them all the time when initializing the UI
        setupListeners();

        // We force a reload of the topology provider as it may not have been initialized
        m_graphContainer.getBaseTopology().refresh();

        // We force a reload to trigger a fireGraphChanged()
        m_graphContainer.setDirty(true);
        m_graphContainer.redoLayout();

        // Trigger a selectionChanged
        m_selectionManager.selectionChanged(m_selectionManager);
    }

    private void setupListeners() {
        getPage().addUriFragmentChangedListener(this);
        m_selectionManager.addSelectionListener(this);
        m_graphContainer.addChangeListener(this);
        m_graphContainer.getMapViewManager().addListener(this);
        m_commandManager.addMenuItemUpdateListener(this);
        m_commandManager.addCommandUpdateListener(this);

        m_graphContainer.addChangeListener(m_searchBox);
        m_selectionManager.addSelectionListener(m_searchBox);

        m_graphContainer.addChangeListener(m_verticesUpdateManager);
        m_selectionManager.addSelectionListener(m_verticesUpdateManager);

        // Register the Info Panel to listen for certain events
        final InfoPanelItemProvider infoPanelItemProvider = new InfoPanelItemProvider();
        m_selectionManager.addSelectionListener(infoPanelItemProvider);
        m_commandManager.addMenuItemUpdateListener(infoPanelItemProvider);
        m_graphContainer.addChangeListener(infoPanelItemProvider);
    }

    private boolean noAdditionalFocusCriteria() {
        Criteria[] crits = m_graphContainer.getCriteria();
        for (Criteria criteria : crits) {
            if (criteria instanceof CategoryHopCriteria) {
                return false;
            }
        }
        return true;
    }

    private void createLayouts() {
        m_rootLayout = new VerticalLayout();
        m_rootLayout.setSizeFull();
        m_rootLayout.addStyleName("root-layout");
        m_rootLayout.addStyleName("topo-root-layout");
        setContent(m_rootLayout);

        addHeader();

        addContentLayout();

        addNoContentWindow();
    }

    private void addNoContentWindow() {
        m_noContentWindow = new NoContentAvailableWindow(m_graphContainer);
        m_noContentWindow.setVisible(true);
        addWindow(m_noContentWindow);
    }

    private void setupErrorHandler() {
        setErrorHandler(new DefaultErrorHandler() {

            @Override
            public void error(com.vaadin.server.ErrorEvent event) {
                Notification.show("An Exception Occurred: see karaf.log", Notification.Type.TRAY_NOTIFICATION);
                LOG.warn("An Exception Occurred: in the TopologyUI", event.getThrowable());

            }
        });
    }

    private void setupAutoRefresher() {
        if (m_graphContainer.hasAutoRefreshSupport()) {
            Refresher refresher = new Refresher();
            refresher.setRefreshInterval((int) m_graphContainer.getAutoRefreshSupport().getInterval() * 1000); // ask every 1 seconds for changes
            refresher.addListener(new DynamicUpdateRefresher());
            addExtension(refresher);
        }
    }

    private void addHeader() {
        if (m_headerHtml != null && m_showHeader) {
            InputStream is = null;
            try {
                is = new ByteArrayInputStream(m_headerHtml.getBytes());
                final CustomLayout headerLayout = new CustomLayout(is);
                headerLayout.setWidth("100%");
                headerLayout.addStyleName("onmsheader");
                m_rootLayout.addComponent(headerLayout);
            } catch (final IOException e) {
                try {
                    is.close();
                } catch (final IOException closeE) {
                    LOG.debug("failed to close HTML input stream", closeE);
                }
                LOG.debug("failed to get header layout data", e);
            }
        }
    }

    private void addContentLayout() {
        m_layout = new VerticalLayout();
        m_layout.setSizeFull();

        // Set expand ratio so that all extra space is allocated to this vertical component
        m_rootLayout.addComponent(m_layout);
        m_rootLayout.setExpandRatio(m_layout, 1);

        //Don't create a horizontal Split container here, no need. Remove and use the absolute
        m_treeMapSplitPanel = new AbsoluteLayout();
        m_treeMapSplitPanel.addComponent(createMapLayout(), "top: 0px; left: 0px; right: 0px; bottom: 0px;");
        m_treeMapSplitPanel.setSizeFull();

        menuBarUpdated(m_commandManager);
        if (m_widgetManager.widgetCount() != 0) {
            updateWidgetView(m_widgetManager);
        } else {
            m_layout.addComponent(m_treeMapSplitPanel);
        }
    }

    private Component createMapLayout() {
        final Property<Double> scale = m_graphContainer.getScaleProperty();

        m_lastUpdatedTimeLabel = new LastUpdatedLabel();
        m_lastUpdatedTimeLabel.setImmediate(true);

        m_zoomLevelLabel.setHeight(20, Unit.PIXELS);
        m_zoomLevelLabel.setWidth(22, Unit.PIXELS);
        m_zoomLevelLabel.addStyleName("center-text");
        m_zoomLevelLabel.addTextChangeListener(new FieldEvents.TextChangeListener() {
            @Override
            public void textChange(FieldEvents.TextChangeEvent event) {
                try {
                    int zoomLevel = Integer.parseInt(event.getText());
                    setSemanticZoomLevel(zoomLevel);
                } catch (NumberFormatException e) {
                    setSemanticZoomLevel(m_graphContainer.getSemanticZoomLevel());
                }

            }
        });

        m_topologyComponent = new TopologyComponent(m_graphContainer, m_iconRepositoryManager, this);
        m_topologyComponent.setSizeFull();
        m_topologyComponent.addMenuItemStateListener(this);
        m_topologyComponent.addVertexUpdateListener(this);

        final Slider slider = new Slider(0, 1);
        slider.setPropertyDataSource(scale);
        slider.setResolution(1);
        slider.setHeight("200px");
        slider.setOrientation(SliderOrientation.VERTICAL);

        slider.setImmediate(true);

        final NativeButton showFocusVerticesBtn = new NativeButton(FontAwesomeIcons.Icon.eye_open.variant());
        showFocusVerticesBtn.setDescription("Toggle Highlight Focus Nodes");
        showFocusVerticesBtn.setHtmlContentAllowed(true);
        showFocusVerticesBtn.addClickListener(new ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
                if (showFocusVerticesBtn.getCaption().equals(FontAwesomeIcons.Icon.eye_close.variant())) {
                    showFocusVerticesBtn.setCaption(FontAwesomeIcons.Icon.eye_open.variant());
                } else {
                    showFocusVerticesBtn.setCaption(FontAwesomeIcons.Icon.eye_close.variant());
                }
                m_topologyComponent.getState()
                        .setHighlightFocus(!m_topologyComponent.getState().isHighlightFocus());
                m_topologyComponent.updateGraph();
            }
        });

        final NativeButton magnifyBtn = new NativeButton();
        magnifyBtn.setHtmlContentAllowed(true);
        magnifyBtn.setCaption("<i class=\"" + FontAwesomeIcons.Icon.zoom_in.stylename() + "\" ></i>");
        magnifyBtn.setStyleName("icon-button");
        magnifyBtn.addClickListener(new ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
                if (slider.getValue() < 1) {
                    slider.setValue(Math.min(1, slider.getValue() + 0.25));
                }
            }
        });

        final NativeButton demagnifyBtn = new NativeButton();
        demagnifyBtn.setHtmlContentAllowed(true);
        demagnifyBtn.setCaption("<i class=\"" + FontAwesomeIcons.Icon.zoom_out.stylename() + "\" ></i>");
        demagnifyBtn.setStyleName("icon-button");
        demagnifyBtn.addClickListener(new ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
                if (slider.getValue() != 0) {
                    slider.setValue(Math.max(0, slider.getValue() - 0.25));
                }
            }
        });

        VerticalLayout sliderLayout = new VerticalLayout();
        sliderLayout.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
        sliderLayout.addComponent(magnifyBtn);
        sliderLayout.addComponent(slider);
        sliderLayout.addComponent(demagnifyBtn);

        m_szlOutBtn = new Button();
        m_szlOutBtn.setHtmlContentAllowed(true);
        m_szlOutBtn.setCaption(FontAwesomeIcons.Icon.arrow_down.variant());
        m_szlOutBtn.setDescription("Collapse Semantic Zoom Level");
        m_szlOutBtn.setEnabled(m_graphContainer.getSemanticZoomLevel() > 0);
        m_szlOutBtn.addClickListener(new ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
                int szl = m_graphContainer.getSemanticZoomLevel();
                if (szl > 0) {
                    szl--;
                    setSemanticZoomLevel(szl);
                    saveHistory();
                }
            }
        });

        final Button szlInBtn = new Button();
        szlInBtn.setHtmlContentAllowed(true);
        szlInBtn.setCaption(FontAwesomeIcons.Icon.arrow_up.variant());
        szlInBtn.setDescription("Expand Semantic Zoom Level");
        szlInBtn.addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                int szl = m_graphContainer.getSemanticZoomLevel();
                szl++;
                setSemanticZoomLevel(szl);
                saveHistory();
            }
        });

        m_panBtn = new Button();
        m_panBtn.setIcon(FontAwesome.ARROWS);
        m_panBtn.setDescription("Pan Tool");
        m_panBtn.setStyleName("toolbar-button down");

        m_selectBtn = new Button();
        m_selectBtn.setIcon(new ThemeResource("images/selection.png"));
        m_selectBtn.setDescription("Selection Tool");
        m_selectBtn.setStyleName("toolbar-button");
        m_selectBtn.addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                m_selectBtn.setStyleName("toolbar-button down");
                m_panBtn.setStyleName("toolbar-button");
                m_topologyComponent.setActiveTool("select");
            }
        });

        m_panBtn.addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                m_panBtn.setStyleName("toolbar-button down");
                m_selectBtn.setStyleName("toolbar-button");
                m_topologyComponent.setActiveTool("pan");
            }
        });

        final Button historyBackBtn = new Button(FontAwesomeIcons.Icon.arrow_left.variant());
        historyBackBtn.setHtmlContentAllowed(true);
        historyBackBtn.setDescription("Click to go back");
        historyBackBtn.addClickListener(new ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
                com.vaadin.ui.JavaScript.getCurrent().execute("window.history.back()");
            }
        });

        final Button historyForwardBtn = new Button(FontAwesomeIcons.Icon.arrow_right.variant());
        historyForwardBtn.setHtmlContentAllowed(true);
        historyForwardBtn.setDescription("Click to go forward");
        historyForwardBtn.addClickListener(new ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
                com.vaadin.ui.JavaScript.getCurrent().execute("window.history.forward()");
            }
        });

        m_searchBox = new SearchBox(m_serviceManager, new CommandManager.DefaultOperationContext(this,
                m_graphContainer, OperationContext.DisplayLocation.SEARCH));

        //History Button Layout
        HorizontalLayout historyButtonLayout = new HorizontalLayout();
        historyButtonLayout.setSpacing(true);
        historyButtonLayout.addComponent(historyBackBtn);
        historyButtonLayout.addComponent(historyForwardBtn);

        //Semantic Controls Layout
        HorizontalLayout semanticLayout = new HorizontalLayout();
        semanticLayout.setSpacing(true);
        semanticLayout.addComponent(szlInBtn);
        semanticLayout.addComponent(m_zoomLevelLabel);
        semanticLayout.addComponent(m_szlOutBtn);
        semanticLayout.setComponentAlignment(m_zoomLevelLabel, Alignment.MIDDLE_CENTER);

        VerticalLayout historyCtrlLayout = new VerticalLayout();
        historyCtrlLayout.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
        historyCtrlLayout.addComponent(historyButtonLayout);

        HorizontalLayout controlLayout = new HorizontalLayout();
        controlLayout.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
        controlLayout.addComponent(m_panBtn);
        controlLayout.addComponent(m_selectBtn);

        VerticalLayout semanticCtrlLayout = new VerticalLayout();
        semanticCtrlLayout.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
        semanticCtrlLayout.addComponent(semanticLayout);

        HorizontalLayout locationToolLayout = createLocationToolLayout();

        //Vertical Layout for all tools on right side
        VerticalLayout toollayout = new VerticalLayout();
        toollayout.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
        toollayout.setSpacing(true);

        toollayout.addComponent(historyCtrlLayout);
        toollayout.addComponent(locationToolLayout);
        toollayout.addComponent(showFocusVerticesBtn);
        toollayout.addComponent(sliderLayout);
        toollayout.addComponent(controlLayout);
        toollayout.addComponent(semanticCtrlLayout);

        AbsoluteLayout mapLayout = new AbsoluteLayout();
        mapLayout.addComponent(m_topologyComponent, "top:0px; left: 0px; right: 0px; bottom: 0px;");
        mapLayout.addComponent(m_lastUpdatedTimeLabel, "top: 5px; right: 10px;");
        mapLayout.addComponent(toollayout, "top: 25px; right: 10px;");
        mapLayout.setSizeFull();

        m_infoPanel = new InfoPanel(m_searchBox, mapLayout);
        return m_infoPanel;
    }

    private HorizontalLayout createLocationToolLayout() {
        HorizontalLayout layout = new HorizontalLayout();
        layout.setSpacing(true);
        layout.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);

        Button showAllMapBtn = new Button(FontAwesomeIcons.Icon.globe.variant());
        showAllMapBtn.setHtmlContentAllowed(true);
        showAllMapBtn.setDescription("Show Entire Map");
        showAllMapBtn.addClickListener(new ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
                m_topologyComponent.showAllMap();
            }
        });

        Button centerSelectionBtn = new Button(FontAwesomeIcons.Icon.location_arrow.variant());
        centerSelectionBtn.setHtmlContentAllowed(true);
        centerSelectionBtn.setDescription("Center On Selection");
        centerSelectionBtn.addClickListener(new ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
                m_topologyComponent.centerMapOnSelection();
            }
        });

        layout.addComponent(centerSelectionBtn);
        layout.addComponent(showAllMapBtn);

        return layout;
    }

    // See if the history manager has an existing fragment stored for
    // this user. Do this before laying out the UI because the history
    // may change during layout.
    private void loadUserSettings() {
        applyHistory(m_applicationContext.getUsername(),
                m_historyManager.getHistoryHash(m_applicationContext.getUsername()));
        m_graphContainer.redoLayout();
    }

    private void applyHistory(String username, String fragment) {
        // If there was existing history, then restore that history snapshot.
        if (fragment != null) {
            LoggerFactory.getLogger(this.getClass()).info("Restoring history for user {}: {}", username, fragment);
            if (getPage() != null) {
                getPage().setUriFragment(fragment);
            }
            m_historyManager.applyHistory(username, fragment, m_graphContainer);
        }
    }

    /**
     * Updates the bottom widget area with the registered widgets
     * 
     * Any widget with the service property of 'location=bottom' are
     * included.
     * 
     * @param widgetManager The WidgetManager.
     */
    private void updateWidgetView(WidgetManager widgetManager) {
        synchronized (m_layout) {
            m_layout.removeAllComponents();
            if (widgetManager.widgetCount() == 0) {
                m_layout.addComponent(m_treeMapSplitPanel);
            } else {
                VerticalSplitPanel bottomLayoutBar = new VerticalSplitPanel();
                bottomLayoutBar.setFirstComponent(m_treeMapSplitPanel);
                // Split the screen 70% top, 30% bottom
                bottomLayoutBar.setSplitPosition(70, Unit.PERCENTAGE);
                bottomLayoutBar.setSizeFull();
                bottomLayoutBar.setSecondComponent(getTabSheet(widgetManager, this));
                m_layout.addComponent(bottomLayoutBar);
                updateTabVisibility();
            }
            m_layout.markAsDirty();
        }
        m_layout.markAsDirty();
    }

    /**
     * Gets a {@link TabSheet} view for all widgets in this manager.
     * 
     * @return TabSheet
     */
    private Component getTabSheet(WidgetManager manager, WidgetContext widgetContext) {
        // Use an absolute layout for the bottom panel
        AbsoluteLayout bottomLayout = new AbsoluteLayout();
        bottomLayout.setSizeFull();

        tabSheet = new TabSheet();
        tabSheet.setSizeFull();

        for (IViewContribution viewContrib : manager.getWidgets()) {
            // Create a new view instance
            final Component view = viewContrib.getView(m_applicationContext, widgetContext);
            try {
                m_graphContainer.getSelectionManager().addSelectionListener((SelectionListener) view);
            } catch (ClassCastException e) {
            }
            try {
                ((SelectionNotifier) view).addSelectionListener(m_graphContainer.getSelectionManager());
            } catch (ClassCastException e) {
            }

            // Icon can be null
            tabSheet.addTab(view, viewContrib.getTitle(), viewContrib.getIcon());

            // If the component supports the HasExtraComponents interface, then add the extra 
            // components to the tab bar
            try {
                Component[] extras = ((HasExtraComponents) view).getExtraComponents();
                if (extras != null && extras.length > 0) {
                    // For any extra controls, add a horizontal layout that will float
                    // on top of the right side of the tab panel
                    final HorizontalLayout extraControls = new HorizontalLayout();
                    extraControls.setHeight(32, Unit.PIXELS);
                    extraControls.setSpacing(true);

                    // Add the extra controls to the layout
                    for (Component component : extras) {
                        extraControls.addComponent(component);
                        extraControls.setComponentAlignment(component, Alignment.MIDDLE_RIGHT);
                    }

                    // Add a TabSheet.SelectedTabChangeListener to show or hide the extra controls
                    tabSheet.addSelectedTabChangeListener(new SelectedTabChangeListener() {
                        private static final long serialVersionUID = 6370347645872323830L;

                        @Override
                        public void selectedTabChange(SelectedTabChangeEvent event) {
                            final TabSheet source = (TabSheet) event.getSource();
                            if (source == tabSheet) {
                                // Bizarrely enough, getSelectedTab() returns the contained
                                // Component, not the Tab itself.
                                //
                                // If the first tab was selected...
                                if (source.getSelectedTab() == view) {
                                    extraControls.setVisible(true);
                                } else {
                                    extraControls.setVisible(false);
                                }
                            }
                        }
                    });

                    // Place the extra controls on the absolute layout
                    bottomLayout.addComponent(extraControls, "top:0px;right:5px;z-index:100");
                }
            } catch (ClassCastException e) {
            }
            view.setSizeFull();
        }

        // Add the tabsheet to the layout
        bottomLayout.addComponent(tabSheet, "top: 0; left: 0; bottom: 0; right: 0;");

        return bottomLayout;
    }

    private void updateTabVisibility() {
        for (int i = 0; i < tabSheet.getComponentCount(); i++) {
            TabSheet.Tab tab = tabSheet.getTab(i);
            if (tab.getComponent() instanceof SelectionAwareTable) {
                ContentType contentType = ((SelectionAwareTable) tab.getComponent()).getContentType();
                boolean visible = m_graphContainer.getBaseTopology().contributesTo(contentType);
                tab.setVisible(visible);
            }
        }
    }

    public void updateTimestamp(long updateTime) {
        m_lastUpdatedTimeLabel.setUpdateTime(updateTime);
    }

    @Override
    public void updateMenuItems() {
        updateMenuItems(m_menuBar.getItems());
    }

    private void updateMenuItems(List<MenuItem> menuItems) {
        for (MenuItem menuItem : menuItems) {
            if (menuItem.hasChildren()) {
                updateMenuItems(menuItem.getChildren());
            } else {
                m_commandManager.updateMenuItem(menuItem, m_graphContainer, this);
            }
        }
    }

    @Override
    public void menuBarUpdated(CommandManager commandManager) {
        if (m_menuBar != null) {
            m_rootLayout.removeComponent(m_menuBar);
        }

        if (m_contextMenu != null) {
            m_contextMenu.detach();
        }

        m_menuBar = commandManager.getMenuBar(m_graphContainer, this);
        m_menuBar.setWidth(100, Unit.PERCENTAGE);
        // Set expand ratio so that extra space is not allocated to this vertical component
        if (m_showHeader) {
            m_rootLayout.addComponent(m_menuBar, 1);
        } else {
            m_rootLayout.addComponent(m_menuBar, 0);
        }

        m_contextMenu = commandManager
                .getContextMenu(new DefaultOperationContext(this, m_graphContainer, DisplayLocation.CONTEXTMENU));
        m_contextMenu.setAsContextMenuOf(this);

        // Add Menu Item to share the View with others
        m_menuBar.addItem("Share", FontAwesome.SHARE, new MenuBar.Command() {
            @Override
            public void menuSelected(MenuItem selectedItem) {
                // create the share link
                String fragment = getPage().getLocation().getFragment();
                String url = getPage().getLocation().toString().replace("#" + getPage().getLocation().getFragment(),
                        "");
                String shareLink = String.format("%s?%s=%s", url,
                        TopologyUIRequestHandler.PARAMETER_HISTORY_FRAGMENT, fragment);

                // Create the Window
                Window shareWindow = new Window();
                shareWindow.setCaption("Share Link");
                shareWindow.setModal(true);
                shareWindow.setClosable(true);
                shareWindow.setResizable(false);
                shareWindow.setWidth(400, Unit.PIXELS);

                TextArea shareLinkField = new TextArea();
                shareLinkField.setValue(shareLink);
                shareLinkField.setReadOnly(true);
                shareLinkField.setRows(3);
                shareLinkField.setWidth(100, Unit.PERCENTAGE);

                // Close Button
                Button close = new Button("Close");
                close.setClickShortcut(ShortcutAction.KeyCode.ESCAPE, null);
                close.addClickListener(event -> shareWindow.close());

                // Layout for Buttons
                HorizontalLayout buttonLayout = new HorizontalLayout();
                buttonLayout.setMargin(true);
                buttonLayout.setSpacing(true);
                buttonLayout.setWidth("100%");
                buttonLayout.addComponent(close);
                buttonLayout.setComponentAlignment(close, Alignment.BOTTOM_RIGHT);

                // Content Layout
                VerticalLayout verticalLayout = new VerticalLayout();
                verticalLayout.setMargin(true);
                verticalLayout.setSpacing(true);
                verticalLayout.addComponent(
                        new Label("Please use the following link to share the current view with others."));
                verticalLayout.addComponent(shareLinkField);
                verticalLayout.addComponent(buttonLayout);

                shareWindow.setContent(verticalLayout);

                getUI().addWindow(shareWindow);
            }
        });

        updateMenuItems();
    }

    @Override
    public void showContextMenu(Object target, int left, int top) {
        // The target must be set before we update the operation context because the op context
        // operations are dependent on the target of the right-click
        m_contextMenu.setTarget(target);
        m_contextMenu.updateOperationContext(
                new DefaultOperationContext(this, m_graphContainer, DisplayLocation.CONTEXTMENU));
        m_contextMenu.open(left, top);
    }

    public WidgetManager getWidgetManager() {
        return m_widgetManager;
    }

    public void setWidgetManager(WidgetManager widgetManager) {
        m_widgetManager = widgetManager;
        m_widgetManager.addUpdateListener(this);
    }

    @Override
    public void widgetListUpdated(WidgetManager widgetManager) {
        if (isAttached()) {
            updateWidgetView(widgetManager);
        }
    }

    @Override
    public GraphContainer getGraphContainer() {
        return m_graphContainer;
    }

    @Override
    public void uriFragmentChanged(UriFragmentChangedEvent event) {
        m_settingFragment++;
        String fragment = event.getUriFragment();
        m_historyManager.applyHistory(m_applicationContext.getUsername(), fragment, m_graphContainer);

        // This is a hack to fix issue SPC-796 so that the display states of the 
        // TopologyComponent and NoContentAvailableWindow are reset correctly 
        // after a history operation
        graphChanged(m_graphContainer);

        //Manually trigger the searchbox to refresh
        m_searchBox.graphChanged(m_graphContainer);

        m_settingFragment--;
    }

    private void saveHistory() {
        if (m_settingFragment == 0) {
            String fragment = m_historyManager.createHistory(m_applicationContext.getUsername(), m_graphContainer);
            if (getPage() != null) {
                getPage().setUriFragment(fragment, false);
            }
        }
    }

    @Override
    public void graphChanged(GraphContainer graphContainer) {
        // are there any vertices to display?
        boolean verticesAvailable = !graphContainer.getGraph().getDisplayVertices().isEmpty();

        // toggle view
        if (verticesAvailable) {
            m_noContentWindow.setVisible(false);
            removeWindow(m_noContentWindow);
            m_topologyComponent.setEnabled(true);
        } else {
            m_topologyComponent.setEnabled(false);
            m_noContentWindow.setVisible(true);
            if (!m_noContentWindow.isAttached()) {
                addWindow(m_noContentWindow);
            }

        }

        m_zoomLevelLabel.setValue(String.valueOf(graphContainer.getSemanticZoomLevel()));
        m_szlOutBtn.setEnabled(graphContainer.getSemanticZoomLevel() > 0);
        updateTabVisibility();
        updateTimestamp(System.currentTimeMillis());
        updateMenuItems();

        synchronized (m_currentHudDisplayLock) {
            if (m_currentHudDisplay != null) {
                m_currentHudDisplay.setVertexFocusCount(getFocusVertices(m_graphContainer));
            }
        }
    }

    private int getFocusVertices(GraphContainer graphContainer) {
        int count = 0;
        Criteria[] crits = graphContainer.getCriteria();
        for (Criteria criteria : crits) {
            try {
                VertexHopCriteria catCrit = (VertexHopCriteria) criteria;
                count += catCrit.getVertices().size();
            } catch (ClassCastException e) {
            }

        }
        return count;
    }

    private void setSemanticZoomLevel(int semanticZoomLevel) {
        m_zoomLevelLabel.setValue(String.valueOf(semanticZoomLevel));
        m_szlOutBtn.setEnabled(semanticZoomLevel > 0);
        m_graphContainer.setSemanticZoomLevel(semanticZoomLevel);
        m_graphContainer.redoLayout();
    }

    @Override
    public void boundingBoxChanged(MapViewManager viewManager) {
        saveHistory();
    }

    @Override
    public void onVertexUpdate() {
        saveHistory();
    }

    public void setHeaderProvider(OnmsHeaderProvider headerProvider) {
        m_headerProvider = headerProvider;
    }

    /**
     * Parameter is a String because config has String values
     * @param boolVal
     */
    //@Override
    public void setShowHeader(String boolVal) {
        m_showHeader = Boolean.valueOf(boolVal);
    }

    @Override
    public void selectionChanged(SelectionContext selectionContext) {
        synchronized (m_currentHudDisplayLock) {
            if (m_currentHudDisplay != null) {
                m_currentHudDisplay.setVertexSelectionCount(selectionContext.getSelectedVertexRefs().size());
                m_currentHudDisplay.setEdgeSelectionCount(selectionContext.getSelectedEdgeRefs().size());
            }
        }

        //After selection always set the pantool back to active tool
        if (m_panBtn != null && !m_panBtn.getStyleName().equals("toolbar-button down")) {
            m_panBtn.setStyleName("toolbar-button down");
        }
        if (m_selectBtn != null && m_selectBtn.getStyleName().equals("toolbar-button down")) {
            m_selectBtn.setStyleName("toolbar-button");
        }
        if (m_topologyComponent != null)
            m_topologyComponent.setActiveTool("pan");
        saveHistory();
    }

    @Override
    public void detach() {
        m_commandManager.removeCommandUpdateListener(this);
        m_commandManager.removeMenuItemUpdateListener(this);
        super.detach(); //To change body of overridden methods use File | Settings | File Templates.
    }

    public void setServiceManager(BundleContext bundleContext) {
        this.m_serviceManager = new OnmsServiceManagerLocator().lookup(bundleContext);
        this.m_bundlecontext = bundleContext;
    }

    public VaadinApplicationContext getApplicationContext() {
        return m_applicationContext;
    }

    @Override
    @EventConsumer
    public void verticesUpdated(VerticesUpdateManager.VerticesUpdateEvent event) {
        Collection<VertexRef> selectedVertexRefs = m_selectionManager.getSelectedVertexRefs();
        Set<VertexRef> vertexRefs = event.getVertexRefs();
        if (!selectedVertexRefs.equals(vertexRefs) && !event.allVerticesSelected()) {
            m_selectionManager.setSelectedVertexRefs(vertexRefs);
        }
    }
}