se.sics.kompics.p2p.monitor.cyclon.server.CyclonMonitorServer.java Source code

Java tutorial

Introduction

Here is the source code for se.sics.kompics.p2p.monitor.cyclon.server.CyclonMonitorServer.java

Source

/**
 * This file is part of the Kompics P2P Framework.
 *
 * Copyright (C) 2009 Swedish Institute of Computer Science (SICS) Copyright (C)
 * 2009 Royal Institute of Technology (KTH)
 *
 * Kompics is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA.
 */
package se.sics.kompics.p2p.monitor.cyclon.server;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;
import java.util.UUID;

import javax.imageio.ImageIO;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import org.apache.commons.codec.binary.Base64;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.statistics.HistogramDataset;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import se.sics.kompics.ComponentDefinition;
import se.sics.kompics.Handler;
import se.sics.kompics.Negative;
import se.sics.kompics.Positive;
import se.sics.kompics.address.Address;
import se.sics.kompics.network.Network;
import se.sics.kompics.p2p.overlay.OverlayAddress;
import se.sics.kompics.p2p.overlay.cyclon.CyclonAddress;
import se.sics.kompics.p2p.overlay.cyclon.CyclonNeighbors;
import se.sics.kompics.p2p.overlay.cyclon.CyclonNodeDescriptor;
import se.sics.kompics.p2p.overlay.cyclon.GraphUtil;
import se.sics.kompics.timer.CancelTimeout;
import se.sics.kompics.timer.ScheduleTimeout;
import se.sics.kompics.timer.Timer;
import se.sics.kompics.web.Web;
import se.sics.kompics.web.WebRequest;
import se.sics.kompics.web.WebResponse;

/**
 * The
 * <code>CyclonMonitorServer</code> class.
 *
 * @author Cosmin Arad <cosmin@sics.se>
 * @version $Id$
 */
public class CyclonMonitorServer extends ComponentDefinition {

    Positive<Network> network = positive(Network.class);
    Positive<Timer> timer = positive(Timer.class);
    Negative<Web> web = negative(Web.class);
    private static final Logger logger = LoggerFactory.getLogger(CyclonMonitorServer.class);
    // private long updatePeriod;
    private final HashSet<UUID> outstandingTimeouts;
    private final HashMap<OverlayAddress, OverlayViewEntry> view;
    private TreeMap<OverlayAddress, CyclonNeighbors> alivePeers;
    private TreeMap<OverlayAddress, CyclonNeighbors> deadPeers;
    private long evictAfter;

    public CyclonMonitorServer(CyclonMonitorServerInit init) {
        this.view = new HashMap<OverlayAddress, OverlayViewEntry>();
        this.alivePeers = new TreeMap<OverlayAddress, CyclonNeighbors>();
        this.deadPeers = new TreeMap<OverlayAddress, CyclonNeighbors>();
        this.outstandingTimeouts = new HashSet<UUID>();

        subscribe(handleWebRequest, web);
        subscribe(handlePeerNotification, network);
        subscribe(handleViewEvictPeer, timer);

        // INIT
        evictAfter = init.getConfiguration().getViewEvictAfter();

        logger.debug("INIT");

        try {
            UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        } catch (InstantiationException e1) {
            e1.printStackTrace();
        } catch (IllegalAccessException e1) {
            e1.printStackTrace();
        } catch (UnsupportedLookAndFeelException e1) {
            e1.printStackTrace();
        }
    }

