/* * (C) Copyright IBM Corp. 2009 * * LICENSE: Eclipse Public License v1.0 * http://www.eclipse.org/legal/epl-v10.html */ package com.ibm.gaiandb.apps.dashboard; import java.net.InetAddress; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; import javax.swing.border.Border; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class TopologyTab extends UpdatingTab implements ActionListener { // Use PROPRIETARY notice if class contains a main() method, otherwise use COPYRIGHT notice. public static final String COPYRIGHT_NOTICE = "(c) Copyright IBM Corp. 2009"; private static final long serialVersionUID = -9067938005215536453L; private static final Logger LOGGER = Logger.getLogger(TopologyTab.class.getName()); private static final int GRAPH_INTERVAL = 10000; // private static final int VALUE_INTERVAL = 1000; private static final int LABEL_INTERVAL = 100; private static final int MAX_AGE = 5; private String tableToQuery = null; private JComboBox<String> comboLogicalTables = new JComboBox<String>(new String[] {"Select target..."}); private static final String GRAPH_SQL = " SELECT gdbx_from_node source, gdbx_to_node target" + " FROM new com.ibm.db2j.GaianTable('gdb_ltnull', 'explainds') T WHERE gdbx_depth > 0"; // " ORDER BY source, target"; /* Alternative query - this shows the paths to all nodes, not just those returning data. SELECT DISTINCT gdbx_from_node source, gdbx_to_node target, gdbx_precedence, gdbx_depth FROM new com.ibm.db2j.GaianTable('lt0', 'explain') T WHERE gdbx_depth > 0 order by gdbx_precedence, gdbx_depth */ // TODO : SQL statement to get Path ArrayList<ArrayList<String>> queryPaths = new ArrayList<ArrayList<String>>(); private final ActionListener monitorSwitchActionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { JComboBox<String> cb = (JComboBox<String>) event.getSource(); if ( null == cb ) return; // String command = event.getActionCommand(); String command = "" + cb.getSelectedIndex(); // System.out.println("Action Performed on Monitor Selection Dropdown for Colour Coding Nodes in Topology Graph. Command = " + command); synchronized (this) { if (null == command || command.equals(LABEL_NONE)) { valueProcessor.clearMonitors(); graph.setMonitor(null); for ( int i=0; i<NUM_RANGE_COLORS; i++ ) { ((JLabel)colorRangePanel.getComponent(i)).setText(""); } } else { try { MonitorInfo selectedMonitor = MetricValueProcessor.MONITORS[Integer.parseInt(command)]; valueProcessor.setMonitors(new MonitorInfo[] {selectedMonitor}); graph.setMonitor(selectedMonitor); Color[] monitorColorRange = selectedMonitor.getColorRange(NUM_RANGE_COLORS); for ( int i=0; i<NUM_RANGE_COLORS; i++ ) { colorRangePanel.getComponent(i).setBackground(monitorColorRange[i]); ((JLabel)colorRangePanel.getComponent(i)).setText(EMPTY_COLOR_FIELD); } ((JLabel)colorRangePanel.getComponent(0)).setText(""+selectedMonitor.minBound); ((JLabel)colorRangePanel.getComponent(NUM_RANGE_COLORS-1)).setText(""+selectedMonitor.maxBound); ((JLabel)colorRangePanel.getComponent(NUM_RANGE_COLORS/2)).setText(selectedMonitor.unit); ((JLabel)colorRangePanel.getComponent(NUM_RANGE_COLORS/2)).setForeground(Color.BLACK); } catch (Exception e) { valueProcessor.clearMonitors(); graph.setMonitor(null); } } updateGraph(); } } }; private final ActionListener discoveryModeButtonsActionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { if ( null == subnetsChks ) return; // String command = event.getActionCommand(); // System.out.println("Action Performed on Discovery options - Radio Button = " + command); synchronized (this) { if ( radioButtonDiscoveryOff.isSelected() ) { setGaianConfigProperty( DISCOVERY_IP, "" ); multicastGroupPanel.setVisible(false); discoveryGatewaysTextField.setEnabled(false); for ( JCheckBox chk : subnetsChks ) chk.setEnabled(false); return; } boolean isMulticast = radioButtonDiscoveryMulticast.isSelected(); String discoveryIP; if ( isMulticast ) { if ( 1 > multicastGroupTextField.getText().length() ) multicastGroupTextField.setText(DEFAULT_MULTICAST_GROUP); discoveryIP = multicastGroupTextField.getText(); if ( discoveryIP.equals( DEFAULT_MULTICAST_GROUP ) ) discoveryIP = null; // equivalent to the default } else { // System.out.println("broadcastTargets: " + broadcastTargets + ", allBroadcastOptions: " + allBroadcastOptions); int numBroadcasts = broadcastTargets.size(); if ( allBroadcastOptions.size() == numBroadcasts ) discoveryIP = BROADCAST_ALL; else if ( 1 > numBroadcasts ) discoveryIP = "UNICAST"; else { discoveryIP = broadcastTargets.toString(); discoveryIP = discoveryIP.substring(1, discoveryIP.length()-1).trim(); // remove wrapping square brackets } } // System.out.println("setGaianConfigProperty("+DISCOVERY_IP+","+discoveryIP+")"); setGaianConfigProperty( DISCOVERY_IP, discoveryIP ); multicastGroupPanel.setVisible( isMulticast ); discoveryGatewaysTextField.setEnabled(true); // // Get some info to log to System.out // int i = 0; // String[] netInfoNeeded = new String[netInfos.size()]; // for ( String[] ni : netInfos ) netInfoNeeded[i++] = ni[ isMulticast ? IDX_NETIP : IDX_NETBCAST ]; // System.out.println("Updating checkboxes for new selection: " + (isMulticast ? multicastTargets : broadcastTargets) // + " - using netInfos: " + Arrays.asList( netInfoNeeded ) ); // Update text in checkboxes and also their selections - based on discovery mode and IPs selections // Note multicastTargets/broadcastTargets are the loaded user selections; netInfos holds the info associated with all the checkboxes int i = 0; Set<String> selectedNetworks = isMulticast ? multicastTargets : broadcastTargets; JCheckBox chkPrevious = null; for ( JCheckBox chk : subnetsChks ) { // Select new relevant checkboxes String[] netInfo = netInfos.get(i++); String netInfoIP = netInfo[ isMulticast ? IDX_NETIP : IDX_NETBCAST ]; boolean isAvailable = null != netInfoIP; chk.setText( netInfo[ IDX_NETNAME ] + ": " + ( isAvailable ? netInfoIP : "unavailable" ) ); boolean isVisible = null == chkPrevious || false == chk.getText().equals(chkPrevious.getText()); chk.setVisible( isVisible ); if ( false == isVisible ) continue; chkPrevious = chk; chk.setEnabled( isAvailable ); chk.setSelected( isAvailable ? selectedNetworks.contains( netInfoIP ) : false ); } } } }; private synchronized void setGaianConfigProperty( final String dprop, final String dval ) { try { if ( null == setGaianConfigPropertyStatement || setGaianConfigPropertyStatement.isClosed() ) setGaianConfigPropertyStatement = conn.prepareStatement( SET_GAIAN_CONFIG_PROPERTY ); setGaianConfigPropertyStatement.setString(1, dprop); setGaianConfigPropertyStatement.setString(2, dval); setGaianConfigPropertyStatement.execute(); } catch ( Exception e ) { System.out.println("Unable to setGaianConfigProperty(), Exception: " + e); e.printStackTrace(); } } private static final String SET_GAIAN_CONFIG_PROPERTY = "call setconfigproperty(?, ?)"; private static final String LIST_NETINFO = "call listnet('')"; private static final String ALL = "ALL", BROADCAST_ALL = "BROADCAST_ALL"; private static final String OffButtonLabel = "Off", MulticastButtonLabel = "Multicast", BroadcastButtonLabel = "Broadcast"; private static final String DEFAULT_MULTICAST_GROUP = com.ibm.gaiandb.GaianNodeSeeker.DEFAULT_MULTICAST_GROUP_IP; // compile-time link only private static final String DISCOVERY_IP = "DISCOVERY_IP"; private static final String MULTICAST_INTERFACES = "MULTICAST_INTERFACES"; private static final String DISCOVERY_GATEWAYS = "DISCOVERY_GATEWAYS"; private static final String ACCESS_CLUSTERS = "ACCESS_CLUSTERS"; private static final String GET_DISCOVERY_PROPS = "" + "select discovery_prop, discovery_val from (" // Note the '=' char prefix in "=DISCOVERY_IP" or "=MULTICAST_INTERFACES" makes the function resolve a default value if the property is not set + " select '"+DISCOVERY_IP+"' DISCOVERY_PROP, getconfigproperty('="+DISCOVERY_IP+"') DISCOVERY_VAL from sysibm.sysdummy1 union all" + " select '"+MULTICAST_INTERFACES+"' DISCOVERY_PROP, getconfigproperty('="+MULTICAST_INTERFACES+"') DISCOVERY_VAL from sysibm.sysdummy1 union all" + " select '"+DISCOVERY_GATEWAYS+"' DISCOVERY_PROP, getconfigproperty('"+DISCOVERY_GATEWAYS+"') DISCOVERY_VAL from sysibm.sysdummy1 union all" + " select '"+ACCESS_CLUSTERS+"' DISCOVERY_PROP, getconfigproperty('"+ACCESS_CLUSTERS+"') DISCOVERY_VAL from sysibm.sysdummy1" + ") sq" ; // select discovery_prop, case when discovery_val is null then 'null' else discovery_val end discovery_val from ( // select 'DISCOVERY_IP' DISCOVERY_PROP, getconfigproperty('=DISCOVERY_IP') DISCOVERY_VAL from sysibm.sysdummy1 union all // select 'MULTICAST_INTERFACES' DISCOVERY_PROP, getconfigproperty('=MULTICAST_INTERFACES') DISCOVERY_VAL from sysibm.sysdummy1 union all // select 'DISCOVERY_GATEWAYS' DISCOVERY_PROP, getconfigproperty('DISCOVERY_GATEWAYS') DISCOVERY_VAL from sysibm.sysdummy1 union all // select 'ACCESS_CLUSTERS' DISCOVERY_PROP, getconfigproperty('ACCESS_CLUSTERS') DISCOVERY_VAL from sysibm.sysdummy1 // ) sq private JRadioButton radioButtonDiscoveryOff = null, radioButtonDiscoveryBroadcast = null, radioButtonDiscoveryMulticast = null; private JPanel subnetsPanel = null; private JCheckBox[] subnetsChks = null; private JPanel multicastGroupPanel = null; private JTextField multicastGroupTextField = null; private JTextField discoveryGatewaysTextField = null; private JTextField clusterIDsTextField = null; private final int IDX_NETNAME = 0, IDX_NETIP = 1, IDX_NETBCAST = 2; private List<String[]> netInfos = null; private Set<String> netInfoSet = null; private Set<String> broadcastTargets = null, multicastTargets = null; private Set<String> allBroadcastOptions = new HashSet<String>(); private PreparedStatement listNetInfoStatement = null, getDiscoveryConfigStatement = null, setGaianConfigPropertyStatement = null; /** * Load any changes to discovery properties, based also on any changes in network interfaces info. * Note: If the GaianDB config is simultaneously updated by some other process, then user choices in the UI may be overwritten * * @return true if check/load completed without errors - false otherwise. */ private synchronized boolean loadDiscoveryConfig() { try { // Check current list of ips and their associated broadcast ips + load any new ones. if ( null == listNetInfoStatement || listNetInfoStatement.isClosed() ) listNetInfoStatement = conn.prepareStatement( LIST_NETINFO ); Set<String> netInfoSetNew = new HashSet<String>(); List<String[]> netInfosNew = new ArrayList<String[]>(); // New set of selected target networks (for multicast or broadcast) Set<String> broadcastTargetsNew = new HashSet<String>(), multicastTargetsNew = new HashSet<String>(); ResultSet rs = listNetInfoStatement.executeQuery(); while ( rs.next() ) { String ifaceName = rs.getString("INTERFACE").trim(), ifaceDesc = rs.getString("DESCRIPTION").trim(), ipv4 = rs.getString("IPV4").trim(), ipv4Broadcast = rs.getString("BROADCAST"); multicastTargetsNew.add(ipv4); if ( null != ipv4Broadcast ) { broadcastTargetsNew.add( ipv4Broadcast = ipv4Broadcast.trim() ); } // System.out.println("listNet entry: ifaceName: " + ifaceName + ", ipv4: " + ipv4 + ", broadcast: " + ipv4Broadcast); netInfoSetNew.add( ifaceName + " " + ipv4 + " " + ipv4Broadcast ); netInfosNew.add( new String[] { ifaceName, ipv4, ipv4Broadcast } ); // IDX_NETNAME = 0, IDX_NETIP = 1, IDX_NETBCAST = 2 } rs.close(); allBroadcastOptions.clear(); allBroadcastOptions.addAll( broadcastTargetsNew ); // System.out.println("netInfoSet: " + netInfoSet + " -> " + netInfoSetNew); boolean netInfoSetChanged = false == netInfoSetNew.equals( netInfoSet ); if ( netInfoSetChanged ) { if (null!=netInfoSet) netInfoSet.clear(); netInfoSet = netInfoSetNew; if (null!=netInfos) netInfos.clear(); netInfos = netInfosNew; } // Check discovery config properties + load any changes if ( null == getDiscoveryConfigStatement || getDiscoveryConfigStatement.isClosed() ) getDiscoveryConfigStatement = conn.prepareStatement( GET_DISCOVERY_PROPS ); boolean isMulticastPropVal = false, isOff = false; rs = getDiscoveryConfigStatement.executeQuery(); while ( rs.next() ) { String dprop = rs.getString(1).trim(); String dval = rs.getString(2); // System.out.println("Discovery property entry dprop: " + dprop + ", dval: " + dval + ", null==dval ? " + (null==dval)); if ( DISCOVERY_IP.equalsIgnoreCase(dprop) ) { if ( 1 > dval.length() ) isOff = true; // switch discovery off else if ( 0 > dval.indexOf(',') && -1 < dval.indexOf('.') && InetAddress.getByName(dval).isMulticastAddress() ) isMulticastPropVal = true; if ( netInfoSetChanged || null == subnetsChks ) { int netInfoIdxForSubnetDescriptors = isMulticastPropVal ? IDX_NETIP : IDX_NETBCAST; // index of ipv4 value or broadcast value // System.out.println("Setting checkbox values with info from each netInfo[" + netInfoIdxForSubnetDescriptors + "]"); // Set relevant info in subnets scroller box - either the ipv4 for Multicast or the ipv4Broadcast for Broadcast int i = 0; subnetsChks = new JCheckBox[netInfos.size()]; for ( String[] netInfo : netInfos ) { String netInfoIP = netInfo[ netInfoIdxForSubnetDescriptors ]; subnetsChks[i++] = new JCheckBox( netInfo[IDX_NETNAME] + ": " + ( null == netInfoIP ? "unavailable" : netInfoIP ) ); } subnetsPanel.removeAll(); JCheckBox chkPrevious = null; for ( JCheckBox chk : subnetsChks ) { chk.setVisible( null == chkPrevious || false == chk.getText().equals(chkPrevious.getText()) ); subnetsPanel.add( chkPrevious = chk, cGoDown ); chk.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { // System.out.println("CheckBox state changed for: " + ((JCheckBox) e.getSource()).getText()); // radio button selected must be either multicast or broadcast - NOT OFF, because check boxes would be disabled. final boolean isMulticast = radioButtonDiscoveryMulticast.isSelected(); int i = 0; List<String> targetIPs = new ArrayList<String>(); for ( JCheckBox chk : subnetsChks ) { if ( chk.isVisible() && chk.isEnabled() && chk.isSelected() ) targetIPs.add( netInfos.get(i)[ isMulticast ? IDX_NETIP : IDX_NETBCAST ] ); i++; } if ( false == isMulticast && targetIPs.isEmpty() ) { setGaianConfigProperty( DISCOVERY_IP, "UNICAST" ); // Only discovery gateways can discovery return; } // System.out.println("targetIPs: " + targetIPs + ", allBroadcastOptions: " + allBroadcastOptions); final boolean isAllSelected = targetIPs.size() == ( isMulticast ? netInfos : allBroadcastOptions ).size(); if ( isAllSelected ) setGaianConfigProperty( isMulticast ? MULTICAST_INTERFACES : DISCOVERY_IP, isMulticast ? ALL : BROADCAST_ALL ); else { String targetsCSV = targetIPs.toString(); targetsCSV = targetsCSV.substring(1, targetsCSV.length()-1).trim(); // remove wrapping square brackets setGaianConfigProperty( isMulticast ? MULTICAST_INTERFACES : DISCOVERY_IP, targetsCSV ); } } }); } // Empty panel to keep the checkboxes at the top subnetsPanel.add(new JPanel(), cExpandDown); } if ( isOff ) continue; if ( isMulticastPropVal ) { // Overwrite GroupIP text field - but only if the value is different and the focus is not on it. if ( false == multicastGroupTextField.getText().equals(dval) && false == multicastGroupTextField.isFocusOwner() ) multicastGroupTextField.setText( dval ); } else if ( false == BROADCAST_ALL.equals(dval) ) broadcastTargetsNew.retainAll( Arrays.asList(splitByCommas(dval)) ); // remove elements that aren't listed } else if ( MULTICAST_INTERFACES.equalsIgnoreCase(dprop) && false == ALL.equals(dval) ) multicastTargetsNew.retainAll( Arrays.asList(splitByCommas(dval)) ); // remove elements that aren't listed else if ( DISCOVERY_GATEWAYS.equalsIgnoreCase(dprop) ) { if ( false == discoveryGatewaysTextField.getText().equals(dval) && false == discoveryGatewaysTextField.isFocusOwner() ) discoveryGatewaysTextField.setText( null == dval ? "" : dval ); } else if ( ACCESS_CLUSTERS.equalsIgnoreCase(dprop) ) if ( false == clusterIDsTextField.getText().equals(dval) && false == clusterIDsTextField.isFocusOwner() ) clusterIDsTextField.setText( null == dval ? "" : dval ); } rs.close(); boolean isCheckBoxSelectionsChanged = false; // Only update the target (i.e. selected) multicast IPs if the selection changed if ( false == multicastTargetsNew.equals( multicastTargets ) ) { isCheckBoxSelectionsChanged = true; if (null!=multicastTargets) multicastTargets.clear(); multicastTargets = multicastTargetsNew; } // Only update the target (i.e. selected) broadcast IPs if the selection changed if ( false == broadcastTargetsNew.equals( broadcastTargets ) ) { isCheckBoxSelectionsChanged = true; if (null!=broadcastTargets) broadcastTargets.clear(); broadcastTargets = broadcastTargetsNew; } // Update netInfo list and selections as appropriate - i.e. if discovery mode changed or if target nets changed final JRadioButton radioButtonToBeSelected = isOff ? radioButtonDiscoveryOff : isMulticastPropVal ? radioButtonDiscoveryMulticast : radioButtonDiscoveryBroadcast; if ( false == radioButtonToBeSelected.isSelected() || isCheckBoxSelectionsChanged ) { radioButtonToBeSelected.doClick(); // Refresh target networks selections monitorPanel.validate(); // Validate layout - this clears issue whereby a QueryTab re-connect makes the checkboxes disappear } } catch ( Exception e ) { System.out.println("Unable to loadDiscoveryConfig(), Exception: " + e); e.printStackTrace(); return false; } return true; } private static String[] splitByCommas( String list ) { return splitByTrimmedDelimiter( list, ',' ); } private static String[] splitByTrimmedDelimiter( String list, char delimiter ) { if ( null == list || 0 == list.length() ) return new String[0]; return list.trim().split("[\\s]*" + delimiter + "[\\s]*"); } private JPanel monitorPanel = null; private JLabel nodeInfo = null; private TopologyGraph graph = null; // private JPanel graphPanel = null; private Updater graphUpdater = null, colourUpdater; private PreparedStatement graphStatement; private MetricValueProcessor valueProcessor; private JPanel colorRangePanel = null; private static final int NUM_RANGE_COLORS=10; private static final String EMPTY_COLOR_FIELD = " "; private static final String LABEL_NONE = "None"; private String clickedNode = null; private String localNodeID = null; private ConfigurationDialog configurationDialog = null; private static final String USR_LABEL = "Username"; private static final String PWD_LABEL = "Password"; private static final String[] textFieldLabels = new String[] { USR_LABEL, PWD_LABEL }; public TopologyTab(Dashboard container) { super(container, new BorderLayout(Dashboard.BORDER_SIZE, Dashboard.BORDER_SIZE)); } private GridBagConstraints cDefault = null, cGoDown = null, cExpandDown = null; protected void create() { Border topBorderPadding = BorderFactory.createEmptyBorder(Dashboard.BORDER_SIZE, 0, 0, 0); Border bottomBorderPadding = BorderFactory.createEmptyBorder(0, 0, Dashboard.BORDER_SIZE, 0); Border topAndBottomBorderPadding = BorderFactory.createEmptyBorder(Dashboard.BORDER_SIZE, 0, Dashboard.BORDER_SIZE, 0); cDefault = new GridBagConstraints(); cDefault.weightx = 1; cDefault.fill = GridBagConstraints.BOTH; cGoDown = (GridBagConstraints) cDefault.clone(); cGoDown.gridx = 0; cExpandDown = (GridBagConstraints) cGoDown.clone(); cExpandDown.weighty = 1; monitorPanel = new JPanel(new GridBagLayout()); ////////////////////// // Show Nodes checkbox ////////////////////// JCheckBox showNames = new JCheckBox("Show Node IDs", true); showNames.setBorder(bottomBorderPadding); showNames.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (null != graph) { graph.setNodeRenderer(e.getStateChange() == ItemEvent.SELECTED); } } }); monitorPanel.add(showNames, cDefault); /////////////////////////////////////////////////////// // Coloring criteria drop-down list and color range key /////////////////////////////////////////////////////// monitorPanel.add(new JLabel("Node Coloring Scheme"), cGoDown); JComboBox<String> metricsDropDown = new JComboBox<String>(); for (int i = 0; i < MetricValueProcessor.MONITORS.length; i++) { metricsDropDown.addItem( MetricValueProcessor.MONITORS[i].name ); metricsDropDown.addActionListener(monitorSwitchActionListener); } monitorPanel.add(metricsDropDown, cGoDown); // ButtonGroup monitorGroup = new ButtonGroup(); //// JRadioButton none = new JRadioButton(LABEL_NONE); //// none.setActionCommand(null); //// none.addActionListener(MONITOR_SWITCH); //// none.setSelected(true); //// monitorGroup.add(none); //// monitorPanel.add(none, cGoDown); // for (int i = 0; i < MetricValueProcessor.MONITORS.length; i++) { // JRadioButton monitorSelector = new JRadioButton(MetricValueProcessor.MONITORS[i].name); // monitorSelector.addActionListener(monitorSwitchActionListener); // monitorSelector.setActionCommand(Integer.toString(i)); // monitorGroup.add(monitorSelector); // monitorPanel.add(monitorSelector, cGoDown); // } colorRangePanel = new JPanel(new GridBagLayout()); colorRangePanel.setBorder(topBorderPadding); for ( int i=0; i<NUM_RANGE_COLORS; i++ ) { JLabel jl = new JLabel(EMPTY_COLOR_FIELD, SwingConstants.CENTER); Font f = jl.getFont(); jl.setFont( f.deriveFont(f.getStyle()^Font.BOLD) ); jl.setOpaque(true); jl.setForeground(Color.WHITE); colorRangePanel.add(jl, cDefault); } monitorPanel.add(colorRangePanel, cGoDown); //////////////////////////////// // Logical tables drop down list //////////////////////////////// comboLogicalTables.addActionListener( new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { comboTableActionPerformed(evt); } private void comboTableActionPerformed(ActionEvent evt) { comboLogicalTables.transferFocus(); // allows us to update the list more readily if necessary JComboBox<String> cb = (JComboBox<String>)evt.getSource(); if ( null == cb ) return; tableToQuery = (String)cb.getSelectedItem(); // System.out.println("comboTableActionPerformed event selection " + tableToQuery); if ( 0 == cb.getSelectedIndex() || null != tableToQuery && 0 == tableToQuery.trim().length() ) tableToQuery = null; updateGraph(); } }); comboLogicalTables.addFocusListener( new java.awt.event.FocusListener() { @Override public void focusGained(FocusEvent e) { // System.out.println("Updating from focusGained"); try { updateLogicalTablesComboBox(); } catch (SQLException ex) {} } @Override public void focusLost(FocusEvent e) {} }); // this doesn't trigger when the item is selected some other way (e.g. through track pad or keyboard..) // comboLogicalTables.addMouseListener( new java.awt.event.MouseListener() { // // @Override // public void mouseClicked(MouseEvent e) { // System.out.println("Updating from MouseClicked"); // try { updateLogicalTablesComboBox(); } // catch (SQLException ex) {} // } // // @Override public void mouseEntered(MouseEvent e) {} // @Override public void mouseExited(MouseEvent e) {} // @Override public void mousePressed(MouseEvent e) {} // @Override public void mouseReleased(MouseEvent e) {} // }); comboLogicalTables.setMaximumRowCount(20); JPanel pathTablePanel = new JPanel(new GridBagLayout()); pathTablePanel.setBorder(topAndBottomBorderPadding); pathTablePanel.add( new JLabel("Show Logical Table Paths"), cGoDown); // for Path Visualisation") , cGoDown); pathTablePanel.add(comboLogicalTables, cGoDown); monitorPanel.add(pathTablePanel, cGoDown); // = Label and drop-down for choosing a logical table for path visualisation monitorPanel.add(new JSeparator(SwingConstants.HORIZONTAL), cGoDown); ////////////// // Cluster IDs ////////////// monitorPanel.add(new JLabel("Cluster IDs (memberships)"), cGoDown); clusterIDsTextField = new JTextField(); monitorPanel.add(clusterIDsTextField, cGoDown); monitorPanel.add(new JSeparator(SwingConstants.HORIZONTAL), cGoDown); clusterIDsTextField.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { setGaianConfigProperty( ACCESS_CLUSTERS, clusterIDsTextField.getText() ); } }); ////////////////// // Discovery Panel ////////////////// monitorPanel.add(new JLabel("Peer Discovery"), cGoDown); ButtonGroup discoveryIPOptionsGroup = new ButtonGroup(); radioButtonDiscoveryOff = new JRadioButton( OffButtonLabel, false ); radioButtonDiscoveryBroadcast = new JRadioButton( BroadcastButtonLabel, false ); radioButtonDiscoveryMulticast = new JRadioButton( MulticastButtonLabel, false ); for ( JRadioButton radioButton : new JRadioButton[] { radioButtonDiscoveryOff, radioButtonDiscoveryBroadcast, radioButtonDiscoveryMulticast } ) { // radioButton.addActionListener(discoveryOptionActionListener); monitorPanel.add( radioButton, cGoDown ); // = Protocol buttons: Off / Broadcast / Multicast radioButton.addActionListener(discoveryModeButtonsActionListener); radioButton.setActionCommand( radioButton.getText() ); discoveryIPOptionsGroup.add( radioButton ); } // Multicast group label and text field - should hide away or be grayed out when broadcast is selected multicastGroupPanel = new JPanel(new GridBagLayout()); multicastGroupPanel.add( new JLabel("Group IP") ); multicastGroupTextField = new JTextField(); multicastGroupTextField.setDocument( Field.getValidatedDocument("[0-9\\.]*") ); //"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+") ); //"[0-9\\.]*") ); multicastGroupPanel.add( multicastGroupTextField, cDefault ); monitorPanel.add( multicastGroupPanel, cGoDown ); // = Multicast Group IP label + input text field multicastGroupTextField.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { String discoveryIP = multicastGroupTextField.getText().trim(); if ( 1 > discoveryIP.length() || discoveryIP.equals( DEFAULT_MULTICAST_GROUP ) ) discoveryIP = null; // equivalent to the default setGaianConfigProperty( DISCOVERY_IP, discoveryIP ); } }); JLabel targetNetworksLabel = new JLabel("Target Networks"); targetNetworksLabel.setBorder(topBorderPadding); monitorPanel.add( targetNetworksLabel, cGoDown); int baseSize = getFont().getSize(); JScrollPane subnetsScrollPane = null; // Position the JPanel that will hold the JCheckBox values for the netInfo IPs monitorPanel.add( subnetsScrollPane = createScroller( subnetsPanel = new JPanel(new GridBagLayout()), -1, baseSize * 8 ), cGoDown); subnetsScrollPane.setMinimumSize(new Dimension(-1, baseSize * 8)); // limit compression on this Pane when the window shrinks JLabel label = new JLabel("Gateway IPs"); label.setBorder(topBorderPadding); monitorPanel.add(label, cGoDown); discoveryGatewaysTextField = new JTextField(); discoveryGatewaysTextField.setDocument( Field.getValidatedDocument("[0-9\\.,]*") ); //"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+") ); //"[0-9\\.]*") ); monitorPanel.add(discoveryGatewaysTextField, cGoDown); discoveryGatewaysTextField.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { setGaianConfigProperty( DISCOVERY_GATEWAYS, discoveryGatewaysTextField.getText() ); } }); // discoveryGatewaysPanel.addFocusListener(new FocusListener() { // public void focusGained(FocusEvent e) {} // public void focusLost(FocusEvent e) { // JTextField tBox = (JTextField)e.getSource(); // if ( 0 == tBox.getText().length() ) tBox.setText("1"); // } // }); // JButton reDiscover = new JButton("Re-Discover Peers"); //// refresh.setBorder(topBorder); // reDiscover.addActionListener(new ActionListener() { // public void actionPerformed(ActionEvent event) { //// try { updateLogicalTablesComboBox(); } catch ( SQLException e ) {} // updateGraph(); // updateValues(); // } // }); // // monitorPanel.add(reDiscover, cGoDown); ////////////////// // Update interval ////////////////// monitorPanel.add(new JSeparator(SwingConstants.HORIZONTAL), cGoDown); JLabel intervalLabel = new JLabel("Update Interval (s)"); // intervalLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, Dashboard.BORDER_SIZE / 2)); JSpinner intervalSpinner = new JSpinner(new SpinnerNumberModel(GRAPH_INTERVAL / 1000, 1, 60, 1)); intervalLabel.setBorder(BorderFactory.createEmptyBorder(0, Dashboard.BORDER_SIZE / 2, 0, 0)); intervalSpinner.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { Integer interval = (Integer)((JSpinner)e.getSource()).getValue(); if (null != interval) { if (null != graphUpdater) graphUpdater.setInterval(interval * 1000); if (null != colourUpdater) colourUpdater.setInterval(interval * 1000); } } }); JPanel intervalPanel = new JPanel(new GridBagLayout()); // intervalPanel.setBorder(topBorder1); intervalPanel.add(intervalLabel); intervalPanel.add(intervalSpinner); JButton refresh = new JButton("Update Now"); // refresh.setBorder(topBorder); refresh.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { // try { updateLogicalTablesComboBox(); } catch ( SQLException e ) {} updateGraph(); updateValues(); loadDiscoveryConfig(); } }); // refresh.setBorder(topBorderPadding); intervalPanel.add(refresh, cGoDown); monitorPanel.add(intervalPanel, cGoDown); ////////////////////////////////////////////////////// // Empty panel to keep the monitor controls at the top ////////////////////////////////////////////////////// monitorPanel.add(new JPanel(), cExpandDown); //////////////// nodeInfo = new JLabel("", SwingConstants.TRAILING); graph = TopologyGraph.getSingleton(); graph.setNodeRenderer(true); // set default node renderer to show node names rather than symbols graph.setBorder(BorderFactory.createEtchedBorder()); graph.panAbs(graph.getWidth() / 2, graph.getHeight() / 2); localNodeID = container.getLocalNodeID(); graph.setLocalNode( localNodeID ); try { graphStatement = conn.prepareStatement( GRAPH_SQL ); graphStatement.setQueryTimeout(Dashboard.QUERY_TIMEOUT); valueProcessor = new MetricValueProcessor(conn) { protected void add(String node, int timestamp, Map<MonitorInfo, Integer> values) { for (Integer value : values.values()) { // if graph has not been destroyed if ( null != graph ) graph.setNodeColor(node, valueProcessor.getMonitors()[0].getColor(value)); } } }; valueProcessor.clearMonitors(); valueProcessor.setTopologyGraph(graph); } catch (SQLException e) { // e.printStackTrace(); if (container.checkConnection()) { LOGGER.severe(unravelMessages(e)); destroy(); showError("Could not retrieve the graph topology information."); } return; } addUpdater(graphUpdater = new Updater("TopologyTab.updateGraph", GRAPH_INTERVAL) { protected boolean update() { return updateGraph(); } }); addUpdater(colourUpdater = new Updater("TopologyTab.updateValues", GRAPH_INTERVAL) { protected boolean update() { return updateValues(); } }); addUpdater(new Updater("TopologyTab.updateDiscoveryConfig", GRAPH_INTERVAL) { protected boolean update() { return loadDiscoveryConfig(); } }); addUpdater(new Updater("TopologyTab.updateInfoLabel", LABEL_INTERVAL) { protected boolean update() { return updateInfoLabel(); } }); addUpdater(new Updater("TopologyTab.enterNodeDetails", LABEL_INTERVAL) { protected boolean update() { return enterNodeDetails(); } }); hideMessage(); add(nodeInfo, BorderLayout.PAGE_START); add(monitorPanel, BorderLayout.LINE_START); add(graph, BorderLayout.CENTER); metricsDropDown.setSelectedIndex(0); // activate the top selection "Connectivity" (default color coding scheme) // System.out.println("Updating from create()"); try { updateLogicalTablesComboBox(); } catch (SQLException e) { // e.printStackTrace(); if (container.checkConnection()) { LOGGER.severe(unravelMessages(e)); destroy(); showError("Could not retrieve the list of logical tables for this node."); } return; } // graphPanel = new JPanel(new GridBagLayout()); // cGoDown.gridx = 0; cGoDown.gridy = 0; cGoDown.weightx = 1; cGoDown.weighty = 1; // cGoDown.fill = GridBagConstraints.BOTH; // graphPanel.add(graph, cGoDown); // // JPanel tgtPanel = new JPanel(new GridBagLayout()); // cGoDown.weightx = 0; cGoDown.weighty = 0; // cGoDown.fill = GridBagConstraints.NONE; // tgtPanel.add(new JLabel("Targetted Nodes: "), cGoDown); // // JButton clrButton = new JButton("Clear"); // cGoDown.gridx = 2; cGoDown.gridy = 0; // tgtPanel.add(clrButton, cGoDown); // // JTextArea qryInput = new JTextArea(); // cGoDown.gridx = 1; cGoDown.gridy = 0; cGoDown.weightx = 1; // cGoDown.fill = GridBagConstraints.HORIZONTAL; // tgtPanel.add(createScroller(qryInput, -1, getFont().getSize()*4), cGoDown); // // tgtPanel.setBorder(topBorder); // // cGoDown.gridx = 0; cGoDown.gridy = 1; // graphPanel.add(tgtPanel, cGoDown); // // add(graphPanel, BorderLayout.CENTER); } private Set<String> nodeLogicalTables = new HashSet<String>(); private PreparedStatement getLTsStatement = null; private synchronized void updateLogicalTablesComboBox() throws SQLException { if ( null == getLTsStatement || getLTsStatement.isClosed() ) getLTsStatement = conn.prepareStatement("select LTNAME from new com.ibm.db2j.GaianConfig('LTDEFS') GC ORDER BY LTNAME"); ResultSet rs = getLTsStatement.executeQuery(); // ResultSet rs = conn.createStatement().executeQuery("select LTNAME from new com.ibm.db2j.GaianConfig('LTDEFS') GC"); if ( rs.next() ) { Set<String> newItems = new HashSet<String>(); int count = 0; do { count++; String lt = rs.getString(1); newItems.add(lt); if ( ! nodeLogicalTables.remove(lt) ) comboLogicalTables.insertItemAt( lt, count ); // insert instead of adding to maintain the order } while ( rs.next() ); for ( String oldItem : nodeLogicalTables ) comboLogicalTables.removeItem(oldItem); nodeLogicalTables.clear(); nodeLogicalTables = newItems; // System.out.println("Items count: " + count); } rs.close(); } AtomicBoolean isGraphUpdating = new AtomicBoolean( false ); protected boolean updateGraph() { if ( null == graph ) return false; // no-op if graph was destroyed if ( isGraphUpdating.compareAndSet( false, true ) ) { try { graph.setEdge(localNodeID, localNodeID); graphStatement = null; // System.out.println("Updating Graph: tableToQuery: " + tableToQuery ); boolean isTableToQuerySetForPathVisualisation = null != tableToQuery; if ( isTableToQuerySetForPathVisualisation ) try { String pathSQL = "SELECT gdbx_from_node source, gdbx_to_node target, gdbx_count, gdbx_precedence " + "FROM new com.ibm.db2j.GaianTable('" + tableToQuery + "', 'explainds') T " + "WHERE gdbx_depth > 0 ORDER BY gdbx_depth"; // System.out.println("Using path graph SQL: " + pathSQL); graphStatement = conn.prepareStatement( pathSQL ); } catch (Exception e) { // System.out.println("Caught exception: " + e); isTableToQuerySetForPathVisualisation = false; comboLogicalTables.setSelectedIndex(0); } if ( null == graphStatement ) graphStatement = conn.prepareStatement( GRAPH_SQL ); ResultSet resultSet = graphStatement.executeQuery(); queryPaths.clear(); while ( resultSet.next() ) { String src = resultSet.getString("SOURCE"); String tgt = resultSet.getString("TARGET"); if ( isTableToQuerySetForPathVisualisation && resultSet.getLong("GDBX_COUNT") > 0 && "F".equals( resultSet.getString("GDBX_PRECEDENCE") ) ) { boolean isNewPath = true; // System.out.println(src + " -> " + tgt + " " + resultSet.getLong("GDBX_COUNT")); if ( queryPaths.isEmpty() ) queryPaths.add(new ArrayList<String>(Arrays.asList(src,tgt))); else { // extend any existing paths ending with 'src' by appending target 'tgt' for( ArrayList<String> path : queryPaths ) { if( path != null && ! path.isEmpty() ) { if( src.equals( path.get(path.size()-1) ) ) { path.add(tgt); isNewPath = false; break; } } } // if no paths were extended (above), create a new path based on an existing sub-path if ( isNewPath ) { ArrayList<String> newPath = null; for ( ArrayList<String> path : queryPaths ) { for ( int i=0; i < path.size()-1 ; i++ ) { if ( src.equals( path.get(i) ) ) { newPath = new ArrayList<String>( path.subList(0,i+1) ); newPath.add(tgt); break; } } if ( null != newPath ) { queryPaths.add( newPath ); break; } } } } } graph.setEdge(src,tgt); } // System.out.println("PATHS: " + Arrays.asList( queryPaths ) ); graph.highLightLinks(queryPaths,-1); // animation tick = -1, i.e. disabled for now graph.update(); updateValues(); graph.recenter(); } catch (SQLException e) { if (container.checkConnection()) { LOGGER.severe(unravelMessages(e)); destroy(); showError("Could not retrieve the graph topology information."); } return false; } finally { isGraphUpdating.set(false); } } return true; } protected boolean updateValues() { synchronized (this) { try { valueProcessor.processLatestMetrics(MAX_AGE); } catch (SQLException e) { if (container.checkConnection()) { LOGGER.severe(unravelMessages(e)); destroy(); showError("Could not process latest metrics on graph topology."); } return false; } } if ( null != graph ) graph.updateValues(); MonitorInfo[] monitors = valueProcessor.getMonitors(); if ( null != monitors ) ((JLabel)colorRangePanel.getComponent(NUM_RANGE_COLORS-1)).setText(""+monitors[0].maxBound); return true; } protected boolean enterNodeDetails() { if (null == graph) { return false; } // int nodeCount = graph.getNodeCount(); String lastClickedNode = graph.getAndUnsetLastClickedNode(); if (null != lastClickedNode) { // System.out.println("configurationDialog isVisible? " + (null==configurationDialog?false:configurationDialog.isVisible())); // show nodeDetails input fields box if ( null == configurationDialog || !configurationDialog.isVisible() ) { clickedNode = lastClickedNode; configurationDialog = new ConfigurationDialog( this, textFieldLabels, clickedNode ); // configurationDialog.addWindowListener(this); // in case we want to know when the config panel was deactivated... configurationDialog.setVisible(true); } else { configurationDialog.requestFocus(); } } return true; } private String previousHoveredOverNode = null; protected boolean updateInfoLabel() { if (null == graph) { return false; } String hoveredOverNode = graph.getCurrentItemName(); boolean hasGraphChanged = graph.hasChanged(); if ( hoveredOverNode != null && hoveredOverNode.equals(previousHoveredOverNode) && !hasGraphChanged && 0 < nodeInfo.getText().length()) return true; previousHoveredOverNode = hoveredOverNode; if (null == hoveredOverNode) { String txt = "Node Count: " + graph.getNodeCount() + " | " + "Radius: " + graph.getRadius() + " | " + "Diameter: " + graph.getDiameter() + " | " + "Nodes per Eccentricity: " + graph.getNodesPerEccentricity() + " | " + "Nodes per Connectivity: " + graph.getNodesPerConnectivity(); if ( hasGraphChanged ) System.out.println( txt ); nodeInfo.setText( txt ); } else { ArrayList<String> fn = new ArrayList<String>( graph.getFurthestNodes(hoveredOverNode) ); int fnsize = fn.size(); while( fn.size() > 2 ) fn.remove(0); nodeInfo.setText( // "Node Count: " + nodeCount + " | " + "Current Node: " + hoveredOverNode + " | " + "Eccentricity: " + graph.getEccentricity(hoveredOverNode) + " | " + "Nodes reached per step: " + graph.getStepCardinalities(hoveredOverNode) + " | " + "Furthest Nodes: " + fn + ( fnsize > 2 ? " (" + (fnsize-2) + " more...)" : "" ) ); } return true; } protected void destroy() { if (null != monitorPanel) { remove(monitorPanel); monitorPanel = null; } if (null != nodeInfo) { remove(nodeInfo); nodeInfo = null; } if (null != graph) { remove(graph); graph = null; } // if (null != graphPanel) { // remove(graphPanel); // graphPanel = null; // } graphUpdater = null; subnetsChks = null; if ( null != netInfos ) netInfos.clear(); netInfos = null; if ( null != netInfoSet ) netInfoSet.clear(); netInfoSet = null; if ( null != allBroadcastOptions ) allBroadcastOptions.clear(); // don't set this one to null if ( null != broadcastTargets ) broadcastTargets.clear(); broadcastTargets = null; if ( null != multicastTargets ) multicastTargets.clear(); multicastTargets = null; } public void actionPerformed(ActionEvent e) { Properties configValues = configurationDialog.getConfigValuesIfReady(); if ( null != configValues ) { try { // System.out.println("clickedNode " + clickedNode); String username = configValues.getProperty(USR_LABEL); String password = configValues.getProperty(PWD_LABEL); if ( 0==username.length() || 0==password.length() ) throw new Exception("Each field must be set"); container.securityAgent.setRemoteAccessCredentials(clickedNode, username, password); configurationDialog.setVisible(false); } catch (Exception e1) { String msg = e1.toString(); // e1.printStackTrace(); int exNameStartIndex = msg.lastIndexOf('.', msg.indexOf(':')) + 1; configurationDialog.requestNewConfigValues(msg.substring(exNameStartIndex)); } } } public void updatePathVisualisation(int tickCount) { // If network created if(graph != null) { graph.highLightLinks(queryPaths,tickCount); // Find Path // Pass in values of which to highlight // Send path as Array of String to the Topology Graph // Write procedure that checks if Links on part of the Array // pass data to Topology Tab // System.out.println(graph.getDisplayX()); // graph.dataPath(); } } }