uk.co.petertribble.jangle.SnmpTreePanel.java Source code

Java tutorial

Introduction

Here is the source code for uk.co.petertribble.jangle.SnmpTreePanel.java

Source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2013 Peter C. Tribble
 */

package uk.co.petertribble.jangle;

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.tree.*;
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.event.*;
import uk.co.petertribble.jingle.JingleTextPane;
import org.jfree.chart.ChartPanel;
import org.jdesktop.swingworker.SwingWorker;

/**
 * A panel displaying the snmp statistics that are available as a list or
 * tree in a left panel, and details and possibly a graphical chart in the
 * right panel.
 *
 * @author Peter Tribble
 */
public class SnmpTreePanel extends JPanel implements TreeSelectionListener, ListSelectionListener, ActionListener {

    private JingleTextPane tp;
    private JingleTextPane tpdesc;
    private JPanel jp1;
    private JPanel jp2;
    private SnmpChart chart;

    // for siblings
    private JPanel jp3;
    private JPanel jp3t;
    private JingleTextPane tpsiblings;
    private SnmpChart schart;

    // for cousins
    private JPanel jp4;
    private JPanel jp4t;
    private JingleTextPane tpcousins;
    private SnmpChart cchart;

    private Exception savederror;

    private JList slist;
    private JProgressBar jpb;
    private DefaultListModel model;
    private JTabbedPane jtpl;
    private JTabbedPane jtpr;
    private JTree stree;
    private static final int TAB_D = 0;
    private static final int TAB_A = 1;
    private static final int TAB_S = 2;
    private static final int TAB_C = 3;

    private String startOID;
    private SnmpObject prevOID;
    private SnmpObject currentOID;
    private SnmpController sc;
    private SnmpList snl;
    private SnmpMibManager smm;
    private List<SnmpObject> oidList;

    private Timer timer;
    private int interval = 30;
    private int age = 1800;

    /**
     * Create a new SnmpTreePanel, starting exploration at the default OID
     * of 1.3.6.1.2.1.
     */
    public SnmpTreePanel() {
        this("1.3.6.1.2.1");
    }