    private Handler<CyclonNeighborsNotification> handlePeerNotification = new Handler<CyclonNeighborsNotification>() {
        public void handle(CyclonNeighborsNotification event) {
            Address peerAddress = event.getPeerAddress();
            CyclonNeighbors neighbors = event.getCyclonNeighbors();
            CyclonAddress cyclonAddress = neighbors.getSelf();

            addPeerToView(cyclonAddress, neighbors);

            logger.debug("Got notification from peer {}", peerAddress);
        }
    };
    private Handler<ViewEvictPeer> handleViewEvictPeer = new Handler<ViewEvictPeer>() {
        public void handle(ViewEvictPeer event) {
            // only evict if it was not refreshed in the meantime
            // which means the timer is not anymore outstanding
            if (outstandingTimeouts.contains(event.getTimeoutId())) {
                removePeerFromView(event.getOverlayAddress());
            }
        }
    };
    private Handler<WebRequest> handleWebRequest = new Handler<WebRequest>() {
        public void handle(WebRequest event) {
            logger.debug("Handling WebRequest");

            String peers = event.getRequest().getParameter("peers");

            boolean showPeers = peers == null ? false : peers.equals("on") ? true : false;

            String html = dumpViewToHtml(showPeers);
            WebResponse response = new WebResponse(html, event, 1, 1);
            trigger(response, web);
        }
    };

    private void addPeerToView(OverlayAddress address, CyclonNeighbors neighbors) {
        long now = System.currentTimeMillis();

        alivePeers.put(address, neighbors);
        deadPeers.remove(address);

        OverlayViewEntry entry = view.get(address);
        if (entry == null) {
            entry = new OverlayViewEntry(address, now, now);
            view.put(address, entry);

            // set eviction timer
            ScheduleTimeout st = new ScheduleTimeout(evictAfter);
            st.setTimeoutEvent(new ViewEvictPeer(st, address));
            UUID evictionTimerId = st.getTimeoutEvent().getTimeoutId();
            entry.setEvictionTimerId(evictionTimerId);
            outstandingTimeouts.add(evictionTimerId);

            trigger(st, timer);

            logger.debug("Added peer {}", address);
        } else {
            entry.setRefreshedAt(now);

            // reset eviction timer
            outstandingTimeouts.remove(entry.getEvictionTimerId());
            trigger(new CancelTimeout(entry.getEvictionTimerId()), timer);

            ScheduleTimeout st = new ScheduleTimeout(evictAfter);
            st.setTimeoutEvent(new ViewEvictPeer(st, address));
            UUID evictionTimerId = st.getTimeoutEvent().getTimeoutId();
            entry.setEvictionTimerId(evictionTimerId);
            outstandingTimeouts.add(evictionTimerId);

            trigger(st, timer);

            logger.debug("Refreshed peer {}", address);
        }
    }

    private void removePeerFromView(OverlayAddress address) {
        if (address != null) {
            CyclonNeighbors neighbors = alivePeers.remove(address);
            deadPeers.put(address, neighbors);
            logger.debug("Removed peer {}", address);
        }
    }

    private String dumpViewToHtml(boolean showPeers) {
        StringBuilder sb = new StringBuilder("<!DOCTYPE html PUBLIC ");
        sb.append("\"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3");
        sb.append(".org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html xmlns=");
        sb.append("\"http://www.w3.org/1999/xhtml\"><head><meta http-equiv=\"");
        sb.append("Content-Type\" content=\"text/html; charset=utf-8\" />");

        // page refresh
        // sb.append("<meta http-equiv=\"refresh\" content=\"10\">");
        sb.append("<title>Kompics P2P Monitor Server</title>");
        sb.append("<style type=\"text/css\"><!--.style2 {font-family: ");
        sb.append("Arial, Helvetica, sans-serif; color: #0099FF;}--></style>");
        sb.append("</head><body><h1 align=\"center\" class=\"style2\">");
        sb.append("Kompics P2P Monitor</h1>");

        printAlivePeers(sb, showPeers);
        if (showPeers) {
            printDeadPeers(sb);
        }
        sb.append("</body></html>");
        return sb.toString();
    }

