/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.gui.communication.views; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MenuDetectEvent; import org.eclipse.swt.events.MenuDetectListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IViewSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.ViewPart; import de.rcenvironment.core.communication.common.InstanceNodeSessionId; import de.rcenvironment.core.communication.common.NetworkGraph; import de.rcenvironment.core.communication.connection.api.ConnectionSetup; import de.rcenvironment.core.communication.connection.api.ConnectionSetupListener; import de.rcenvironment.core.communication.connection.api.ConnectionSetupListenerAdapter; import de.rcenvironment.core.communication.connection.api.ConnectionSetupService; import de.rcenvironment.core.communication.connection.api.ConnectionSetupState; import de.rcenvironment.core.communication.nodeproperties.spi.NodePropertiesChangeListener; import de.rcenvironment.core.communication.nodeproperties.spi.NodePropertiesChangeListenerAdapter; import de.rcenvironment.core.communication.routing.NetworkRoutingService; import de.rcenvironment.core.communication.spi.NetworkTopologyChangeListener; import de.rcenvironment.core.communication.spi.NetworkTopologyChangeListenerAdapter; import de.rcenvironment.core.component.api.DistributedComponentKnowledge; import de.rcenvironment.core.component.spi.DistributedComponentKnowledgeListener; import de.rcenvironment.core.gui.communication.views.contributors.ConnectionSetupsListContributor; import de.rcenvironment.core.gui.communication.views.contributors.InstanceComponentsInfoContributor; import de.rcenvironment.core.gui.communication.views.contributors.MonitoringDataContributor; import de.rcenvironment.core.gui.communication.views.contributors.SshConnectionSetupsListContributor; import de.rcenvironment.core.gui.communication.views.internal.AnchorPoints; import de.rcenvironment.core.gui.communication.views.model.NetworkGraphNodeWithContext; import de.rcenvironment.core.gui.communication.views.model.NetworkGraphNodeWithContext.Context; import de.rcenvironment.core.gui.communication.views.model.NetworkViewModel; import de.rcenvironment.core.gui.communication.views.spi.NetworkViewContributor; import de.rcenvironment.core.gui.communication.views.spi.StandardUserNodeActionNode; import de.rcenvironment.core.gui.communication.views.spi.StandardUserNodeActionType; import de.rcenvironment.core.gui.resources.api.ImageManager; import de.rcenvironment.core.gui.resources.api.StandardImages; import de.rcenvironment.core.gui.utils.common.ClipboardHelper; import de.rcenvironment.core.monitoring.system.api.model.FullSystemAndProcessDataSnapshot; import de.rcenvironment.core.utils.incubator.ServiceRegistry; import de.rcenvironment.core.utils.incubator.ServiceRegistryPublisherAccess; /** * A view that shows a tree of all known network nodes and connections. * * @author Sascha Zur * @author Robert Mischke * @author Oliver Seebach * @author David Scholz * @author Brigitte Boden */ public class NetworkView extends ViewPart { /** * The ID of the view as specified by the extension. */ public static final String ID = "de.rcenvironment.core.gui.communication.views.NetworkView"; private static final ImageDescriptor ADD = ImageDescriptor.createFromURL(NetworkView.class.getResource("/resources/icons/newConnection.png")); private static final ImageDescriptor ADDSSH = ImageDescriptor.createFromURL(NetworkView.class.getResource("/resources/icons/newSshConnection.png")); private static final ImageDescriptor EDIT = ImageDescriptor.createFromURL(NetworkView.class.getResource("/resources/icons/edit16.gif")); private static final ImageDescriptor START = ImageDescriptor.createFromURL(NetworkView.class.getResource("/resources/icons/run_enabled.gif")); private static final ImageDescriptor STOP = ImageDescriptor.createFromURL(NetworkView.class.getResource("/resources/icons/cancel_enabled.gif")); private static final ImageDescriptor COPY = ImageManager.getInstance().getImageDescriptor(StandardImages.COPY_16); private static final ImageDescriptor DELETE = ImageManager.getInstance().getImageDescriptor(StandardImages.DELETE_16); private static final String TAB = "\t"; private Display display; private TreeViewer viewer; private Action toggleNodeIdsVisibleAction; private Action toggleRawNodePropertiesVisibleAction; private Action addNetworkConnectionAction; private Action addSSHConnectionAction; private Action copyToClipBoardAction; private Action editAction; private Action deleteAction; private Action startAction; private Action stopAction; private NetworkViewContentProvider contentProvider; private NetworkViewLabelProvider labelProvider; private boolean rawPropertiesVisible; private final ServiceRegistryPublisherAccess serviceRegistryAccess; private NetworkViewModel model; private ConnectionSetupsListContributor networkConnectionsContributor; private SshConnectionSetupsListContributor sshConnectionsContributor; private List<NetworkViewContributor> allContributors; public NetworkView() { serviceRegistryAccess = ServiceRegistry.createPublisherAccessFor(this); } @Override public void init(IViewSite site) throws PartInitException { super.init(site); labelProvider = new NetworkViewLabelProvider(); labelProvider.setNodeIdsVisible(false); contentProvider = initializeContentProvider(); contentProvider.setRawPropertiesVisible(false); createActions(); } /** A {@link KeyListener} to react on pressedkey's. */ private final class NetworkViewKeyListener extends KeyAdapter { @Override public void keyPressed(KeyEvent event) { if (event.stateMask == SWT.CTRL) { if (event.keyCode == 'c') { copyToClipBoardAction.run(); } } else if (event.keyCode == SWT.DEL) { deleteAction.run(); } } } private void createActions() { // add "show nodeIds" option // TODO add icon toggleNodeIdsVisibleAction = new Action("Show Node Identifiers", SWT.TOGGLE) { @Override public void run() { labelProvider.setNodeIdsVisible(this.isChecked()); // refresh all labels viewer.refresh(AnchorPoints.INSTANCES_PARENT_NODE, true); } }; // add "show raw node properties" option // TODO add icon toggleRawNodePropertiesVisibleAction = new Action("Show Raw Node Properties", SWT.TOGGLE) { @Override public void run() { rawPropertiesVisible = this.isChecked(); contentProvider.setRawPropertiesVisible(rawPropertiesVisible); viewer.refresh(AnchorPoints.INSTANCES_PARENT_NODE, false); } }; // copies the selected raw node properties to the clipboard copyToClipBoardAction = new Action("Copy to Clipboard" + TAB + "Ctrl+C", COPY) { @Override public void run() { IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); if (selection.getFirstElement() instanceof NetworkGraphNodeWithContext) { NetworkGraphNodeWithContext selectionObject = (NetworkGraphNodeWithContext) selection.getFirstElement(); if (selectionObject.getContext() == Context.RAW_NODE_PROPERTY) { // single raw node selected ClipboardHelper.setContent(labelProvider.getText(selectionObject)); } else if (selectionObject.getContext() == Context.RAW_NODE_PROPERTIES_FOLDER) { // all raw nodes folder selected String allRawNodeProperties = "RAW NODE PROPERTIES: \n\n"; Map<InstanceNodeSessionId, Map<String, String>> nodeProperties = model.getNodeProperties(); for (InstanceNodeSessionId identifier : nodeProperties.keySet()) { if (identifier.getAssociatedDisplayName() == selectionObject.getDisplayNameOfNode()) { for (String nodeProperty : nodeProperties.get(identifier).keySet()) { allRawNodeProperties += nodeProperty + ": " + nodeProperties.get(identifier).get(nodeProperty) + "\n"; } } } ClipboardHelper.setContent(allRawNodeProperties); } } } }; // add "add network connection" option addNetworkConnectionAction = new Action("Add Network Connection...", ADD) { @Override public void run() { networkConnectionsContributor.showAddConnectionDialog(); } }; // add "add network connection" option addSSHConnectionAction = new Action("Add SSH Remote Access Connection...", ADDSSH) { @Override public void run() { sshConnectionsContributor.showAddConnectionDialog(); } }; // add "edit network connection" option editAction = new Action("Edit Network Connection...", EDIT) { @Override public void run() { executeStandardUserNodeAction(StandardUserNodeActionType.EDIT); } }; // add "delete network connection" option deleteAction = new Action("Delete Network Connection..." + TAB + "Delete", DELETE) { @Override public void run() { executeStandardUserNodeAction(StandardUserNodeActionType.DELETE); } }; deleteAction.setAccelerator(SWT.DEL); // add "start network connection" option startAction = new Action("Start/Connect", START) { @Override public void run() { executeStandardUserNodeAction(StandardUserNodeActionType.START); } }; // add "stop network connection" option stopAction = new Action("Stop/Disconnect", STOP) { @Override public void run() { executeStandardUserNodeAction(StandardUserNodeActionType.STOP); } }; } @Override public void createPartControl(Composite parent) { // store display reference for asyncExec calls display = parent.getShell().getDisplay(); viewer = new TreeViewer(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL); viewer.setContentProvider(contentProvider); viewer.setLabelProvider(labelProvider); // viewer.setSorter(new NetworkViewSorter()); NetworkGraph initialGraph = serviceRegistryAccess.getService(NetworkRoutingService.class).getReachableNetworkGraph(); Collection<ConnectionSetup> initialConnectionSetups = serviceRegistryAccess.getService(ConnectionSetupService.class).getAllConnectionSetups(); model = new NetworkViewModel(initialGraph, null, initialConnectionSetups, new HashMap<InstanceNodeSessionId, Map<String, String>>(), new ConcurrentHashMap<InstanceNodeSessionId, FullSystemAndProcessDataSnapshot>()); for (NetworkViewContributor contributor : allContributors) { contributor.setCurrentModel(model); contributor.setTreeViewer(viewer); } viewer.setInput(model); viewer.expandToLevel(3); viewer.getControl().addKeyListener(new NetworkViewKeyListener()); // Update context menu on open viewer.getTree().addMenuDetectListener(new MenuDetectListener() { @Override public void menuDetected(MenuDetectEvent event) { updatePossibleActionsForSelection(getSelectedTreeNode()); } }); viewer.getTree().addMouseListener(new MouseListener() { @Override public void mouseUp(MouseEvent event) {} @Override public void mouseDown(MouseEvent event) {} @Override public void mouseDoubleClick(MouseEvent event) { expandSelectedNode(); if (editAction.isEnabled()) { editAction.run(); } } }); viewer.getTree().addKeyListener(new KeyListener() { @Override public void keyPressed(KeyEvent arg0) { if (arg0.keyCode == SWT.CR) { expandSelectedNode(); } } @Override public void keyReleased(KeyEvent arg0) { // TODO Auto-generated method stub } }); hookContextMenu(); addToolbarActions(); registerChangeListeners(); registerSelectionListeners(); } @Override public void dispose() { serviceRegistryAccess.dispose(); for (NetworkViewContributor contributor : allContributors) { contributor.dispose(); } super.dispose(); } /** * Passing the focus request to the viewer's control. */ @Override public void setFocus() { viewer.getControl().setFocus(); } private NetworkViewContentProvider initializeContentProvider() { allContributors = defineContributors(); List<NetworkViewContributor> rootContributors = new ArrayList<>(); List<NetworkViewContributor> instanceDataContributors = new ArrayList<>(); for (NetworkViewContributor contributor : allContributors) { if (contributor.getRootElementsPriority() != 0) { rootContributors.add(contributor); } if (contributor.getInstanceDataElementsPriority() != 0) { instanceDataContributors.add(contributor); } } Collections.sort(rootContributors, new Comparator<NetworkViewContributor>() { @Override public int compare(NetworkViewContributor o1, NetworkViewContributor o2) { return Integer.compare(o1.getRootElementsPriority(), o2.getRootElementsPriority()); } }); Collections.sort(instanceDataContributors, new Comparator<NetworkViewContributor>() { @Override public int compare(NetworkViewContributor o1, NetworkViewContributor o2) { return Integer.compare(o1.getInstanceDataElementsPriority(), o2.getInstanceDataElementsPriority()); } }); return new NetworkViewContentProvider(rootContributors, instanceDataContributors); } private List<NetworkViewContributor> defineContributors() { // add contributors here; their ordering does not matter as long as the priority values are unique List<NetworkViewContributor> result = new ArrayList<>(); // note: saving contributors in a field is usually not required, but we don't go for a full-blown plugin structure here - misc_ro networkConnectionsContributor = new ConnectionSetupsListContributor(); sshConnectionsContributor = new SshConnectionSetupsListContributor(); result.add(networkConnectionsContributor); result.add(new MonitoringDataContributor()); result.add(new InstanceComponentsInfoContributor()); result.add(sshConnectionsContributor); return result; } private void registerSelectionListeners() { disableAllActions(); viewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { Object node = getSelectedTreeNode(); if (node != null) { updatePossibleActionsForSelection(node); } else { disableAllActions(); } } }); } /** * Registers an event listener for network changes as an OSGi service (whiteboard pattern). * * @param display */ private void registerChangeListeners() { serviceRegistryAccess.registerService(NetworkTopologyChangeListener.class, new NetworkTopologyChangeListenerAdapter() { @Override public void onReachableNetworkChanged(final NetworkGraph networkGraph) { display.asyncExec(new Runnable() { @Override public void run() { model.networkGraph = networkGraph; model.updateGraphWithProperties(); if (viewer.getControl().isDisposed()) { return; } // update the tree, but no need to update existing labels viewer.refresh(AnchorPoints.INSTANCES_PARENT_NODE, false); } }); } }); serviceRegistryAccess.registerService(NodePropertiesChangeListener.class, new NodePropertiesChangeListenerAdapter() { @Override public void onNodePropertyMapsOfNodesChanged(final Map<InstanceNodeSessionId, Map<String, String>> updatedPropertyMaps) { display.asyncExec(new Runnable() { @Override public void run() { model.nodeProperties.putAll(updatedPropertyMaps); // inner maps are // immutable model.updateGraphWithProperties(); if (viewer.getControl().isDisposed()) { return; } viewer.refresh(AnchorPoints.INSTANCES_PARENT_NODE); } }); } }); serviceRegistryAccess.registerService(DistributedComponentKnowledgeListener.class, new DistributedComponentKnowledgeListener() { @Override public void onDistributedComponentKnowledgeChanged(final DistributedComponentKnowledge newKnowledge) { display.asyncExec(new Runnable() { @Override public void run() { model.componentKnowledge = newKnowledge; if (viewer.getControl().isDisposed()) { return; } viewer.refresh(AnchorPoints.INSTANCES_PARENT_NODE); } }); } }); // TODO move this listener into the contributor for better separation serviceRegistryAccess.registerService(ConnectionSetupListener.class, new ConnectionSetupListenerAdapter() { @Override public void onCollectionChanged(final Collection<ConnectionSetup> setups) { display.asyncExec(new Runnable() { @Override public void run() { model.connectionSetups = setups; if (viewer.getControl().isDisposed()) { return; } // no need to update all labels viewer.refresh(networkConnectionsContributor.getFullRefreshRootElement(), false); viewer.setExpandedState(networkConnectionsContributor.getRootElementToExpand(), true); } }); } @Override public void onStateChanged(final ConnectionSetup setup, final ConnectionSetupState oldState, ConnectionSetupState newState) { // trigger GUI refresh display.asyncExec(new Runnable() { @Override public void run() { if (viewer.getControl().isDisposed()) { return; } // only update this specific label Object node = networkConnectionsContributor.getTreeNodeForSetup(setup); if (node != null) { if (node.equals(getSelectedTreeNode())) { updatePossibleActionsForSelection(node); } viewer.update(node, null); } } }); } }); } private Object getSelectedTreeNode() { ISelection rawSelection = viewer.getSelection(); if (rawSelection != null && rawSelection instanceof TreeSelection) { TreeSelection selection = ((TreeSelection) rawSelection); return selection.getFirstElement(); } return null; } private void updatePossibleActionsForSelection(Object node) { if (node == null) { disableAllActions(); return; } if (node instanceof StandardUserNodeActionNode) { StandardUserNodeActionNode typedNode = (StandardUserNodeActionNode) node; startAction.setEnabled(typedNode.isActionApplicable(StandardUserNodeActionType.START)); stopAction.setEnabled(typedNode.isActionApplicable(StandardUserNodeActionType.STOP)); editAction.setEnabled(typedNode.isActionApplicable(StandardUserNodeActionType.EDIT)); deleteAction.setEnabled(typedNode.isActionApplicable(StandardUserNodeActionType.DELETE)); copyToClipBoardAction.setEnabled(typedNode.isActionApplicable(StandardUserNodeActionType.COPY_TO_CLIPBOARD)); } else if (node instanceof NetworkGraphNodeWithContext) { // TODO special handling; migrate disableAllActions(); NetworkGraphNodeWithContext typedNode = (NetworkGraphNodeWithContext) node; if (typedNode.getContext() == Context.RAW_NODE_PROPERTY || typedNode.getContext() == Context.RAW_NODE_PROPERTIES_FOLDER) { copyToClipBoardAction.setEnabled(true); } } else { disableAllActions(); } } private void disableAllActions() { editAction.setEnabled(false); deleteAction.setEnabled(false); startAction.setEnabled(false); stopAction.setEnabled(false); copyToClipBoardAction.setEnabled(false); } private void executeStandardUserNodeAction(StandardUserNodeActionType actionType) { Object node = getSelectedTreeNode(); if (node instanceof StandardUserNodeActionNode) { StandardUserNodeActionNode typedNode = (StandardUserNodeActionNode) node; if (typedNode.isActionApplicable(actionType)) { // execute typedNode.performAction(actionType); } else { // the action was enabled when it shouldn't be anymore, so update all action states - misc_ro updatePossibleActionsForSelection(typedNode); } } } private void addToolbarActions() { // add toolbar actions (right top of view) final IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager(); toolBarManager.add(addNetworkConnectionAction); toolBarManager.add(addSSHConnectionAction); toolBarManager.add(new Separator()); toolBarManager.add(startAction); toolBarManager.add(stopAction); toolBarManager.add(editAction); toolBarManager.add(deleteAction); } private void hookContextMenu() { // submenu final MenuManager subMenuManager = new MenuManager("Advanced"); subMenuManager.add(toggleNodeIdsVisibleAction); subMenuManager.add(toggleRawNodePropertiesVisibleAction); final MenuManager menuManager = new MenuManager(); menuManager.add(addNetworkConnectionAction); menuManager.add(addSSHConnectionAction); menuManager.add(new Separator()); menuManager.add(startAction); menuManager.add(stopAction); menuManager.add(editAction); menuManager.add(deleteAction); menuManager.add(new Separator()); menuManager.add(copyToClipBoardAction); menuManager.add(new Separator()); menuManager.add(subMenuManager); // "Advanced" menuManager.updateAll(true); final Menu menu = menuManager.createContextMenu(viewer.getTree()); viewer.getTree().setMenu(menu); getSite().registerContextMenu(menuManager, viewer); getSite().setSelectionProvider(viewer); } /** * Actually expands if tree node is not expanded, collapses otherwise. * */ private void expandSelectedNode() { Object selectedTreeNode = getSelectedTreeNode(); if (selectedTreeNode == null) { return; } if (viewer.getExpandedState(selectedTreeNode)) { viewer.collapseToLevel(selectedTreeNode, 1); } else { viewer.expandToLevel(selectedTreeNode, 1); } } }