    /**
     * Create a new SnmpTreePanel, starting exploration at the specified OID.
     *
     * @param startOID the OID to start from
     */
    public SnmpTreePanel(String startOID) {
        this.startOID = startOID;
        setLayout(new BorderLayout());

        jtpl = new JTabbedPane();
        model = new DefaultListModel();
        oidList = new ArrayList<SnmpObject>();
        slist = new JList(model);
        slist.addListSelectionListener(this);
        slist.setCellRenderer(new SnmpListCellRenderer());
        jtpl.add(SnmpResources.getString("SNMP.LIST.TEXT"), new JScrollPane(slist));

        // we use explicit placement so the tabs can be manipulated later
        jtpr = new JTabbedPane();

        // details tab
        jp1 = new JPanel(new BorderLayout());
        jp2 = new JPanel(new BorderLayout());
        tp = new JingleTextPane("text/plain");
        jp1.add(new JScrollPane(tp));
        // jp2 holds the chart
        jp2 = new JPanel(new BorderLayout());
        JSplitPane jpt = new JSplitPane(JSplitPane.VERTICAL_SPLIT, jp1, jp2);
        jpt.setOneTouchExpandable(true);
        jpt.setDividerLocation(120);

        jtpr.insertTab(SnmpResources.getString("SNMP.DETAILS.TEXT"), (Icon) null, jpt, (String) null, TAB_D);

        tpdesc = new JingleTextPane("text/plain");
        jtpr.insertTab(SnmpResources.getString("SNMP.ABOUT.TEXT"), (Icon) null, new JScrollPane(tpdesc),
                (String) null, TAB_A);

        // siblings tab
        tpsiblings = new JingleTextPane("text/plain");
        jp3 = new JPanel(new BorderLayout());
        jp3t = new JPanel(new BorderLayout());
        JSplitPane jps = new JSplitPane(JSplitPane.VERTICAL_SPLIT, jp3t, jp3);
        jps.setOneTouchExpandable(true);
        jps.setDividerLocation(200);
        jtpr.insertTab(SnmpResources.getString("SNMP.SIBLINGS.TEXT"), (Icon) null, jps, (String) null, TAB_S);

        // cousins tab
        tpcousins = new JingleTextPane("text/plain");
        jp4 = new JPanel(new BorderLayout());
        jp4t = new JPanel(new BorderLayout());
        JSplitPane jpc = new JSplitPane(JSplitPane.VERTICAL_SPLIT, jp4t, jp4);
        jpc.setOneTouchExpandable(true);
        jpc.setDividerLocation(200);
        jtpr.insertTab(SnmpResources.getString("SNMP.COUSINS.TEXT"), (Icon) null, jpc, (String) null, TAB_C);

        // split pane to hold the lot
        JSplitPane psplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, jtpl, jtpr);
        psplit.setOneTouchExpandable(true);
        psplit.setDividerLocation(200);
        add(psplit);
    }

    /**
     * Start exploring the snmp tree.
     *
     * @param params details of the snmp server to query
     */
    public void exploreServer(SnmpParams params) {
        sc = new SnmpController(params);
        snl = new SnmpList(sc);
        jpb = new JProgressBar(0, 1);
        jpb.setValue(0);
        jpb.setIndeterminate(true);
        jp2.add(jpb, BorderLayout.SOUTH);
        jp2.validate();
        // load data in background
        (new ExploreWorker()).execute();
    }

    /*
     * This is where the tree is walked and the data loaded. We do the snmp
     * tree walk in the background and update the gui when we're done - that
     * means building the model for the list and finishing off the progress
     * bar.
     *
     * One possibly bad consequence of this method is that we have to expose
     * the data structures as class variables so that they can be accessed
     * from multiple contexts.
     */
    class ExploreWorker extends SwingWorker<String, Object> {
        @Override
        public String doInBackground() {
            buildData();
            return "done";
        }

        @Override
        protected void done() {
            try {
                jpb.setIndeterminate(false);
                jpb.setValue(1);
                if (savederror == null) {
                    buildUI();
                    startLoop();
                } else {
                    showError();
                }
                jp2.removeAll();
                jp2.validate();
                jp2.repaint();
            } catch (Exception e) {
            }
        }
    }

    /**
     * Start the timer, so the display will continually update.
     */
    public void startLoop() {
        if (timer == null) {
            timer = new Timer(interval * 1000, this);
        }
        timer.start();
    }

    private void setPanel(SnmpTreeNode node) {
        if (node != null) {
            if (node.getUserObject() instanceof SnmpObject) {
                showObject((SnmpObject) node.getUserObject());
            } else if (node.getUserObject() instanceof String) {
                showObject((String) node.getUserObject());
            }
        }
    }

    /*
     * Show the MIB details for a fake parent node.
     * We only do any work if it exists - if we can't find a MIB entry
     * then we leave the display unchanged.
     */
    private void showObject(String oid) {
        if (smm == null) {
            smm = SnmpMibManager.getInstance();
        }
        String s = smm.getNearestMibForOID(oid);
        if (s != null) {
            clearCharts();
            tpdesc.setText(s);
            jtpr.setSelectedIndex(TAB_A);
            jtpr.setEnabledAt(TAB_D, false);
            jtpr.setEnabledAt(TAB_S, false);
            jtpr.setEnabledAt(TAB_C, false);
        }
    }

    /*
     * Show the object. If the new object is a sibling of the current
     * object, leave the sibling chart intact; if the new object is a
     * cousin of the current object, leave the cousin chart intact.
     */
    private void showObject(SnmpObject sno) {
        if (smm == null) {
            smm = SnmpMibManager.getInstance();
        }
        jtpr.setEnabledAt(TAB_D, true);
        jtpr.setEnabledAt(TAB_S, true);
        jtpr.setEnabledAt(TAB_C, true);
        // stop any current charts
        clearChart();
        prevOID = currentOID;
        currentOID = sno;
        setText(sno);
        String oid = sno.toString();
        // show a chart if it looks like a number
        if (canChart(sno)) {
            chart = new SnmpChart(sc, oid, smm.prettifyOID(oid), interval, age);
            jp2.add(new ChartPanel(chart.getChart()));
        }
        jp2.validate();
        jp1.validate();
        String s = smm.getNearestMibForOID(oid);
        tpdesc.setText((s == null) ? "" : s);
        if (!isSibling(currentOID, prevOID)) {
            clearSiblingChart();
            showSiblings(sno);
        }
        if (!isCousin(currentOID, prevOID)) {
            clearCousinChart();
            showCousins(sno);
        }
    }

    /*
     * Check whether two SnmpObjects are siblings
     */
    private boolean isSibling(SnmpObject sn1, SnmpObject sn2) {
        if (sn1 == null || sn2 == null) {
            return false;
        }
        return snl.getSiblings(sn1.toString()).contains(sn2);
    }

    /*
     * Check whether two SnmpObjects are cousins
     */
    private boolean isCousin(SnmpObject sn1, SnmpObject sn2) {
        if (sn1 == null || sn2 == null) {
            return false;
        }
        return snl.getCousins(sn1.toString()).contains(sn2);
    }

    private boolean canChart(SnmpObject sno) {
        /*
         * This is only called from showObject, so we can assume that smm has
         * been initialized. We manually refuse to chart certain names that
         * simply wouldn't make sense.
         */
        String sp = smm.prettifyOID(sno);
        if (sp.startsWith("hrSWInstalled") || sp.startsWith("hrSWRun")) {
            return false;
        }
        String typeString = sno.getTypeString();
        if (!"OctetString".equals(typeString) && !"IPAddress".equals(typeString) && !"Null".equals(typeString)
                && !"ObjectIdentifier".equals(typeString)) {
            return true;
        }
        return false;
    }

    private void clearCharts() {
        clearChart();
        clearSiblingChart();
        clearCousinChart();
    }

    private void clearChart() {
        if (chart != null) {
            chart.stopLoop();
            jp2.removeAll();
            jp2.validate();
            jp2.repaint();
        }
    }

    private void clearSiblingChart() {
        jp3t.removeAll();
        jp3t.validate();
        jp3t.repaint();
        if (schart != null) {
            schart.stopLoop();
            jp3.removeAll();
            jp3.validate();
            jp3.repaint();
        }
    }

    private void clearCousinChart() {
        jp4t.removeAll();
        jp4t.validate();
        jp4t.repaint();
        if (cchart != null) {
            cchart.stopLoop();
            jp4.removeAll();
            jp4.validate();
            jp4.repaint();
        }
    }

    private void showSiblings(SnmpObject sno) {
        String oid = sno.toString();
        List<SnmpObject> ls = snl.getSiblings(oid);
        if (ls.size() > 1) {
            if (canChart(sno)) {
                schart = new SnmpChart(sc, ls, smm.prettifyOID(SnmpUtil.getParentOID(oid)), interval, age);
                jp3.add(new ChartPanel(schart.getChart()));
                jp3t.add(new JScrollPane(new JTable(schart)));
            } else {
                StringBuilder sb = new StringBuilder();
                for (SnmpObject so : ls) {
                    sb.append(smm.prettifyOID(so)).append("=");
                    sb.append(SnmpUtil.niceString(so)).append("\n");
                }
                tpsiblings.setText(sb.toString());
                jp3t.add(new JScrollPane(tpsiblings));
            }
            jp3.validate();
            jp3t.validate();
        } else {
            jtpr.setEnabledAt(TAB_S, false);
        }
    }

    private void showCousins(SnmpObject sno) {
        String oid = sno.toString();
        List<SnmpObject> alloids = snl.getCousins(oid);
        if (alloids.size() > 1) {
            StringBuilder sb = new StringBuilder();
            // a list to hold those that can be charted
            List<SnmpObject> lc = new ArrayList<SnmpObject>();
            for (SnmpObject so : alloids) {
                sb.append(smm.prettifyOID(so)).append("=");
                sb.append(SnmpUtil.niceString(so)).append("\n");
                if (canChart(so)) {
                    lc.add(so);
                }
            }
            if (lc.size() > 1) {
                cchart = new SnmpChart(sc, lc, alloids,
                        smm.prettifyOID(SnmpUtil.getParentOID(SnmpUtil.getParentOID(oid))), interval, age);
                jp4.add(new ChartPanel(cchart.getChart()));
                jp4t.add(new JScrollPane(new JTable(cchart)));
            } else {
                tpcousins.setText(sb.toString());
                jp4t.add(new JScrollPane(tpcousins));
            }
            jp4.validate();
            jp4t.validate();
        } else {
            jtpr.setEnabledAt(TAB_C, false);
        }
    }

    private void setText(SnmpObject sno) {
        StringBuilder sb = new StringBuilder();
        sb.append("OID: ").append(smm.prettifyOID(sno)).append("\n");
        String typeString = sno.getTypeString();
        sb.append("Type: ").append(typeString).append("\n");
        sb.append("Value: ").append(SnmpUtil.niceString(sno));
        if ("ObjectIdentifier".equals(typeString)) {
            sb.append(" = ").append(smm.prettifyOID(sno.getValue().toString()));
        }
        sb.append("\n");
        tp.setText(sb.toString());
    }

    /**
     * Set the update interval.
     *
     * @param interval the desired update delay in seconds
     */
    public void setDelay(int interval) {
        this.interval = interval;
        if (timer != null) {
            timer.setDelay(interval * 1000);
        }
        if (chart != null) {
            chart.setDelay(interval);
        }
        if (schart != null) {
            schart.setDelay(interval);
        }
        if (cchart != null) {
            cchart.setDelay(interval);
        }
    }

    /**
     * Set the chart display window.
     *
     * @param age The required maximum chart age in seconds.
     */
    public void setMaxAge(int age) {
        this.age = age;
        if (chart != null) {
            chart.setMaxAge(age);
        }
        if (schart != null) {
            schart.setMaxAge(age);
        }
        if (cchart != null) {
            cchart.setMaxAge(age);
        }
    }

    private void updateCurrent() {
        if (currentOID != null) {
            try {
                currentOID.setValue(sc.getValue(currentOID.toString()).getValue());
            } catch (SnmpException sne) {
                // FIXME do summat
            }
            setText(currentOID);
        }
    }

    // retrieve the data via an snmp walk of the tree
    private void buildData() {
        try {
            oidList = snl.getList(startOID);
        } catch (SnmpException sne) {
            savederror = sne;
        }
    }

    private void showError() {
        if (savederror != null) {
            System.out.println(savederror.getMessage());
            JOptionPane.showMessageDialog(this, savederror.getMessage(), "SNMP error", JOptionPane.ERROR_MESSAGE);
            savederror = null;
        }
    }

    /*
     * This methods builds both the list and the tree.
     */
    private void buildUI() {
        for (SnmpObject sno : oidList) {
            model.addElement(sno);
        }
        /*
         * We create a SnmpTreeNode for each element in the list and add it to
         * a Hash with the name as the key. Then we walk the list - if a node
         * has a parent, we add it to the parent, else we need to create
         * intermediate nodes to fill in the tree.
         */
        // root node
        SnmpTreeNode rootNode = new SnmpTreeNode("SNMP");
        // real nodes
        Map<String, SnmpTreeNode> m = new HashMap<String, SnmpTreeNode>();
        // intermediate nodes
        Map<String, SnmpTreeNode> m2 = new HashMap<String, SnmpTreeNode>();
        for (SnmpObject sno : oidList) {
            m.put(sno.toString(), new SnmpTreeNode(sno));
        }
        for (SnmpObject sno : oidList) {
            String s = sno.toString();
            SnmpTreeNode mynode = m.get(s);
            String parent = SnmpUtil.getParentOID(s);
            while (parent != null) {
                if (m.containsKey(parent)) {
                    /*
                     * If there's a parent node, add this as a child.
                     * This almost never happens.
                     */
                    m.get(parent).add(mynode);
                    mynode = m.get(parent);
                } else if (m2.containsKey(parent)) {
                    /*
                     * If we've already created a parent node, add this node
                     * to that.
                     */
                    m2.get(parent).add(mynode);
                    mynode = m2.get(parent);
                } else {
                    /*
                     * Need to create a new node
                     */
                    SnmpTreeNode pnode = new SnmpTreeNode(parent);
                    pnode.add(mynode);
                    m2.put(parent, pnode);
                    mynode = pnode;
                }
                parent = SnmpUtil.getParentOID(parent);
            }
            rootNode.add(mynode);
        }
        if (stree == null) {
            stree = new JTree(rootNode);
            stree.addTreeSelectionListener(this);
            jtpl.add(SnmpResources.getString("SNMP.TREE.TEXT"), new JScrollPane(stree));
        } else {
            stree.setModel(new DefaultTreeModel(rootNode));
        }
    }

    // handle ListSelectionListener events
    public void valueChanged(ListSelectionEvent e) {
        if (!e.getValueIsAdjusting() && (slist.getSelectedIndex() != -1)) {
            Object o = slist.getSelectedValue();
            if (o instanceof SnmpObject) {
                showObject((SnmpObject) o);
            }
        }
    }

    // handle TreeSelectionListener events
    public void valueChanged(TreeSelectionEvent e) {
        TreePath tpth = e.getNewLeadSelectionPath();
        if (tpth != null) {
            setPanel((SnmpTreeNode) tpth.getLastPathComponent());
        }
    }

    // handle timer events
    public void actionPerformed(ActionEvent e) {
        updateCurrent();
    }
}