    private void printAlivePeers(StringBuilder sb, boolean showPeers) {

        long t0 = System.currentTimeMillis();
        GraphUtil g = new GraphUtil(alivePeers);
        long t1 = System.currentTimeMillis();

        double id, od, cc, pl, istd;
        int diameter;

        id = g.getMeanInDegree();
        istd = g.getInDegreeStdDev();
        od = g.getMeanOutDegree();
        cc = g.getMeanClusteringCoefficient();
        pl = g.getMeanPathLength();
        diameter = g.getDiameter();
        int netSize = g.getNetworkSize();

        sb.append("<h2 align=\"center\" class=\"style2\">");
        sb.append("View of Cyclon Random Overlay:</h2>");
        sb.append("<table width=\"400\" border=\"1\" align=\"center\"><tr>");
        sb.append("<th class=\"style2\" width=\"250\" scope=\"col\">Metric");
        sb.append("</th><th class=\"style2\"");
        sb.append(" width=\"150\" scope=\"col\">Value</th></tr><tr>");

        sb.append("<td>Network size</td><td><div align=\"center\">");
        sb.append(netSize).append("</div></td></tr>");
        sb.append("<td>Disconnected node pairs</td><td><div align=\"center\">");
        sb.append(g.getInfinitePathCount()).append("/");
        sb.append(netSize * (netSize - 1)).append("</div></td></tr>");
        sb.append("<td>Diameter</td><td><div align=\"center\">");
        sb.append(diameter).append("</div></td></tr>");
        sb.append("<td>Average path length</td><td><div align=\"center\">");
        sb.append(String.format("%.4f", pl)).append("</div></td></tr>");
        sb.append("<td>Clustering-coefficient</td><td><div align=\"center\">");
        sb.append(String.format("%.4f", cc)).append("</div></td></tr>");
        sb.append("<td>Average in-degree</td><td><div align=\"center\">");
        sb.append(String.format("%.4f", id)).append("</div></td></tr>");
        sb.append("<td>In-degree standard deviation</td><td><div align=\"center\">");
        sb.append(String.format("%.4f", istd)).append("</div></td></tr>");
        sb.append("<td>Average out-degree</td><td><div align=\"center\">");
        sb.append(String.format("%.4f", od)).append("</div></td></tr>");
        sb.append("</table>");

        // print in-degree distribution
        HistogramDataset dataset = new HistogramDataset();
        double[] values = g.getInDegrees();
        int min = (int) g.getMinInDegree(), max = (int) g.getMaxInDegree();
        int bins = max - min;
        bins = bins < 1 ? 1 : bins;
        dataset.addSeries("In-degree distribution", values, bins, min, max);
        JFreeChart chart = ChartFactory.createHistogram(null, null, null, dataset, PlotOrientation.VERTICAL, true,
                false, false);
        // chart.getXYPlot().setForegroundAlpha(0.95f);
        BufferedImage image = chart.createBufferedImage(800, 300);

        String imageString = "";
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageIO.write(image, "png", out);
            out.close();
            Base64 bencoder = new Base64();
            imageString = new String(bencoder.encode(out.toByteArray()));
            imageString = "data:image/png;base64," + imageString;
        } catch (IOException e) {
            sb.append(e);
        }

        sb.append("<br><div align=\"center\"><img src=\"").append(imageString);
        sb.append("\" alt=\"In-degree distribution " + "\" /></div><br>");

        sb.append("<div align=\"center\">It took ").append(t1 - t0);
        sb.append("ms to compute these statistics.<br>");

        // refresh form
        sb.append("<form method=\"get\" name=\"rfrshFrm\" id=\"rfrshFrm\">");
        sb.append("<label>Show peers ");
        sb.append("<input name=\"peers\" type=\"checkbox\" id=\"peers\" />");
        // sb.append("checked=\"").append("false").append("\" />");
        sb.append("<input type=\"submit\" value=\"Refresh\" />");
        sb.append("</form></div>");

        if (!showPeers) {
            return;
        }

        sb.append("<h2 align=\"center\" class=\"style2\">");
        sb.append("Individual peers:</h2>");
        sb.append("<table width=\"1300\" border=\"1\" align=\"center\"><tr>");
        sb.append("<th class=\"style2\" width=\"50\" scope=\"col\">Count</th>");
        sb.append("<th class=\"style2\" width=\"50\" scope=\"col\">Peer</th>");
        sb.append("<th class=\"style2\" width=\"800\" scope=\"col\">Neighbors</th>");
        sb.append("<th class=\"style2\" width=\"50\" scope=\"col\">In Degree</th>");
        sb.append("<th class=\"style2\" width=\"50\" scope=\"col\">Out Degree</th>");
        sb.append("<th class=\"style2\" width=\"100\" scope=\"col\">Cluestering Coefficient</th>");
        sb.append("<th class=\"style2\" width=\"50\" scope=\"col\">Age</th>");
        sb.append("<th class=\"style2\" width=\"100\" scope=\"col\">Freshness</th></tr>");

        int count = 1;

        for (OverlayAddress address : alivePeers.keySet()) {
            CyclonNeighbors neighbors = alivePeers.get(address);

            List<CyclonNodeDescriptor> descriptors = neighbors.getDescriptors();
            Collections.sort(descriptors);

            sb.append("<tr>");
            sb.append("<td><div align=\"center\">").append(count++);
            // sb.append("(").append(g.map.get(address)).append(")");
            sb.append("</div></td>");

            // print peer address
            sb.append("</div></td><td bgcolor=\"#99CCFF\"><div align=\"center\">");
            appendPeerLink(sb, address);
            sb.append("</div></td>");

            // print neighbors
            if (descriptors != null) {
                sb.append("<td><div align=\"left\">");
                sb.append("[");
                Iterator<CyclonNodeDescriptor> iter = descriptors.iterator();
                while (iter.hasNext()) {
                    appendPeerLink(sb, iter.next().getCyclonAddress());
                    if (iter.hasNext()) {
                        sb.append(", ");
                    }
                }
                sb.append("]");
            } else {
                sb.append("<td bgcolor=\"#FFCCFF\"><div align=\"left\">");
                sb.append("[empty]");
            }
            sb.append("</div></td>");

            int v = g.getNodeIndexByAddress(address);

            // print in-degree
            sb.append("<td><div align=\"center\">").append(g.getInDegree(v));
            sb.append("</div></td>");
            // print out-degree
            sb.append("<td><div align=\"center\">").append(g.getOutDegree(v));
            sb.append("</div></td>");
            // print clustering coefficient

            // directedGraph

            sb.append("<td><div align=\"center\">").append(String.format("%.4f", g.getClustering(v)));
            sb.append("</div></td>");

            long now = System.currentTimeMillis();
            OverlayViewEntry viewEntry = view.get(address);

            // print age
            sb.append("<td><div align=\"right\">");
            sb.append(durationToString(now - viewEntry.getAddedAt()));
            sb.append("</div></td>");

            // print freshness
            sb.append("<td><div align=\"right\">");
            sb.append(durationToString(now - viewEntry.getRefreshedAt()));
            sb.append("</div></td>");

            sb.append("</tr>");
        }
        sb.append("</table>");
    }

    private void printDeadPeers(StringBuilder sb) {
        int count;
        // print dead peers
        if (deadPeers.size() > 0) {
            sb.append("<h2 align=\"center\" class=\"style2\">");
            sb.append("Dead peers:</h2>");
            sb.append("<table width=\"1300\" border=\"1\" align=\"center\"><tr>");
            sb.append("<th class=\"style2\" width=\"50\" scope=\"col\">Count</th>");
            sb.append("<th class=\"style2\" width=\"50\" scope=\"col\">Peer</th>");
            sb.append("<th class=\"style2\" width=\"800\" scope=\"col\">Neighbors</th>");
            sb.append("<th class=\"style2\" width=\"50\" scope=\"col\">In Degree</th>");
            sb.append("<th class=\"style2\" width=\"50\" scope=\"col\">Out Degree</th>");
            sb.append("<th class=\"style2\" width=\"100\" scope=\"col\">Cluestering Coefficient</th>");

            sb.append("<th class=\"style2\" width=\"50\" scope=\"col\">Lifetime</th>");
            sb.append("<th class=\"style2\" width=\"100\" scope=\"col\">Dead for</th></tr>");

            LinkedList<OverlayAddress> peers = new LinkedList<OverlayAddress>(deadPeers.descendingKeySet());

            count = 1;

            for (OverlayAddress address : peers) {
                CyclonNeighbors neighbors = deadPeers.get(address);

                List<CyclonNodeDescriptor> descriptors = neighbors.getDescriptors();

                sb.append("<tr>");
                sb.append("<td><div align=\"center\">").append(count++);
                // sb.append("(").append(g.map.get(address)).append(")");
                sb.append("</div></td>");

                // print peer address
                sb.append("</div></td><td bgcolor=\"#99CCFF\"><div align=\"center\">");
                appendPeerLink(sb, address);
                sb.append("</div></td>");

                // print neighbors
                if (descriptors != null) {
                    sb.append("<td><div align=\"left\">");
                    sb.append("[");
                    Iterator<CyclonNodeDescriptor> iter = descriptors.iterator();
                    while (iter.hasNext()) {
                        appendPeerLink(sb, iter.next().getCyclonAddress());
                        if (iter.hasNext()) {
                            sb.append(", ");
                        }
                    }
                    sb.append("]");
                } else {
                    sb.append("<td bgcolor=\"#FFCCFF\"><div align=\"left\">");
                    sb.append("[empty]");
                }
                sb.append("</div></td>");

                // print in-degree
                sb.append("<td><div align=\"center\">").append(0);
                sb.append("</div></td>");
                // print out-degree
                sb.append("<td><div align=\"center\">").append(0);
                sb.append("</div></td>");
                // print clustering coefficient

                // directedGraph

                sb.append("<td><div align=\"center\">").append(String.format("%.4f", 0.0));
                sb.append("</div></td>");

                long now = System.currentTimeMillis();
                OverlayViewEntry viewEntry = view.get(address);

                // print lifetime
                sb.append("<td><div align=\"right\">");
                sb.append(durationToString(viewEntry.getRefreshedAt() - viewEntry.getAddedAt()));
                sb.append("</div></td>");

                // print dead for
                sb.append("<td><div align=\"right\">");
                sb.append(durationToString(now - viewEntry.getRefreshedAt()));
                sb.append("</div></td>");

                sb.append("</tr>");
            }
            sb.append("</table>");
        }
    }

    private final void appendPeerLink(StringBuilder sb, OverlayAddress address) {
        sb.append("<a href=\"http://");
        sb.append(address.getPeerAddress().getIp().getHostAddress());
        sb.append(":").append(address.getPeerAddress().getPort() - 1).append("/");
        sb.append(address.getPeerAddress().getId()).append("/").append("\">");
        // show dead peer links in red
        if (deadPeers.containsKey(address)) {
            sb.append("<FONT style=\"BACKGROUND-COLOR: #FAAFBA\">");
            sb.append(address.toString()).append("</FONT></a>");
        } else {
            sb.append(address.toString()).append("</a>");
        }

    }

    private String durationToString(long duration) {
        StringBuilder sb = new StringBuilder();

        // get duration in seconds
        duration /= 1000;

        int s = 0, m = 0, h = 0, d = 0, y = 0;
        s = (int) (duration % 60);
        // get duration in minutes
        duration /= 60;
        if (duration > 0) {
            m = (int) (duration % 60);
            // get duration in hours
            duration /= 60;
            if (duration > 0) {
                h = (int) (duration % 24);
                // get duration in days
                duration /= 24;
                if (duration > 0) {
                    d = (int) (duration % 365);
                    // get duration in years
                    y = (int) (duration / 365);
                }
            }
        }

        boolean printed = false;

        if (y > 0) {
            sb.append(y).append("y");
            printed = true;
        }
        if (d > 0) {
            sb.append(d).append("d");
            printed = true;
        }
        if (h > 0) {
            sb.append(h).append("h");
            printed = true;
        }
        if (m > 0) {
            sb.append(m).append("m");
            printed = true;
        }
        if (s > 0 || printed == false) {
            sb.append(s).append("s");
        }

        return sb.toString();
    }
}